JBoss transactions with aspectj in a desktop app.
n3k0 May 4, 2015 3:01 AMHi!
This is my first post at this community, so i'll make my best try to explain myself.
I have a desktop app, that app needs to make (in the service layer) to do an update operation in two
different tables, BUT, this error appears in the console:
2015-05-04 00:37:19 [AWT-EventQueue-0] WARN com.arjuna.ats.jta - ARJUNA016087: TransactionImple.delistResource - unknown resource 2015-05-04 00:37:19 [AWT-EventQueue-0] ERROR c.p.f.a.i.DataSourceInterceptor - DataSourceAspect -> execute -> java.sql.SQLException: ARJUNA017011: Delist of resource failed. at com.arjuna.ats.internal.jdbc.ConnectionImple.close(ConnectionImple.java:321) ~[narayana-jta.jar:5.0.4.Final (revision: b4060)] at com.package.of.model.dao.impl.ProfileDaoImpl.deleteBy_aroundBody7$advice(ProfileDaoImpl.java:46) [bin/:na]
My configuration is as follows:
First, i have a homemade annotation (my own transactional, for practical purposes, is enough):
@Retention(RetentionPolicy.RUNTIME) @Target({ TYPE, METHOD, FIELD, PARAMETER }) public @interface Transactional {}
So, i have an aspectj class that recognizes the annotation and process it:
@Aspect public class TransactionInterceptor { private static final Logger LOG = LoggerFactory.getLogger(TransactionInterceptor.class); @Pointcut(value = "execution(@the.package.that.contains.annotation.Transactional * *(..))") public void makeTheTransaction(){} @Around("makeTheTransaction()") public Object execute(ProceedingJoinPoint proceedingJoinPoint){ transactionManager // this object is a global variable // = com.arjuna.ats.jta.TransactionManager.transactionManager(); // i tried with transactionmanager = com.arjuna.ats.jta.UserTransaction.userTransaction(); // and userTransaction but the result is //the same error MethodSignature methodSignature = (MethodSignature)proceedingJoinPoint.getSignature(); Method method = methodSignature.getMethod(); Object result = null; try { transactionManager.begin(); result = proceedingJoinPoint.proceed(); transactionManager.commit(); } } catch (Throwable e) { LOG.error("TransactionInterceptor -> execute ->",e.getClass(), e); try { transactionManager.rollback(); } catch (IllegalStateException | SecurityException | SystemException e1) { LOG.error("TransactionInterceptor -> execute ->", e1.getClass() , e1); } } return result; } }
I have a Dao annotation too, in order to inject connections:
@Retention(RetentionPolicy.RUNTIME) @Target({TYPE , METHOD}) public @interface Dao { }
And, my second aspect that injects the connection, like this;
@Aspect public class DataSourceInterceptor { private static final Logger LOG = LoggerFactory.getLogger(DataSourceInterceptor.class); private TransactionalDriverBuilder transactionalDriverBuilder = new TransactionalDriverBuilder(); @Pointcut(value = "execution(@package.where.lives.annotation.Dao * *(..))") public void addTheDataSource() {} @Around("addTheDataSource()") public Object execute(ProceedingJoinPoint proceedingJoinPoint) { Object result = null; try { Connection connection = null; if (proceedingJoinPoint.getTarget() != null) { Field field = null; try{ field = proceedingJoinPoint.getTarget().getClass().getDeclaredField("connection"); } catch(NoSuchFieldException e){ field = proceedingJoinPoint.getTarget().getClass().getSuperclass().getDeclaredField("connection"); } field.setAccessible(true); connection = fromTransactionalDriverBuilder(); field.set(proceedingJoinPoint.getTarget(), connection); result = proceedingJoinPoint.proceed(); connection.close(); } } catch (Throwable e) { LOG.error("DataSourceAspect -> execute ->", e.getClass(), e); } return result; } } private Connection fromTransactionalDriverBuilder(){ return transactionalDriverBuilder.getConnection(); } }
So, the TransactionalDriverBuilder is a subclass of TransactionalDriver previosly configured, it looks like this:
public class TransactionalDriverBuilder extends TransactionalDriver{ private static final Logger LOG = LoggerFactory.getLogger(TransactionalDriverBuilder.class); private Properties dbProps = new Properties(); public TransactionalDriverBuilder(){ super(); try { DriverManager.registerDriver(new Driver()); } catch (SQLException e1) { e1.printStackTrace(); } // DB_BUNDLE is a resource bundle that contains my configuration dbProps.setProperty(TransactionalDriver.userName , DB_BUNDLE.getString("dataSource.user")); dbProps.setProperty(TransactionalDriver.password , DB_BUNDLE.getString("dataSource.password")); dbProps.setProperty(TransactionalDriver.dynamicClass , DSConfig.class.getName()); dbProps.setProperty(TransactionalDriver.poolConnections , Boolean.TRUE.toString()); dbProps.setProperty("com.arjuna.ats.jta.supportSubtransactions" , "YES"); dbProps.setProperty("com.arjuna.ats.jta.allowMultipleLastResources", "true"); jdbcPropertyManager.getJDBCEnvironmentBean() .setIsolationLevel(Connection.TRANSACTION_READ_COMMITTED); } public Connection getConnection(){ Connection connection = null; try { connection = this.connect("jdbc:arjuna:jdbc/TestDS" , dbProps); } catch (SQLException e) { LOG.error("ArjunaTransactionalDriverBuilder -> getConnection ",e); } return connection; } } } catch (SQLException e) { LOG.error("ArjunaTransactionalDriverBuilder -> getConnection ",e); } return connection; } }
The class DSConfig that appears in the dynamicClass parameter, is a subclass of DomainClass that returns a datasource object.
After that, in my service class, i put the Transactional annotation in my method; like this:
@Transactional public int deleteUser(AppUser toDelete) { if(this.profileDao.deleteBy(toDelete.getUserNumber())>0){ //here i try to do the first operation return this.userDao.delete(toDelete.getNick()); //here i try to make my second operation } return 0; }
So, in the (swing) front end,when i invoke the deleteUser Method, (debugging) the profileDao deletes one profile (the executeQuery method returns 1 ) BUT when the result retuns, before the userDao.delete , the exception previously mentioned appears, after that, a large stack trace appears, but is a consequence of that error.
Thanks in advance for reading, and If anyone can shake my brain in the correct direction, please comment this, i would like to make a guide with the correct solution because not everyone need/can use something more like apache delta spike transactional approach or spring or EJB or similar, just something simple.
Thanks.