You are viewing a plain text version of this content. The canonical link for it is here.
Posted to ojb-dev@db.apache.org by Jason Mihalick <ja...@jCraftsmen.com> on 2004/08/19 04:53:28 UTC

A managed version of SequenceManagerHighLowImpl

Armin,

While we are chatting, I have another patch, but I need some advice on 
how to finish it off...

I recently finished our upgrade to OJB 1.0.  Our producton deployment 
environment is Websphere 5.0 on OS/390 and our dev environment is Tomcat 
5.  One of the things that prompted our upgrade was that we recently 
discovered a horrible bug has been in place in our production deployment 
since January.  The problem was that we were using the 
ConnectionFactoryManagedImpl, but we were not using JTA!  Not until I 
turned on OJB debugging did I realize that the PersistenceBroker calls 
to beginTransaction and rollback were having NO EFFECT!  After I had a 
small heart-attack ;-), I started working on refactoring our code to use 
JTA.  While working on that refactor, I found this (at 
http://db.apache.org/ojb/docu/guides/sequencemanager.html#High%2FLow+sequence+manager):

    High/Low sequence manager

        ...
        Limitations:
        - do not use in managed environments when connections were
        enlisted in running transactions, e.g. when using DataSources of
        an application server

Well, unfortunately, we really depend on this sequence manager for one 
of our tables so that we can guarantee that a primary key value is NEVER 
reused.  So, I started looking at what it would take to make this 
SequenceManagerHighLowImp JTA friendly and I came up with the attached 
subclass.  The problem is, however, the attached class has dependencies 
from our project's class structure.  In particular, I currently depend 
on our own JtaTransactionImpl class for fetching the UserTransaction 
JNDI name, which varies from AppServer to AppServer.  What are your 
thoughts about this?  Do you have any ideas where in OJB we can store 
the JNDI name for the UserTransaction class?  I have some other ideas 
for this too.  If we had a place to keep the JNDI name, then I can also 
see a PersistenceBroker Implementation that could use JTA instead of the 
PersistenceBroker transaction managment -- OR -- a new API in OJB that 
will abstract the transaction manager so that you can easily configure 
it to use the PersistenceBroker implementation or the JTA 
implementation.  This is what I have done in our application ... I have 
both a PersistenceBrokerTransactionImpl and JtaTransactionImpl along 
with a TransactionFactory to create the proper type of transactions 
based on whether we are deployed to Tomcat vs. WebSphere.

What are your thoughts?

Thanks,
Jason

Re: A managed version of SequenceManagerHighLowImpl

Posted by Armin Waibel <ar...@apache.org>.
Hi Jason,

Jason Mihalick wrote:
>   Armin,
> 
> While we are chatting, I have another patch, but I need some advice on 
> how to finish it off...
> 
> I recently finished our upgrade to OJB 1.0.  Our producton deployment 
> environment is Websphere 5.0 on OS/390 and our dev environment is Tomcat 
> 5.  One of the things that prompted our upgrade was that we recently 
> discovered a horrible bug has been in place in our production deployment 
> since January.  The problem was that we were using the 
> ConnectionFactoryManagedImpl, but we were not using JTA!  Not until I 
> turned on OJB debugging did I realize that the PersistenceBroker calls 
> to beginTransaction and rollback were having NO EFFECT!  After I had a 
> small heart-attack ;-), I started working on refactoring our code to use 
> JTA.

yep, when using JTA tx it is not allowed to commit/rollback the 
connection, therefore in ConnectionFactoryManagedImpl these methods are 
no-op. To use the PB-api in a "normal" consistent way in managed 
environments internal calls to PB.beginTransaction/commitTransaction... 
are needed (cache handling, resource cleanup, ...), but these methods 
also commit/rollback the connection, so we introduce 
ConnectionFactoryManagedImpl which use a wrapper class for the 
connections with no-op methods.



>  While working on that refactor, I found this (at 
> http://db.apache.org/ojb/docu/guides/sequencemanager.html#High%2FLow+sequence+manager):
> 
>     High/Low sequence manager
> 
>         ...
>         Limitations:
>         - do not use in managed environments when connections were
>         enlisted in running transactions, e.g. when using DataSources of
>         an application server
> 
> Well, unfortunately, we really depend on this sequence manager for one 
> of our tables so that we can guarantee that a primary key value is NEVER 
> reused.  So, I started looking at what it would take to make this 
> SequenceManagerHighLowImp JTA friendly and I came up with the attached 
> subclass.

Looks fine! Hope I understand the idea: When this sequence manager SM 
was called within a running tx, e.g. an ArticleManagerBean with cm-tx 
and we store new Article objects, the SM lookup a UserTransaction, the 
SM begin the "new" UserTransaction, so the container should suspend the 
running (container managed) tx. SM obtain a new key sequence and commit 
the UserTransaction, then the container should resume the suspended tx.
Is this right?


>  The problem is, however, the attached class has dependencies 
> from our project's class structure.  In particular, I currently depend 
> on our own JtaTransactionImpl class for fetching the UserTransaction 
> JNDI name, which varies from AppServer to AppServer.  What are your 
> thoughts about this?  Do you have any ideas where in OJB we can store 
> the JNDI name for the UserTransaction class?

No problem, we can store this information in the
org.apache.ojb.broker.transaction.tm.TransactionManagerFactory 
implementation classes (adding new method "UserTransaction 
getUserTransaction()" or something similar). Should be easy to 
implement, all we need are the JNDI lookup names of the UserTransaction 
for all supported appServer/JTA implementations.


>  I have some other ideas 
> for this too.  If we had a place to keep the JNDI name, then I can also 
> see a PersistenceBroker Implementation that could use JTA instead of the 
> PersistenceBroker transaction managment -- OR -- a new API in OJB that 
> will abstract the transaction manager so that you can easily configure 
> it to use the PersistenceBroker implementation or the JTA 
> implementation. 

Think the last suggestion will be great! Separation of the transaction 
demarcation methods from the PB interface. Should the new tx handling be 
extendable by the top-level api (odmg, jdo)? Or a better approach could 
be making listener available to allow top-level api to participate in 
tx. I'm not sure how to handle this stuff.


regards,
Armin

> This is what I have done in our application ... I have 
> both a PersistenceBrokerTransactionImpl and JtaTransactionImpl along 
> with a TransactionFactory to create the proper type of transactions 
> based on whether we are deployed to Tomcat vs. WebSphere.
> 
> What are your thoughts?
> 
> Thanks,
> Jason
> 
> 
> ------------------------------------------------------------------------
> 
> /*
>  * Created on Aug 16, 2004
>  *
>  */
> package org.apache.ojb.broker.util.sequence;
> 
> import javax.naming.Context;
> import javax.naming.InitialContext;
> import javax.transaction.Status;
> import javax.transaction.Transaction;
> import javax.transaction.TransactionManager;
> import javax.transaction.UserTransaction;
> 
> import org.apache.commons.lang.exception.NestableRuntimeException;
> import org.apache.ojb.broker.PBFactoryException;
> import org.apache.ojb.broker.PersistenceBroker;
> import org.apache.ojb.broker.PersistenceBrokerFactory;
> import org.apache.ojb.broker.metadata.FieldDescriptor;
> import org.apache.ojb.broker.transaction.tm.TransactionManagerFactoryException;
> import org.apache.ojb.broker.transaction.tm.TransactionManagerFactoryFactory;
> import org.apache.ojb.broker.util.logging.Logger;
> import org.apache.ojb.broker.util.logging.LoggerFactory;
> 
> import com.aep.appsvcs.util.persist.JtaTransactionImpl;
> 
> /**
>  * @author s011974
>  *
>  */
> public class SequenceManagerManagedHighLowImpl extends
>     SequenceManagerHighLowImpl {
> 
>   private static Logger log = LoggerFactory.getLogger(SequenceManagerManagedHighLowImpl.class);
> //  private TransactionManager txMan = null;
>   
>   public SequenceManagerManagedHighLowImpl(PersistenceBroker broker) {
>     
>     super(broker);
> /*
>     try
>     {
>         txMan = TransactionManagerFactoryFactory.instance().getTransactionManager();
>     }
>     catch (TransactionManagerFactoryException e)
>     {
>         throw new PBFactoryException("Can't instantiate TransactionManager of managed environment", e);
>     }
> */
>   }
> 
>   // NEW METHOD -- JRM
>   protected UserTransaction newTransaction() {
> 
>     String jndiName = System.getProperty( JtaTransactionImpl.JNDI_USERTRANSACTION_PROPERTY	);
>     if ( null == jndiName ) {
>       throw new NestableRuntimeException( "The property " + JtaTransactionImpl.JNDI_USERTRANSACTION_PROPERTY + 
>           " has not been set.  SequenceManagerManagedHighLowImpl transaction handling will fail. " );
>     }
>     
>     // Now do the lookup to resolve a UserTransaction object
>     UserTransaction tran = null;
>     try {
>       Context ctx = new InitialContext();
>       tran = (UserTransaction) ctx.lookup( jndiName );
>     } catch ( Exception ex ) {
>       throw new NestableRuntimeException( "Failure to acquire UserTransaction " +
>           "from JNDI lookup of " + jndiName );
>     }
> 
>     return tran;
>   }
>   
>   protected HighLowSequence getSequence(PersistenceBroker brokerForSequence,
>       FieldDescriptor field,
>       String sequenceName)  throws SequenceManagerException
> 	{
>     HighLowSequence newSequence = null;
>     PersistenceBroker internBroker = null;
>     UserTransaction tran = null;
>     
>     try
>     {
>       internBroker = PersistenceBrokerFactory.createPersistenceBroker(brokerForSequence.getPBKey());
> 
>       // NEW CODE, USE JTA -- JRM
>       tran = newTransaction();
>       if ( tran == null ) {
>         throw new SequenceManagerException( "Failed to invoke new UserTransaction." );
>       }
>       
>       tran.begin();
>       // END NEW CODE
>       
>       
>       newSequence = lookupStoreSequence(internBroker, field, sequenceName);
> 	
>       // NEW CODE, USE JTA -- JRM
>       tran.commit();
>       // END NEW CODE
>       
>       if (log.isDebugEnabled()) log.debug("new sequence was " + newSequence);
>     }
>     catch(Exception e)
>     {
> 			log.error("Can't lookup new HighLowSequence for field "
> 			    + (field != null ? field.getAttributeName() : null)
> 			    + " using sequence name " + sequenceName, e);
>       // NEW CODE, USE JTA -- JRM
> 			if ( tran != null ) 
> 			{
> 			  try {
> 			    
> 				  int status = tran.getStatus();
> 				  if ( status == Status.STATUS_ACTIVE ) 
> 				  {
> 				    tran.rollback();
> 				  }
> 			  } catch ( Exception ex ) {
> 		      log.error( "Error while rolling back HighLowSequence for field " 
> 					    + (field != null ? field.getAttributeName() : null)
> 					    + " using sequence name " + sequenceName, ex);
> 		    }
> 			}
> 			// END NEW CODE
> 			throw new SequenceManagerException("Can't build new sequence", e);
>     }
>     finally
>     {
>       attempts = 0;
>       if (internBroker != null) internBroker.close();
>     }
>     return newSequence;
> 	}
>   
> }
> 
> 
> 
> 
> ------------------------------------------------------------------------
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: ojb-dev-unsubscribe@db.apache.org
> For additional commands, e-mail: ojb-dev-help@db.apache.org

---------------------------------------------------------------------
To unsubscribe, e-mail: ojb-dev-unsubscribe@db.apache.org
For additional commands, e-mail: ojb-dev-help@db.apache.org