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.