1 Reply Latest reply on May 5, 2015 4:54 AM by tomjenkinson

    JBoss transactions with aspectj in a desktop app.

    n3k0

      Hi!

      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.

        • 1. Re: JBoss transactions with aspectj in a desktop app.
          tomjenkinson

          Hi!

           

          Thanks very much for your interest in Narayana!

           

          I have an observation and a request:

           

          Observation: If you just want to update two tables in the same database within a transaction you do not need a transaction manager. Connection::setAutoCommit(false), Connection::update(SQL_A), Connection::update(SQL_B), Connection::commit()

           

          Request: Its not entirely clear what is happening with the aspectj stuff and it would be a lot easier to read if you can just cut and paste that code into a simple Junit test if that is possible please? At a guess it looks like you are closing the connection each time which would delist the transactional driver and there could be something wrong with that from your side or our side but its quite tricky to infer due to the injection. If you can craft a simple test it will be much easier to advise you on the connection handling and determine if there is something wrong with enlist/delist within transactional driver.

           

          Thanks in advance,
          Tom