Atomikos Forum |
|
Hi!
First, keep up good work on developing Atomikos. It is great piece of software. And now let’s go to work. For instructions how to set up spring (and use it), please go to Springframework and Springsource site. There is a lot of good information and examples. It should be enough to get you started. For examples how Atomikos and Spring beans are used, use web – there is a lot of good samples. I’m using Eclipse IDE with springsource eclipse plugin, and have setup springsource server (version 1.0.0.RC2, but as far as I can tell, there is no big difference with 1.0.0.RELEASE). Springsource server have special directory where user can put OSGI bundles to enhance server - repository/bundles/usr (they are jar files with extra few lines in manifest file). On the other hand, Atomikos is not distributed as OSGI bundle, but set of jar archives. When you download Atomikos, look under “dist” directory, there should be few jar files. I did search web and available documentation, but there is _no_ explanation on which jar is used for what! After lost a lot of hours, I was finally able to determine what is what. In short, “transactions-essentials-all.jar” is all you need, you can delete the rest. NOTE: transactions-essentials-all.jar should contain classes from all other jars, but for some reason, it has few classes extra? Anyhow, first step is to make it OSGI bundle. This can be done using BND tool ( http://www.aqute.biz/Code/Bnd ). Download it, and also check that you have java (>=1.4). Here is properties file which we will use to bundle-lize it.
<code>
Bundle-Version: ${Atomikos-Version} Export-Package: *;-noimport:=true;version="${Atomikos-Version}" Bundle-Name: Atomikos Transaction Manager - ${Implementation-Title} Bundle-SymbolicName: com.springsource.com.atomikos.${replace;$(Implementation-Title);\.jar;-imp} -classpath=../lib Import-Package: org.hibernate.*;resolution:=optional,net.sf.hibernate.*;resolution:=optional,oracle.*;resolution:=optional,* </code> NOTE: make sure you have one extra newline at the end! Save this in some file (ie. transactions-essentials-all.bnd). Go to directory where you unpacked Atomikos distribution, and into dist directory (there you will have transactions-essentials-all.jar). Now run: <code> Java –jar <path to bnd jar> wrap –properties <path to transactions-essentials-all.bnd> transactions-essentials-all.jar </code> It should produce something like: <code> transactions-essentials-all.jar 401 0 0 warnings 1 : No translation found for macro: Implementation-Title 2 : No translation found for macro: Implementation-Title 3 : No translation found for macro: Atomikos-Version </code> And this should produce transactions-essentials-all.jar$ file. You can rename it to com.springsource.com.atomikos.transactions-essentials-all-imp.jar (this is also symbolic name in bundle), or some other .jar name, actual file name is not important. Put this file in springsource user bundles repository (<springsource root>/repository/bundles/usr), and you are set to go (actually, you will probably need to download few additional bundles to satisfy dependencies – use springsource repository web for this).
And now we get to one problem. In most cases, you will have your web application in one war (or jar) file, and everything should be ok. But, if you are like, me, and separate things logically, this will not work. Problem is that context path loader will not be able to locate all required classes.
In short, when you have multiple bundles which form one logical part, each bundle have its own “context”, and class loader, and threads get theirs context from bundle in which they were created. So when you try to run your application, you will get: <code> atomikos.unknown I AtomikosConnectionProxy: error creating dynamic proxy java.lang.IllegalArgumentException: interface com.atomikos.datasource.pool.Reapable is not visible from class loader at java.lang.reflect.Proxy.getProxyClass(Proxy.java:353) at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:581) at com.atomikos.jdbc.AtomikosConnectionProxy.newInstance(AtomikosConnectionProxy.java:193) at com.atomikos.jdbc.AtomikosXAPooledConnection.doCreateConnectionProxy(AtomikosXAPooledConnection.java:80) at com.atomikos.datasource.pool.AbstractXPooledConnection.createConnectionProxy(AbstractXPooledConnection.java:44) at com.atomikos.datasource.pool.ConnectionPool.borrowConnection(ConnectionPool.java:128) at com.atomikos.jdbc.AbstractDataSourceBean.getConnection(AbstractDataSourceBean.java:285) at com.atomikos.jdbc.AbstractDataSourceBean.getConnection(AbstractDataSourceBean.java:337) at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:113) at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:79) at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:382) at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:458) at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:466) at org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:474) at org.springframework.jdbc.core.JdbcTemplate.queryForMap(JdbcTemplate.java:470) at org.springframework.jdbc.core.simple.SimpleJdbcTemplate.queryForMap(SimpleJdbcTemplate.java:205)
…
atomikos.unknown W atomikos connection pool: error creating proxy of connection an AtomikosXAPooledConnection with a SessionHandleState with 1 context(s) java.lang.IllegalArgumentException: interface com.atomikos.datasource.pool.Reapable is not visible from class loader at java.lang.reflect.Proxy.getProxyClass(Proxy.java:353) at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:581) at com.atomikos.jdbc.AtomikosConnectionProxy.newInstance(AtomikosConnectionProxy.java:197) at com.atomikos.jdbc.AtomikosXAPooledConnection.doCreateConnectionProxy(AtomikosXAPooledConnection.java:80) at com.atomikos.datasource.pool.AbstractXPooledConnection.createConnectionProxy(AbstractXPooledConnection.java:44) at com.atomikos.datasource.pool.ConnectionPool.borrowConnection(ConnectionPool.java:128) at com.atomikos.jdbc.AbstractDataSourceBean.getConnection(AbstractDataSourceBean.java:285) at com.atomikos.jdbc.AbstractDataSourceBean.getConnection(AbstractDataSourceBean.java:337) at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:113) at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:79) at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:382) at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:458) at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:466) at org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:474) at org.springframework.jdbc.core.JdbcTemplate.queryForMap(JdbcTemplate.java:470) at org.springframework.jdbc.core.simple.SimpleJdbcTemplate.queryForMap(SimpleJdbcTemplate.java:205) … </code> And this will repeat for a few times, and then it will fail with:
<code>
.app.spring.GenericModuleOsgiBundleXmlApplicationContext.unknown E Post refresh error org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'ProvTestImpl' defined in URL [bundleentry://88/META-INF/spring/bundle-service. xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [aa.aaa.aaa.test.ProvTest]: Constructor threw exception; nested exception is org.springframework.transaction.UnexpectedRollbackException: JTA transaction already completed - probably rolled back at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:254) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:925) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:835) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:440) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409) at java.security.AccessController.doPrivileged(Native Method) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:221) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164) …
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [aa.aaa.aaa.test.ProvTest]: Constructor threw exception; nested exception is org.springframework.transaction.UnexpectedRollbackException: JTA transaction already completed - probably rolled back
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:115) at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:87) at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:248) ... Caused by: org.springframework.transaction.UnexpectedRollbackException: JTA transaction already completed - probably rolled back at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1012) at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:709) at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:678) at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:321) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:116) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204) </code> My method ProvTest executes few select and few update SQL commands – nothing special there.
Unfortunately, those errors does not tell you much, and the last one is totally confusing.
So, after few days, I finnaly cracked it down to com.atomikos.jdbc. AtomikosConnectionProxy class, method Reapable. This class is used for creating proxy object for set of classes, and it is declared as _static_. On the forum, I did found one post which gave me hint how to solve problem. In short, main issue here is that thread which is calling this method does not “see” all classes which this static method can “load”. So we need to change this – using source! :-) There is alternative to this, and this is to include Atomikos bundle in every bundle you create. But because this is bad practice in OSGI world, I decided not to employ it. So, source! For compiling Atomikos you will need java and “ant”. See documentation in Atomikos how to do it. IMPORTANT: backup everything in dist directory under Atomikos distribution, because build process will overwrite it! Go to Atomikos dir, and edit sources/ com/atomikos/jdbc/AtomikosConnectionProxy.java, find Reapable method, and make it look like this: <code> … interfaces.add ( Reapable.class ); Class[] interfaceClasses = ( Class[] ) interfaces.toArray ( new Class[0] ); try { ret = (Reapable) Proxy.newProxyInstance(AtomikosConnectionProxy.class.getClassLoader(), interfaceClasses , proxy); return ret; } catch ( IllegalArgumentException e ) { Configuration.logDebug ( "AtomikosConnectionProxy: error creating dynamic proxy localy, trying thread class loader" ); } try { ret = (Reapable) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaceClasses , proxy); … </code>
Save it, and compile Atomikos. It will recreate dist directory, with new transactions-essentials-all.jar. We now must get AtomikosConnectionProxy* from new jar to old one.
Make two directories, in one unpack original jar, and in other this new one. Unpacking is done using jar command, just like tar (man tar). In short: <code> Jar –xf <jar file name> </code> will unpack jar in current dir. This will create META-INF and “com” dirs. Copy class(es) from new to old one, and recreate jar archive. Creating jar archive is done: <code> Jar –cfm <name of new jar> META-INF/MANIFEST.MF com </code> And as last step, “wrap” it again through bnd utility. Done. :-) BUT, if you plan to use oracle, there is one more step – you must provide Atomikos jar with needed imports of oracle driver. In short, if you get errors like this: <code> atomikos.unknown D AtomikosConnectionProxy: error creating dynamic proxy localy, trying thread class loader </code> or that oracle.jdbc or oracje.jdbc.internal is missing, then you need one more hack – called “fragment”. Fragment is bundle extension, which augment manifest file of existing bundle. This is basically bundle jar, but with slightly different manifest file: <code> Manifest-Version: 1.0 Bundle-Version: 0.0.1 Bundle-Name: Transaction_config Bundle Bundle-ManifestVersion: 2 Bundle-SymbolicName: aa.aaa.aaa.aaaa Fragment-Host: com.springsource.com.atomikos.transactions-essentials Import-Package: oracle.jdbc, oracle.jdbc.internal, oracle.jdbc.oracore, oracle.jdbc.pool, oracle.sql </code> NOTE: space before “oracle” at the end, and empty newline! In short, make empty dir, save this manifest somewhere and do: <code> Jar –cfm <new jar name> <saved manifest file> <created empty dir> </code> and put it under spring bundle repository. That should be it. I hope … I will test this more, and report if additional changes are needed. H. p.s. Sorry on many posts, but i needed to break down this ... |