User: mnf999
Date: 01/08/01 11:34:39
Modified: src/main/org/jboss/ejb/plugins
EntitySynchronizationInterceptor.java
Log:
The new interceptor impacts how this one deals with the beanlock introduced by bill
Revision Changes Path
1.42 +620 -575
jboss/src/main/org/jboss/ejb/plugins/EntitySynchronizationInterceptor.java
Index: EntitySynchronizationInterceptor.java
===================================================================
RCS file:
/cvsroot/jboss/jboss/src/main/org/jboss/ejb/plugins/EntitySynchronizationInterceptor.java,v
retrieving revision 1.41
retrieving revision 1.42
diff -u -r1.41 -r1.42
--- EntitySynchronizationInterceptor.java 2001/07/13 08:54:11 1.41
+++ EntitySynchronizationInterceptor.java 2001/08/01 18:34:39 1.42
@@ -1,575 +1,620 @@
-/**
- * JBoss, the OpenSource EJB server
- *
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
- */
-package org.jboss.ejb.plugins;
-
-import java.lang.reflect.Method;
-import java.rmi.RemoteException;
-import java.rmi.ServerException;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.ArrayList;
-import java.util.HashSet;
-
-import javax.ejb.EJBObject;
-import javax.ejb.CreateException;
-import javax.ejb.EJBException;
-import javax.ejb.NoSuchEntityException;
-import javax.ejb.RemoveException;
-import javax.ejb.EntityBean;
-import javax.transaction.Status;
-import javax.transaction.Synchronization;
-import javax.transaction.Transaction;
-import javax.transaction.TransactionManager;
-import javax.transaction.RollbackException;
-import javax.transaction.SystemException;
-
-import org.jboss.ejb.Container;
-import org.jboss.ejb.EntityContainer;
-import org.jboss.ejb.EntityPersistenceManager;
-import org.jboss.ejb.EntityEnterpriseContext;
-import org.jboss.ejb.EnterpriseContext;
-import org.jboss.ejb.InstanceCache;
-import org.jboss.ejb.InstancePool;
-import org.jboss.ejb.MethodInvocation;
-import org.jboss.metadata.ConfigurationMetaData;
-import org.jboss.logging.log4j.JBossCategory;
-import org.jboss.util.Sync;
-
-/**
- * The role of this interceptor is to synchronize the state of the cache with
- * the underlying storage. It does this with the ejbLoad and ejbStore
- * semantics of the EJB specification. In the presence of a transaction this
- * is triggered by transaction demarcation. It registers a callback with the
- * underlying transaction monitor through the JTA interfaces. If there is no
- * transaction the policy is to store state upon returning from invocation.
- * The synchronization polices A,B,C of the specification are taken care of
- * here.
- *
- * <p><b>WARNING: critical code</b>, get approval from senior developers
- * before changing.
- *
- * @author <a href="mailto:[EMAIL PROTECTED]">Marc Fleury</a>
- * @author <a href="mailto:[EMAIL PROTECTED]">Scott Stark</a>
- * @version $Revision: 1.41 $
- *
- * <p><b>Revisions:</b><br>
- * <p><b>2001/06/28: marcf</b>
- * <ol>
- * <li>Moved to new synchronization
- * <li>afterCompletion doesn't return to pool anymore, idea is to simplify
- * design by not mucking with reuse of the instances
- * <li>before completion checks for a rolledback tx and doesn't call the
- * store in case of a rollback we are notified but we don't register
- * the resource
- * </ol>
- * <p><b>2001/07/12: starksm</b>
- * <ol>
- * <li>Fixed invalid use of the ctx.transaction in beforeCompletion
- * <li>Added clearContextTx to make sure clean up of ctx.tx was done consistently
- * <li>Clean up the EntityContainer cast-fest
- * </ol>
- */
-public class EntitySynchronizationInterceptor
- extends AbstractInterceptor
-{
- // Constants -----------------------------------------------------
-
- // Attributes ----------------------------------------------------
- /** Use a JBoss custom log4j category for trace level logging */
- static JBossCategory log = (JBossCategory)
JBossCategory.getInstance(EntityInstanceInterceptor.class);
-
- /**
- * The current commit option.
- */
- protected int commitOption;
-
- /**
- * The refresh rate for commit option d
- */
- protected long optionDRefreshRate;
-
- /**
- * The container of this interceptor.
- */
- protected EntityContainer container;
-
- /**
- * Optional isModified method
- */
- protected Method isModified;
-
- /**
- * For commit option D this is the cache of valid entities
- */
- protected HashSet validContexts;
-
- // Static --------------------------------------------------------
-
- // Constructors --------------------------------------------------
-
- // Public --------------------------------------------------------
-
- public void setContainer(Container container)
- {
- this.container = (EntityContainer)container;
- }
-
- public void init()
- throws Exception
- {
-
- try
- {
- validContexts = new HashSet();
- ConfigurationMetaData configuration =
container.getBeanMetaData().getContainerConfiguration();
- commitOption = configuration.getCommitOption();
- optionDRefreshRate = configuration.getOptionDRefreshRate();
-
- //start up the validContexts thread if commit option D
- if(commitOption == ConfigurationMetaData.D_COMMIT_OPTION)
- {
- ValidContextsRefresher vcr = new ValidContextsRefresher(validContexts,
optionDRefreshRate);
- new Thread(vcr).start();
- }
-
- isModified = container.getBeanClass().getMethod("isModified", new
Class[0]);
- if (!isModified.getReturnType().equals(Boolean.TYPE))
- isModified = null; // Has to have "boolean" as return type!
- } catch (Exception e)
- {
- System.out.println(e.getMessage());
- }
- }
-
- public Container getContainer()
- {
- return container;
- }
-
- /**
- * Register a transaction synchronization callback with a context.
- */
- private void register(EntityEnterpriseContext ctx, Transaction tx)
- {
- boolean trace = log.isTraceEnabled();
- if( trace )
- log.trace("register, ctx="+ctx+", tx="+tx);
-
- // Create a new synchronization
- InstanceSynchronization synch = new InstanceSynchronization(tx, ctx);
-
- EntityContainer ctxContainer = null;
- try
- {
- ctxContainer = (EntityContainer) ctx.getContainer();
- // associate the entity bean with the transaction so that
- // we can do things like synchronizeEntitiesWithinTransaction
- ctxContainer.getTxEntityMap().associate(tx, ctx);
-
- // We want to be notified when the transaction commits
- tx.registerSynchronization(synch);
-
- // marcf: rethink the need for synchronization here, who else uses it?
- synchronized(ctx.getTxLock())
- {ctx.setTxSynchronized(true);}
-
- }
- catch (RollbackException e)
- {
- //Indicates that the transaction is already marked for rollback
- ctxContainer.getTxEntityMap().disassociate(tx, ctx);
-
- // The state in the instance is to be discarded, we force a reload of state
- synchronized (ctx)
- {ctx.setValid(false);}
-
- // This is really a mistake from the JTA spec, the fact that the tx is
marked rollback should not be relevant
- // We should still hear about the demarcation
- clearContextTx("RollbackException", ctx, tx, trace);
- }
- catch (Exception e)
- {
- if( ctxContainer != null )
- ctxContainer.getTxEntityMap().disassociate(tx, ctx);
- // If anything goes wrong with the association remove the ctx-tx
association
- clearContextTx("Exception", ctx, tx, trace);
- throw new EJBException(e);
- }
- }
-
- /**
- * As per the spec 9.6.4, entities must be synchronized with the datastore
- * when an ejbFind<METHOD> is called.
- */
- private void synchronizeEntitiesWithinTransaction(Transaction tx) throws
Exception
- {
- Object[] entities = container.getTxEntityMap().getEntities(tx);
- for (int i = 0; i < entities.length; i++)
- {
- EntityEnterpriseContext ctx = (EntityEnterpriseContext)entities[i];
- storeEntity(ctx);
- }
- }
-
- private void storeEntity(EntityEnterpriseContext ctx) throws Exception
- {
- if (ctx.getId() != null)
- {
- boolean dirty = true;
- // Check isModified bean method flag
- if (isModified != null)
- {
- Object[] args = {};
- Boolean modified = (Boolean) isModified.invoke(ctx.getInstance(), args);
- dirty = modified.booleanValue();
- }
-
- // Store entity
- if (dirty)
- {
- container.getPersistenceManager().storeEntity(ctx);
- }
- }
- }
-
- // Interceptor implementation --------------------------------------
-
- public Object invokeHome(MethodInvocation mi)
- throws Exception
- {
-
- EntityEnterpriseContext ctx =
(EntityEnterpriseContext)mi.getEnterpriseContext();
- Transaction tx = mi.getTransaction();
-
- try
- {
- if (tx != null && mi.getMethod().getName().startsWith("find"))
- {
- // As per the spec EJB2.0 9.6.4, entities must be synchronized with the
datastore
- // when an ejbFind<METHOD> is called.
- synchronizeEntitiesWithinTransaction(tx);
- }
-
- return getNext().invokeHome(mi);
-
- } finally
- {
-
- // An anonymous context was sent in, so if it has an id it is a real
instance now
- if (ctx.getId() != null)
- {
-
- // Currently synched with underlying storage
- ctx.setValid(true);
-
- if (tx!= null) register(ctx, tx); // Set tx
- }
- }
- }
-
- public Object invoke(MethodInvocation mi)
- throws Exception
- {
- // We are going to work with the context a lot
- EntityEnterpriseContext ctx =
(EntityEnterpriseContext)mi.getEnterpriseContext();
-
- // The Tx coming as part of the Method Invocation
- Transaction tx = mi.getTransaction();
- if( log.isTraceEnabled() )
- log.trace("invoke called for ctx "+ctx+", tx="+tx);
-
- //Commit Option D....
- if(commitOption == ConfigurationMetaData.D_COMMIT_OPTION &&
!validContexts.contains(ctx.getId()))
- {
- //bean isn't in cache
- //so set valid to false so that we load...
- ctx.setValid(false);
- }
-
- // Is my state valid?
- if (!ctx.isValid())
- {
- // If not tell the persistence manager to load the state
- container.getPersistenceManager().loadEntity(ctx);
-
- // Now the state is valid
- ctx.setValid(true);
- }
-
- // So we can go on with the invocation
-
- // Invocation with a running Transaction
- if (tx != null && tx.getStatus() != Status.STATUS_NO_TRANSACTION)
- {
- try
- {
- //Invoke down the chain
- return getNext().invoke(mi);
- }
- finally
- {
- //register the wrapper with the transaction monitor (but only register
once).
- // The transaction demarcation will trigger the storage operations
- if (!ctx.isTxSynchronized()) register(ctx,tx);
- }
- }
- //
- else
- { // No tx
- try
- {
-
- Object result = getNext().invoke(mi);
-
- // Store after each invocation -- not on exception though, or removal
- // And skip reads too ("get" methods)
- if (ctx.getId() != null)
- {
- storeEntity(ctx);
- }
-
- return result;
-
- }
- catch (Exception e)
- {
- // Exception - force reload on next call
- ctx.setValid(false);
- throw e;
- }
- }
- }
-
-
- // Protected ----------------------------------------------------
-
- // Inner classes -------------------------------------------------
-
- private class InstanceSynchronization
- implements Synchronization
- {
- /**
- * The transaction we follow.
- */
- private Transaction tx;
-
- /**
- * The context we manage.
- */
- private EntityEnterpriseContext ctx;
-
- /**
- * Create a new instance synchronization instance.
- */
- InstanceSynchronization(Transaction tx, EntityEnterpriseContext ctx)
- {
- this.tx = tx;
- this.ctx = ctx;
- }
-
- // Synchronization implementation -----------------------------
-
- public void beforeCompletion()
- {
- boolean trace = log.isTraceEnabled();
- if( trace )
- log.trace("beforeCompletion called for ctx "+ctx);
-
- if (ctx.getId() != null)
- {
- // This is an independent point of entry. We need to make sure the
- // thread is associated with the right context class loader
- ClassLoader oldCl = Thread.currentThread().getContextClassLoader();
-
Thread.currentThread().setContextClassLoader(container.getClassLoader());
-
- try
- {
- try
- {
- // Store instance if business method was invoked
- if( trace )
- log.trace("Checking ctx="+ctx+", for status of tx="+tx);
- if (tx.getStatus() != Status.STATUS_MARKED_ROLLBACK)
- {
- // Check if the bean defines the isModified method
- boolean dirty = true;
- if (isModified != null)
- {
- try
- {
- Object[] args = {};
- Boolean modified = (Boolean)
isModified.invoke(ctx.getInstance(), args);
- dirty = modified.booleanValue();
- }
- catch (Exception ignored)
- {
- }
- }
-
- if( trace )
- log.trace("sync calling store on ctx "+ctx+",
dirty="+dirty);
- if (dirty)
- container.getPersistenceManager().storeEntity(ctx);
- }
- }
- catch (NoSuchEntityException ignored)
- {
- if( trace )
- log.trace("Ignoring NSEE", ignored);
- }
- }
- // EJB 1.1 12.3.2: all exceptions from ejbStore must be marked for
rollback
- // and the instance must be discarded
- catch (Exception e)
- {
- log.error("Store failed", e);
- // Store failed -> rollback!
- try
- {
- tx.setRollbackOnly();
- }
- catch (SystemException ex)
- {
- if( trace )
- log.trace("Ignoring SE", ex);
- }
- catch (IllegalStateException ex)
- {
- if( trace )
- log.trace("Ignoring ISE", ex);
- }
- }
- finally
- {
- Thread.currentThread().setContextClassLoader(oldCl);
- }
- }
- }
-
- public void afterCompletion(int status)
- {
- boolean trace = log.isTraceEnabled();
-
- // This is an independent point of entry. We need to make sure the
- // thread is associated with the right context class loader
- ClassLoader oldCl = Thread.currentThread().getContextClassLoader();
- Thread.currentThread().setContextClassLoader(container.getClassLoader());
-
- synchronized (ctx)
- {
- try
- {
- // If rolled back -> invalidate instance
- if (status == Status.STATUS_ROLLEDBACK)
- {
- // remove from the cache
- container.getInstanceCache().remove(ctx.getCacheKey());
-
- // Wake all those waiting that the context is no longer in cache
- ctx.notifyAll();
-
- }
- else
- {
- switch (commitOption)
- {
- // Keep instance cached after tx commit
- case ConfigurationMetaData.A_COMMIT_OPTION:
- // The state is still valid (only point of access is us)
- ctx.setValid(true);
- break;
-
- // Keep instance active, but invalidate state
- case ConfigurationMetaData.B_COMMIT_OPTION:
- // Invalidate state (there might be other points of entry)
- ctx.setValid(false);
- break;
- // Invalidate everything AND Passivate instance
- case ConfigurationMetaData.C_COMMIT_OPTION:
- try
- {
- container.getInstanceCache().release(ctx);
- }
- catch (Exception e)
- {
- log.debug(e);
- }
- break;
- case ConfigurationMetaData.D_COMMIT_OPTION:
- //if the local cache is emptied then valid is set to
false(see invoke() )
- validContexts.add(ctx.getId());
- break;
- }
- }
- }
- finally
- {
- // finish the transaction association
- EntityContainer ctxContainer = (EntityContainer) ctx.getContainer();
- ctxContainer.getTxEntityMap().disassociate(tx, ctx);
- clearContextTx("afterCompletion", ctx, tx, trace);
- Thread.currentThread().setContextClassLoader(oldCl);
- }
- } // synchronized(ctx)
- }
-
- }
-
- /** All the threads waiting for the end of the transaction need to wake up and
- go lock on the next thing, either ctx for those threads that will win the
- transactional race or ctx.txLock for the rest of them (better luck next time)
- no-one must stay on this txLock since the tx is going away so this is the one
- and only call!.
- So don't replace the following by "notify()" (!)
- */
- private void clearContextTx(String msg, EntityEnterpriseContext ctx, Transaction
tx, boolean trace)
- {
- Object txLock = ctx.getTxLock();
- synchronized(txLock)
- {
- if( trace )
- log.trace(msg+", clear tx for ctx="+ctx+", tx="+tx);
- // The context is no longer synchronized on the TX
- ctx.setTxSynchronized(false);
- ctx.setTransaction(null);
-
- // Wake up threads waiting for the transaction demarcation, ain't gonna
happen
- if( trace )
- log.trace(msg+", send notifyAll on TxLock for ctx="+ctx);
- txLock.notifyAll();
- }
- }
-
- class ValidContextsRefresher implements Runnable
- {
- private HashSet validContexts;
- private long refreshRate;
-
- public ValidContextsRefresher(HashSet validContexts,long refreshRate)
- {
- this.validContexts = validContexts;
- this.refreshRate = refreshRate;
- }
-
- public void run()
- {
- while(true)
- {
- validContexts.clear();
- log.trace("Flushing the valid contexts");
- try
- {
- Thread.sleep(refreshRate);
- }
- catch(Exception e)
- {
- log.debug("Interrupted from sleep", e);
- }
- }
- }
- }
-}
+/**
+* JBoss, the OpenSource EJB server
+*
+* Distributable under LGPL license.
+* See terms of license at gnu.org.
+*/
+package org.jboss.ejb.plugins;
+
+import java.lang.reflect.Method;
+import java.rmi.RemoteException;
+import java.rmi.ServerException;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.HashSet;
+
+import javax.ejb.EJBObject;
+import javax.ejb.CreateException;
+import javax.ejb.EJBException;
+import javax.ejb.NoSuchEntityException;
+import javax.ejb.RemoveException;
+import javax.ejb.EntityBean;
+import javax.transaction.Status;
+import javax.transaction.Synchronization;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+import javax.transaction.RollbackException;
+import javax.transaction.SystemException;
+
+import org.jboss.ejb.BeanLock;
+import org.jboss.ejb.BeanLockManager;
+import org.jboss.ejb.Container;
+import org.jboss.ejb.EntityContainer;
+import org.jboss.ejb.EntityPersistenceManager;
+import org.jboss.ejb.EntityEnterpriseContext;
+import org.jboss.ejb.EnterpriseContext;
+import org.jboss.ejb.InstanceCache;
+import org.jboss.ejb.InstancePool;
+import org.jboss.ejb.MethodInvocation;
+import org.jboss.metadata.ConfigurationMetaData;
+import org.jboss.logging.log4j.JBossCategory;
+import org.jboss.util.Sync;
+
+/**
+ * The role of this interceptor is to synchronize the state of the cache with
+ * the underlying storage. It does this with the ejbLoad and ejbStore
+ * semantics of the EJB specification. In the presence of a transaction this
+ * is triggered by transaction demarcation. It registers a callback with the
+ * underlying transaction monitor through the JTA interfaces. If there is no
+ * transaction the policy is to store state upon returning from invocation.
+ * The synchronization polices A,B,C of the specification are taken care of
+ * here.
+ *
+ * <p><b>WARNING: critical code</b>, get approval from senior developers
+ * before changing.
+ *
+ * @author <a href="mailto:[EMAIL PROTECTED]">Marc Fleury</a>
+ * @author <a href="mailto:[EMAIL PROTECTED]">Scott Stark</a>
+ * @author <a href="mailto:[EMAIL PROTECTED]">Bill Burke</a>
+ * @version $Revision: 1.42 $
+ *
+ * <p><b>Revisions:</b><br>
+ * <p><b>2001/06/28: marcf</b>
+ * <ol>
+ * <li>Moved to new synchronization
+ * <li>afterCompletion doesn't return to pool anymore, idea is to simplify
+ * design by not mucking with reuse of the instances
+ * <li>before completion checks for a rolledback tx and doesn't call the
+ * store in case of a rollback we are notified but we don't register
+ * the resource
+ * </ol>
+ * <p><b>2001/07/12: starksm</b>
+ * <ol>
+ * <li>Fixed invalid use of the ctx.transaction in beforeCompletion
+ * <li>Added clearContextTx to make sure clean up of ctx.tx was done consistently
+ * <li>Clean up the EntityContainer cast-fest
+ * </ol>
+ * <p><b>2001/07/26: billb</b>
+ * <ol>
+ * <li>Locking is now separate from EntityEnterpriseContext objects and is now
+ * encapsulated in BeanLock and BeanLockManager. Did this because the lifetime
+ * of an EntityLock is sometimes longer than the lifetime of the ctx.
+ * </ol>
+ * <p><b>2001/08/01: marcf</b>
+ * <ol>
+ * <li>Updated BeanLock to work in new interceptor and out of InstanceInterceptor
so we update
+ * here as well to use the new interceptor lock logic and the BeanLock that bill
introduced
+ * <li>Make use of clearContextTx in all exceptional cases
+ * </ol>
+ */
+public class EntitySynchronizationInterceptor
+ extends AbstractInterceptor
+{
+ // Constants -----------------------------------------------------
+
+ // Attributes ----------------------------------------------------
+ /** Use a JBoss custom log4j category for trace level logging */
+ static JBossCategory log = (JBossCategory)
JBossCategory.getInstance(EntityInstanceInterceptor.class);
+
+ /**
+ * The current commit option.
+ */
+ protected int commitOption;
+
+ /**
+ * The refresh rate for commit option d
+ */
+ protected long optionDRefreshRate;
+
+ /**
+ * The container of this interceptor.
+ */
+ protected EntityContainer container;
+
+ /**
+ * Optional isModified method
+ */
+ protected Method isModified;
+
+ /**
+ * For commit option D this is the cache of valid entities
+ */
+ protected HashSet validContexts;
+
+ // Static --------------------------------------------------------
+
+ // Constructors --------------------------------------------------
+
+ // Public --------------------------------------------------------
+
+ public void setContainer(Container container)
+ {
+ this.container = (EntityContainer)container;
+ }
+
+ public void init()
+ throws Exception
+ {
+
+ try
+ {
+ validContexts = new HashSet();
+ ConfigurationMetaData configuration =
container.getBeanMetaData().getContainerConfiguration();
+ commitOption = configuration.getCommitOption();
+ optionDRefreshRate = configuration.getOptionDRefreshRate();
+
+ //start up the validContexts thread if commit option D
+ if(commitOption == ConfigurationMetaData.D_COMMIT_OPTION)
+ {
+ ValidContextsRefresher vcr = new ValidContextsRefresher(validContexts,
optionDRefreshRate);
+ new Thread(vcr).start();
+ }
+
+ isModified = container.getBeanClass().getMethod("isModified", new
Class[0]);
+ if (!isModified.getReturnType().equals(Boolean.TYPE))
+ isModified = null; // Has to have "boolean" as return type!
+ } catch (Exception e)
+ {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ public Container getContainer()
+ {
+ return container;
+ }
+
+ /**
+ * Register a transaction synchronization callback with a context.
+ */
+ private void register(EntityEnterpriseContext ctx, Transaction tx)
+ {
+ boolean trace = log.isTraceEnabled();
+ if( trace )
+ log.trace("register, ctx="+ctx+", tx="+tx);
+
+ // Create a new synchronization
+ InstanceSynchronization synch = new InstanceSynchronization(tx, ctx);
+
+ EntityContainer ctxContainer = null;
+ try
+ {
+ ctxContainer = (EntityContainer) ctx.getContainer();
+ // associate the entity bean with the transaction so that
+ // we can do things like synchronizeEntitiesWithinTransaction
+ ctxContainer.getTxEntityMap().associate(tx, ctx);
+
+ // We want to be notified when the transaction commits
+ tx.registerSynchronization(synch);
+
+ ctx.hasTxSynchronization(true);
+ }
+ catch (RollbackException e)
+ {
+ //Indicates that the transaction is already marked for rollback
+ ctxContainer.getTxEntityMap().disassociate(tx, ctx);
+
+ // The state in the instance is to be discarded, we force a reload of state
+ synchronized (ctx)
+ {ctx.setValid(false);}
+
+ // This is really a mistake from the JTA spec, the fact that the tx is
marked rollback should not be relevant
+ // We should still hear about the demarcation
+ clearContextTx("RollbackException", ctx, tx, trace);
+ }
+ catch (Exception e)
+ {
+ if( ctxContainer != null )
+ ctxContainer.getTxEntityMap().disassociate(tx, ctx);
+ // If anything goes wrong with the association remove the ctx-tx
association
+ clearContextTx("Exception", ctx, tx, trace);
+ throw new EJBException(e);
+ }
+ }
+
+ /**
+ * As per the spec 9.6.4, entities must be synchronized with the datastore
+ * when an ejbFind<METHOD> is called.
+ */
+ private void synchronizeEntitiesWithinTransaction(Transaction tx) throws
Exception
+ {
+ Object[] entities = container.getTxEntityMap().getEntities(tx);
+ for (int i = 0; i < entities.length; i++)
+ {
+ EntityEnterpriseContext ctx = (EntityEnterpriseContext)entities[i];
+ storeEntity(ctx);
+ }
+ }
+
+ private void storeEntity(EntityEnterpriseContext ctx) throws Exception
+ {
+ if (ctx.getId() != null)
+ {
+ boolean dirty = true;
+ // Check isModified bean method flag
+ if (isModified != null)
+ {
+ Object[] args = {};
+ Boolean modified = (Boolean) isModified.invoke(ctx.getInstance(), args);
+ dirty = modified.booleanValue();
+ }
+
+ // Store entity
+ if (dirty)
+ {
+ container.getPersistenceManager().storeEntity(ctx);
+ }
+ }
+ }
+
+ // Interceptor implementation --------------------------------------
+
+ public Object invokeHome(MethodInvocation mi)
+ throws Exception
+ {
+
+ EntityEnterpriseContext ctx =
(EntityEnterpriseContext)mi.getEnterpriseContext();
+ Transaction tx = mi.getTransaction();
+
+ try
+ {
+ if (tx != null && mi.getMethod().getName().startsWith("find"))
+ {
+ // As per the spec EJB2.0 9.6.4, entities must be synchronized with the
datastore
+ // when an ejbFind<METHOD> is called.
+ synchronizeEntitiesWithinTransaction(tx);
+ }
+
+ return getNext().invokeHome(mi);
+
+ } finally
+ {
+
+ // An anonymous context was sent in, so if it has an id it is a real
instance now
+ if (ctx.getId() != null)
+ {
+
+ // Currently synched with underlying storage
+ ctx.setValid(true);
+
+ if (tx!= null) register(ctx, tx); // Set tx
+ }
+ }
+ }
+
+ public Object invoke(MethodInvocation mi)
+ throws Exception
+ {
+ // We are going to work with the context a lot
+ EntityEnterpriseContext ctx =
(EntityEnterpriseContext)mi.getEnterpriseContext();
+
+ // The Tx coming as part of the Method Invocation
+ Transaction tx = mi.getTransaction();
+
+ if( log.isTraceEnabled() )
+ log.trace("invoke called for ctx "+ctx+", tx="+tx);
+
+ //Commit Option D....
+ if(commitOption == ConfigurationMetaData.D_COMMIT_OPTION &&
!validContexts.contains(ctx.getId()))
+ {
+ //bean isn't in cache
+ //so set valid to false so that we load...
+ ctx.setValid(false);
+ }
+
+ // Is my state valid?
+ if (!ctx.isValid())
+ {
+ try
+ {
+ // If not tell the persistence manager to load the state
+ container.getPersistenceManager().loadEntity(ctx);
+ }
+ catch (Exception ex)
+ {
+ clearContextTx("loadEntity Exception", ctx, tx, log.isTraceEnabled());
+ throw ex;
+ }
+
+
+ // Now the state is valid
+ ctx.setValid(true);
+ }
+
+ // So we can go on with the invocation
+
+ // Invocation with a running Transaction
+ if (tx != null && tx.getStatus() != Status.STATUS_NO_TRANSACTION)
+ {
+ try
+ {
+ //Invoke down the chain
+ return getNext().invoke(mi);
+ }
+ finally
+ {
+ //register the wrapper with the transaction monitor (but only register
once).
+ // The transaction demarcation will trigger the storage operations
+ if (!ctx.hasTxSynchronization()) register(ctx,tx);
+ }
+ }
+ //
+ else
+ { // No tx
+ try
+ {
+
+ Object result = getNext().invoke(mi);
+
+ // Store after each invocation -- not on exception though, or removal
+ // And skip reads too ("get" methods)
+ if (ctx.getId() != null)
+ {
+ storeEntity(ctx);
+ }
+
+ return result;
+
+ }
+ catch (Exception e)
+ {
+ // Exception - force reload on next call
+ ctx.setValid(false);
+ throw e;
+ }
+ }
+ }
+
+
+ // Protected ----------------------------------------------------
+
+ // Inner classes -------------------------------------------------
+
+ private class InstanceSynchronization
+ implements Synchronization
+ {
+ /**
+ * The transaction we follow.
+ */
+ private Transaction tx;
+
+ /**
+ * The context we manage.
+ */
+ private EntityEnterpriseContext ctx;
+
+ /**
+ * The context lock
+ */
+ private BeanLock lock;
+
+ /**
+ * Create a new instance synchronization instance.
+ */
+ InstanceSynchronization(Transaction tx, EntityEnterpriseContext ctx)
+ {
+ this.tx = tx;
+ this.ctx = ctx;
+ this.lock = container.getLockManager().getLock(ctx.getCacheKey());
+ }
+
+ // Synchronization implementation -----------------------------
+
+ public void beforeCompletion()
+ {
+ boolean trace = log.isTraceEnabled();
+ if( trace )
+ log.trace("beforeCompletion called for ctx "+ctx);
+
+ if (ctx.getId() != null)
+ {
+ // This is an independent point of entry. We need to make sure the
+ // thread is associated with the right context class loader
+ ClassLoader oldCl = Thread.currentThread().getContextClassLoader();
+
Thread.currentThread().setContextClassLoader(container.getClassLoader());
+
+ try
+ {
+ try
+ {
+ // Store instance if business method was invoked
+ if( trace )
+ log.trace("Checking ctx="+ctx+", for status of tx="+tx);
+ if (tx.getStatus() != Status.STATUS_MARKED_ROLLBACK)
+ {
+ // Check if the bean defines the isModified method
+ boolean dirty = true;
+ if (isModified != null)
+ {
+ try
+ {
+ Object[] args = {};
+ Boolean modified = (Boolean)
isModified.invoke(ctx.getInstance(), args);
+ dirty = modified.booleanValue();
+ }
+ catch (Exception ignored)
+ {
+ }
+ }
+
+ if( trace )
+ log.trace("sync calling store on ctx "+ctx+",
dirty="+dirty);
+ if (dirty)
+ container.getPersistenceManager().storeEntity(ctx);
+ }
+ }
+ catch (NoSuchEntityException ignored)
+ {
+ if( trace )
+ log.trace("Ignoring NSEE", ignored);
+ }
+ }
+ // EJB 1.1 12.3.2: all exceptions from ejbStore must be marked for
rollback
+ // and the instance must be discarded
+ catch (Exception e)
+ {
+ log.error("Store failed", e);
+ // Store failed -> rollback!
+ try
+ {
+ tx.setRollbackOnly();
+ }
+ catch (SystemException ex)
+ {
+ if( trace )
+ log.trace("Ignoring SE", ex);
+ }
+ catch (IllegalStateException ex)
+ {
+ if( trace )
+ log.trace("Ignoring ISE", ex);
+ }
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(oldCl);
+ }
+ }
+ }
+
+ public void afterCompletion(int status)
+ {
+ boolean trace = log.isTraceEnabled();
+
+ // This is an independent point of entry. We need to make sure the
+ // thread is associated with the right context class loader
+ ClassLoader oldCl = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(container.getClassLoader());
+
+ lock.sync();
+ try
+ {
+ try
+ {
+ // If rolled back -> invalidate instance
+ if (status == Status.STATUS_ROLLEDBACK)
+ {
+ // remove from the cache
+ container.getInstanceCache().remove(ctx.getCacheKey());
+ }
+ else
+ {
+ switch (commitOption)
+ {
+ // Keep instance cached after tx commit
+ case ConfigurationMetaData.A_COMMIT_OPTION:
+ // The state is still valid (only point of access is us)
+ ctx.setValid(true);
+ break;
+
+ // Keep instance active, but invalidate state
+ case ConfigurationMetaData.B_COMMIT_OPTION:
+ // Invalidate state (there might be other points of entry)
+ ctx.setValid(false);
+ break;
+ // Invalidate everything AND Passivate instance
+ case ConfigurationMetaData.C_COMMIT_OPTION:
+ try
+ {
+ container.getInstanceCache().release(ctx);
+ }
+ catch (Exception e)
+ {
+ log.debug(e);
+ }
+ break;
+ case ConfigurationMetaData.D_COMMIT_OPTION:
+ //if the local cache is emptied then valid is set to false(see
invoke() )
+ validContexts.add(ctx.getId());
+ break;
+ }
+ }
+ }
+ finally
+ {
+ // finish the transaction association
+ container.getTxEntityMap().disassociate(tx, ctx);
+ if( trace )
+ log.trace("afterCompletion, clear tx for ctx="+ctx+", tx="+tx);
+ // The context is no longer synchronized on the TX
+ ctx.hasTxSynchronization(false);
+
+ ctx.setTransaction(null);
+
+ // notify the lock of the end of the transaction
+ // it wakes up all the threads in the current transaction
+ // it wakes up at least one thread from the next transaction
+ // This next tx thread will set the transaction variable in the lock
logic
+ lock.nextTransaction();
+
+ if( trace )
+ log.trace("afterCompletion, sent notifyAll on TxLock for
ctx="+ctx);
+ }
+ } // synchronized(lock)
+ finally
+ {
+ lock.releaseSync();
+ container.getLockManager().removeLockRef(lock.getId());
+ Thread.currentThread().setContextClassLoader(oldCl);
+ }
+ }
+
+ }
+
+ private void clearContextTx(String msg, EntityEnterpriseContext ctx, Transaction
tx, boolean trace)
+ {
+ BeanLock lock = container.getLockManager().getLock(ctx.getCacheKey());
+ lock.sync();
+ try
+ {
+ if( trace )
+ log.trace(msg+", clear tx for ctx="+ctx+", tx="+tx);
+ // The context is no longer synchronized on the TX
+ ctx.hasTxSynchronization(false);
+ ctx.setTransaction(null);
+
+
+ // notify the lock this will set current transaction to null
+ // it also wakes up at least one thread from the next transaction
+ // This thread will set the next transaction in the lock logic
+ lock.nextTransaction();
+
+ }
+ finally
+ {
+ lock.releaseSync();
+
+ container.getLockManager().removeLockRef(lock.getId());
+ }
+ }
+
+ class ValidContextsRefresher implements Runnable
+ {
+ private HashSet validContexts;
+ private long refreshRate;
+
+ public ValidContextsRefresher(HashSet validContexts,long refreshRate)
+ {
+ this.validContexts = validContexts;
+ this.refreshRate = refreshRate;
+ }
+
+ public void run()
+ {
+ while(true)
+ {
+ validContexts.clear();
+ log.trace("Flushing the valid contexts");
+ try
+ {
+ Thread.sleep(refreshRate);
+ }
+ catch(Exception e)
+ {
+ log.debug("Interrupted from sleep", e);
+ }
+ }
+ }
+ }
+}
_______________________________________________
Jboss-development mailing list
[EMAIL PROTECTED]
http://lists.sourceforge.net/lists/listinfo/jboss-development