KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > ejb > GlobalTxEntityMap


1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */

22 package org.jboss.ejb;
23
24 import org.jboss.logging.Logger;
25 import org.jboss.tm.TransactionLocal;
26
27 import javax.ejb.EJBException JavaDoc;
28 import javax.transaction.RollbackException JavaDoc;
29 import javax.transaction.Status JavaDoc;
30 import javax.transaction.Synchronization JavaDoc;
31 import javax.transaction.SystemException JavaDoc;
32 import javax.transaction.Transaction JavaDoc;
33 import java.util.ArrayList JavaDoc;
34 import java.util.List JavaDoc;
35
36 /**
37  * This class provides a way to find out what entities are contained in
38  * what transaction. It is used, to find which entities to call ejbStore()
39  * on when a ejbFind() method is called within a transaction. EJB 2.0- 9.6.4
40  * also, it is used to synchronize on a remove.
41  * Used in EntitySynchronizationInterceptor, EntityContainer
42  *
43  * Entities are stored in an ArrayList to ensure specific ordering.
44  *
45  * @author <a HREF="bill@burkecentral.com">Bill Burke</a>
46  * @author <a HREF="alex@jboss.org">Alexey Loubyansky</a>
47  * @version $Revision: 43998 $
48  */

49 public class GlobalTxEntityMap
50 {
51    private static final Logger log = Logger.getLogger(GlobalTxEntityMap.class);
52
53    private final TransactionLocal txSynch = new TransactionLocal();
54
55    /**
56     * An instance can be in one of the three states:
57     * <ul>
58     * <li>not associated with the tx and, hence, does not need to be synchronized</li>
59     * <li>associated with the tx and needs to be synchronized</li>
60     * <li>associated with the tx but does not need to be synchronized</li>
61     * </ul>
62     * Implementations of TxAssociation implement these states.
63     */

64    public static interface TxAssociation
65    {
66       /**
67        * Schedules the instance for synchronization. The instance might or might not be associated with the tx.
68        *
69        * @param tx the transaction the instance should be associated with if not yet associated
70        * @param instance the instance to be scheduled for synchronization
71        * @throws SystemException
72        * @throws RollbackException
73        */

74       void scheduleSync(Transaction JavaDoc tx, EntityEnterpriseContext instance)
75          throws SystemException JavaDoc, RollbackException JavaDoc;
76
77       /**
78        * Synchronizes the instance if it is needed.
79        * @param thread current thread
80        * @param tx current transaction
81        * @param instance the instance to be synchronized
82        * @throws Exception thrown if synchronization failed
83        */

84       void synchronize(Thread JavaDoc thread, Transaction JavaDoc tx, EntityEnterpriseContext instance)
85          throws Exception JavaDoc;
86
87       /**
88        * Invokes ejbStore if needed
89        * @param thread current thread
90        * @param instance the instance to be synchronized
91        * @throws Exception thrown if synchronization failed
92        */

93       void invokeEjbStore(Thread JavaDoc thread, EntityEnterpriseContext instance)
94          throws Exception JavaDoc;
95    }
96
97    public static final TxAssociation NONE = new TxAssociation()
98    {
99       public void scheduleSync(Transaction JavaDoc tx, EntityEnterpriseContext instance)
100          throws SystemException JavaDoc, RollbackException JavaDoc
101       {
102          EntityContainer.getGlobalTxEntityMap().associate(tx, instance);
103          instance.setTxAssociation(SYNC_SCHEDULED);
104       }
105
106       public void synchronize(Thread JavaDoc thread, Transaction JavaDoc tx, EntityEnterpriseContext instance)
107       {
108          throw new UnsupportedOperationException JavaDoc();
109       }
110
111       public void invokeEjbStore(Thread JavaDoc thread, EntityEnterpriseContext instance)
112       {
113          throw new UnsupportedOperationException JavaDoc();
114       }
115    };
116
117    public static final TxAssociation SYNC_SCHEDULED = new TxAssociation()
118    {
119       public void scheduleSync(Transaction JavaDoc tx, EntityEnterpriseContext instance)
120       {
121       }
122
123       public void invokeEjbStore(Thread JavaDoc thread, EntityEnterpriseContext instance) throws Exception JavaDoc
124       {
125          if(instance.getId() != null)
126          {
127             EntityContainer container = (EntityContainer) instance.getContainer();
128             // set the context class loader before calling the store method
129
SecurityActions.setContextClassLoader(thread, container.getClassLoader());
130
131             // store it
132
container.invokeEjbStore(instance);
133          }
134       }
135
136       public void synchronize(Thread JavaDoc thread, Transaction JavaDoc tx, EntityEnterpriseContext instance)
137          throws Exception JavaDoc
138       {
139          // only synchronize if the id is not null. A null id means
140
// that the entity has been removed.
141
if(instance.getId() != null)
142          {
143             EntityContainer container = (EntityContainer) instance.getContainer();
144
145             // set the context class loader before calling the store method
146
SecurityActions.setContextClassLoader(thread, container.getClassLoader());
147
148             // store it
149
container.storeEntity(instance);
150
151             instance.setTxAssociation(SYNCHRONIZED);
152          }
153       }
154    };
155
156    public static final TxAssociation SYNCHRONIZED = new TxAssociation()
157    {
158       public void scheduleSync(Transaction JavaDoc tx, EntityEnterpriseContext instance)
159       {
160          instance.setTxAssociation(SYNC_SCHEDULED);
161       }
162
163       public void invokeEjbStore(Thread JavaDoc thread, EntityEnterpriseContext instance)
164       {
165       }
166
167       public void synchronize(Thread JavaDoc thread, Transaction JavaDoc tx, EntityEnterpriseContext instance)
168       {
169       }
170    };
171
172    public static final TxAssociation PREVENT_SYNC = new TxAssociation()
173    {
174       public void scheduleSync(Transaction JavaDoc tx, EntityEnterpriseContext instance)
175       {
176       }
177
178       public void synchronize(Thread JavaDoc thread, Transaction JavaDoc tx, EntityEnterpriseContext instance) throws Exception JavaDoc
179       {
180          EntityContainer container = (EntityContainer)instance.getContainer();
181          if(container.getPersistenceManager().isStoreRequired(instance))
182          {
183             throw new EJBException JavaDoc("The instance of " +
184                container.getBeanMetaData().getEjbName() +
185                " with pk=" +
186                instance.getId() +
187                " was not stored to prevent potential inconsistency of data in the database:" +
188                " the instance was evicted from the cache during the transaction" +
189                " and the database was possibly updated by another process.");
190          }
191       }
192
193       public void invokeEjbStore(Thread JavaDoc thread, EntityEnterpriseContext instance) throws Exception JavaDoc
194       {
195          GlobalTxEntityMap.SYNC_SCHEDULED.invokeEjbStore(thread, instance);
196       }
197    };
198
199    /**
200     * sync all EntityEnterpriseContext that are involved (and changed)
201     * within a transaction.
202     */

203    public void synchronizeEntities(Transaction JavaDoc tx)
204    {
205       GlobalTxSynchronization globalSync = (GlobalTxSynchronization) txSynch.get(tx);
206       if(globalSync != null)
207       {
208          globalSync.synchronize();
209       }
210    }
211
212    /**
213     * associate instance with transaction
214     */

215    private void associate(Transaction JavaDoc tx, EntityEnterpriseContext entity)
216       throws RollbackException JavaDoc, SystemException JavaDoc
217    {
218       GlobalTxSynchronization globalSync = (GlobalTxSynchronization) txSynch.get(tx);
219       if(globalSync == null)
220       {
221          globalSync = new GlobalTxSynchronization(tx);
222          txSynch.set(tx, globalSync);
223          tx.registerSynchronization(globalSync);
224       }
225
226       //There should be only one thread associated with this tx at a time.
227
//Therefore we should not need to synchronize on entityFifoList to ensure exclusive
228
//access. entityFifoList is correct since it was obtained in a synch block.
229

230       globalSync.associate(entity);
231    }
232
233    // Inner
234

235    /**
236     * A list of instances associated with the transaction.
237     */

238    private class GlobalTxSynchronization implements Synchronization JavaDoc
239    {
240       private Transaction JavaDoc tx;
241       private List JavaDoc instances = new ArrayList JavaDoc();
242       private boolean synchronizing;
243
244       public GlobalTxSynchronization(Transaction JavaDoc tx)
245       {
246          this.tx = tx;
247       }
248
249       public void associate(EntityEnterpriseContext ctx)
250       {
251          instances.add(ctx);
252       }
253
254       public void synchronize()
255       {
256          if(synchronizing || instances.isEmpty())
257          {
258             return;
259          }
260
261          synchronizing = true;
262
263          // This is an independent point of entry. We need to make sure the
264
// thread is associated with the right context class loader
265
Thread JavaDoc currentThread = Thread.currentThread();
266          ClassLoader JavaDoc oldCl = SecurityActions.getContextClassLoader();
267
268          EntityEnterpriseContext instance = null;
269          try
270          {
271             for(int i = 0; i < instances.size(); i++)
272             {
273                // any one can mark the tx rollback at any time so check
274
// before continuing to the next store
275
if(tx.getStatus() == Status.STATUS_MARKED_ROLLBACK)
276                {
277                   return;
278                }
279
280                instance = (EntityEnterpriseContext) instances.get(i);
281                instance.getTxAssociation().invokeEjbStore(currentThread, instance);
282             }
283
284             for(int i = 0; i < instances.size(); i++)
285             {
286                // any one can mark the tx rollback at any time so check
287
// before continuing to the next store
288
if(tx.getStatus() == Status.STATUS_MARKED_ROLLBACK)
289                {
290                   return;
291                }
292
293                // read-only instances will never get into this list.
294
instance = (EntityEnterpriseContext) instances.get(i);
295                instance.getTxAssociation().synchronize(currentThread, tx, instance);
296             }
297          }
298          catch(Exception JavaDoc causeByException)
299          {
300             // EJB 1.1 section 12.3.2 and EJB 2 section 18.3.3
301
// exception during store must log exception, mark tx for
302
// rollback and throw a TransactionRolledback[Local]Exception
303
// if using caller's transaction. All of this is handled by
304
// the AbstractTxInterceptor and LogInterceptor.
305
//
306
// All we need to do here is mark the transaction for rollback
307
// and rethrow the causeByException. The caller will handle logging
308
// and wraping with TransactionRolledback[Local]Exception.
309
try
310             {
311                tx.setRollbackOnly();
312             }
313             catch(Exception JavaDoc e)
314             {
315                log.warn("Exception while trying to rollback tx: " + tx, e);
316             }
317
318             // Rethrow cause by exception
319
if(causeByException instanceof EJBException JavaDoc)
320             {
321                throw (EJBException JavaDoc) causeByException;
322             }
323             throw new EJBException JavaDoc("Exception in store of entity:" +
324                ((instance == null || instance.getId() == null) ? "<null>" : instance.getId().toString()),
325                causeByException);
326          }
327          finally
328          {
329             SecurityActions.setContextClassLoader(oldCl);
330             synchronizing = false;
331          }
332       }
333
334       // Synchronization implementation -----------------------------
335

336       public void beforeCompletion()
337       {
338          if(log.isTraceEnabled())
339          {
340             log.trace("beforeCompletion called for tx " + tx);
341          }
342
343          // let the runtime exceptions fall out, so the committer can determine
344
// the root cause of a rollback
345
synchronize();
346       }
347
348       public void afterCompletion(int status)
349       {
350          //no-op
351
}
352    }
353 }
354
Popular Tags