001: /**
002: * EasyBeans
003: * Copyright (C) 2006 Bull S.A.S.
004: * Contact: easybeans@ow2.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: TxEntityManagerHandler.java 2101 2007-12-06 15:56:03Z benoitf $
023: * --------------------------------------------------------------------------
024: */package org.ow2.easybeans.persistence;
025:
026: import java.util.HashMap;
027: import java.util.Map;
028: import java.util.Stack;
029:
030: import javax.persistence.EntityManager;
031: import javax.persistence.EntityManagerFactory;
032: import javax.transaction.RollbackException;
033: import javax.transaction.Status;
034: import javax.transaction.SystemException;
035: import javax.transaction.Transaction;
036:
037: import org.ow2.easybeans.transaction.JTransactionManager;
038:
039: /**
040: * This class is used to handle different entity manager for Transaction Scoped
041: * Persistence Context. It associates one EntityManager to a given transaction
042: * @author Florent Benoit
043: */
044: public class TxEntityManagerHandler {
045:
046: /**
047: * EntityManager factory that will create entity manager.
048: */
049: private EntityManagerFactory entityManagerFactory = null;
050:
051: /**
052: * Map between transactions and the entity manager.
053: */
054: private Map<Transaction, EntityManager> entityManagers = null;
055:
056: /**
057: * Entity Manager used for a single method call when there is no TX. An
058: * interceptor will request to create a new entity manager and will close it
059: * at the end of the method.
060: */
061: private ThreadLocal<Stack<EntityManager>> threadEntityManager = new ThreadLocal<Stack<EntityManager>>();
062:
063: /**
064: * Build a new handler of these entity managers.
065: * @param entityManagerFactory Factory used to create the entity manager (if
066: * no entity manager is already associated to the current
067: * transaction)
068: */
069: public TxEntityManagerHandler(
070: final EntityManagerFactory entityManagerFactory) {
071: this .entityManagerFactory = entityManagerFactory;
072: this .entityManagers = new HashMap<Transaction, EntityManager>();
073: }
074:
075: /**
076: * Gets the current entity manager (or create one) for the given transaction
077: * if there is one.
078: * @return an entitymanager
079: */
080: public synchronized EntityManager getCurrent() {
081: EntityManager current = null;
082:
083: // Get current transaction (if any)
084: Transaction currentTx = null;
085: try {
086: currentTx = JTransactionManager.getTransactionManager()
087: .getTransaction();
088: } catch (SystemException e) {
089: throw new IllegalStateException(
090: "Cannot get current transaction", e);
091: }
092:
093: // get status of the current tx (if any)
094: int statusTx = Status.STATUS_UNKNOWN;
095: if (currentTx != null) {
096: try {
097: statusTx = currentTx.getStatus();
098: } catch (SystemException e) {
099: throw new IllegalStateException(
100: "Cannot get the status on the current transaction",
101: e);
102: }
103: }
104: /**
105: * If the entity manager is called when no JTA transaction is in
106: * progress, a persistence context is created and destroyed to service
107: * the method call only, and any entities loaded from the database will
108: * immediately become detached at the end of the method call.
109: */
110: if (currentTx == null || !(statusTx == Status.STATUS_ACTIVE)) {
111: Stack<EntityManager> stack = threadEntityManager.get();
112: if (stack == null) {
113: throw new IllegalStateException(
114: "No EntityManager stack associated on the current thread without TX."
115: + "NoTxInterceptor may not have been called");
116: }
117: return stack.peek();
118: }
119:
120: current = entityManagers.get(currentTx);
121: /**
122: * If the entity manager is called and there is an existing persistence
123: * context bound to the current JTA transaction, the call takes place in
124: * that context.
125: */
126: if (current == null) {
127: /**
128: * If the entity manager is called and there is no persistence
129: * context associated with the current JTA transaction, a new
130: * persistence context will be created and bound to the JTA
131: * transaction, and the call will take place in that context.
132: */
133: current = buildNewTxEntityManager(currentTx);
134: }
135:
136: return current;
137: }
138:
139: /**
140: * Builds a new Entity manager for the given transaction.
141: * @param tx transaction on which associate the entity manager
142: * @return built entity manager.
143: */
144: private EntityManager buildNewTxEntityManager(final Transaction tx) {
145: EntityManager entityManager = entityManagerFactory
146: .createEntityManager();
147:
148: /**
149: * The persistence context is created and then associated with the
150: * current JTA transaction. The persistence context ends when the
151: * associated JTA transaction commits or rolls back,
152: */
153: try {
154: tx.registerSynchronization(new TxEntityManagerLifeCycle(
155: entityManager, tx, this ));
156: } catch (IllegalStateException e) {
157: throw new IllegalStateException(
158: "Cannot register Entity manager lifecycle", e);
159: } catch (RollbackException e) {
160: throw new IllegalStateException(
161: "Cannot register Entity manager lifecycle", e);
162: } catch (SystemException e) {
163: throw new IllegalStateException(
164: "Cannot register Entity manager lifecycle", e);
165: }
166:
167: // add in the managed entitymanager
168: entityManagers.put(tx, entityManager);
169:
170: return entityManager;
171: }
172:
173: /**
174: * Builds a new Entity manager used when there is no transaction.
175: * @return built entity manager.
176: */
177: private EntityManager buildNoTxEntityManager() {
178: return entityManagerFactory.createEntityManager();
179: }
180:
181: /**
182: * Release the entity manager associated to the given tx.
183: * @param tx tx to removed
184: */
185: public void release(final Transaction tx) {
186: entityManagers.remove(tx);
187: }
188:
189: /**
190: * Sets the current entity manager (used when to transaction is active).
191: */
192: public void addCurrent() {
193: Stack<EntityManager> stackEntityManager = threadEntityManager
194: .get();
195: // No stack yet, create a new one
196: if (stackEntityManager == null) {
197: stackEntityManager = new Stack<EntityManager>();
198: threadEntityManager.set(stackEntityManager);
199: }
200: // Add a new entity manager on the stack
201: stackEntityManager.push(buildNoTxEntityManager());
202: }
203:
204: /**
205: * Sets back to the previous entity manager and close the current entity
206: * manager.
207: */
208: public void closeCurrentAndReturnToPrevious() {
209: Stack<EntityManager> stack = threadEntityManager.get();
210:
211: // Get entitymanager on the current stack
212: EntityManager current = stack.peek();
213:
214: // Remove the element in the stack
215: stack.pop();
216:
217: // If stack is now empty, reset it
218: if (stack.empty()) {
219: threadEntityManager.set(null);
220: }
221:
222: // close the current
223: if (current != null) {
224: current.close();
225: }
226:
227: }
228: }
|