001: /*
002: * JOnAS: Java(TM) Open Application Server
003: * Copyright (C) 1999 Bull S.A.
004: * Contact: jonas-team@objectweb.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: TxListener.java 9238 2006-07-26 08:52:36Z coqp $
023: * --------------------------------------------------------------------------
024: */
025:
026: package org.objectweb.jonas_ejb.container;
027:
028: import java.util.Iterator;
029: import java.util.LinkedList;
030: import javax.naming.Context;
031: import javax.transaction.Synchronization;
032: import javax.transaction.Transaction;
033:
034: import org.objectweb.jonas_ejb.container.jorm.JormFactory;
035: import org.objectweb.jorm.api.PMapper;
036: import org.objectweb.medor.eval.prefetch.api.PrefetchCache;
037: import org.objectweb.util.monolog.api.BasicLevel;
038:
039: /**
040: * This class is a listener on the transaction. It is registered as
041: * a Synchronization on the current transaction. It holds the list of all
042: * instances that are possibly modified during the transaction.
043: * @author Philippe Durieux
044: * @author Alexei Novakov (fix ConcurrentModification issue)
045: */
046: public class TxListener implements Synchronization {
047:
048: private JEntityFactory bf;
049: private Transaction tx;
050:
051: /**
052: * list of contexts to be stored on disk.
053: */
054: private LinkedList ctxList = new LinkedList();
055: private LinkedList additionalContexts = new LinkedList();
056: private boolean inStoreInstances = false;
057:
058: /**
059: * constructor
060: * @param bf ref on the Bean Factory
061: * @param tx associated Transaction
062: */
063: public TxListener(JEntityFactory bf, Transaction tx) {
064: if (TraceEjb.isDebugTxlistener()) {
065: TraceEjb.txlistener.log(BasicLevel.DEBUG, "");
066: }
067: this .bf = bf;
068: this .tx = tx;
069: }
070:
071: /**
072: * Synchronizes all instances on disk.
073: * This must be called before every SQL request (finder) or at beforeCompletion().
074: * We must be in the good transaction context here, and with the correct
075: * component environment (java:comp).
076: */
077: public synchronized void storeInstances() {
078: if (TraceEjb.isDebugTxlistener()) {
079: TraceEjb.txlistener.log(BasicLevel.DEBUG, "");
080: }
081: inStoreInstances = true;
082: storeInstances(ctxList);
083: while (!additionalContexts.isEmpty()) {
084: LinkedList lst = new LinkedList();
085: lst.addAll(additionalContexts);
086: additionalContexts.clear();
087: storeInstances(lst);
088: ctxList.addAll(lst);
089: }
090: inStoreInstances = false;
091: }
092:
093: /**
094: * Remove an instance from the list
095: */
096: public synchronized void removeInstance(JEntityContext ec) {
097: if (TraceEjb.isDebugTxlistener()) {
098: TraceEjb.txlistener.log(BasicLevel.DEBUG, "");
099: }
100: if (!ctxList.remove(ec)) {
101: if (!additionalContexts.remove(ec)) {
102: TraceEjb.txlistener.log(BasicLevel.WARN,
103: "Not in the list");
104: }
105: }
106: }
107:
108: /**
109: * Add a new Context in the list.
110: * @param ec the JEntityContext to synchronize
111: */
112: public synchronized void addInstance(JEntityContext ec) {
113: if (TraceEjb.isDebugTxlistener()) {
114: TraceEjb.txlistener.log(BasicLevel.DEBUG, "");
115: TraceEjb.txlistener.log(BasicLevel.DEBUG, ec
116: .getEntityFactory().getEJBName());
117: }
118: if (inStoreInstances) {
119: additionalContexts.addLast(ec);
120: } else {
121: ctxList.addLast(ec);
122: }
123: }
124:
125: // -------------------------------------------------------------------
126: // Synchronization implementation
127: // -------------------------------------------------------------------
128:
129: /**
130: * This beforeCompletion method is called by the transaction
131: * manager prior to the start of the transaction completion process.
132: * This method executes in the transaction context of the calling
133: * thread.
134: * The Bean's state must be stored on the persistent storage before
135: * the completion of the transaction.
136: */
137: public void beforeCompletion() {
138: if (TraceEjb.isDebugTxlistener()) {
139: TraceEjb.txlistener.log(BasicLevel.DEBUG, "");
140: }
141:
142: // Set classloader for getting the right component context
143: ClassLoader old = Thread.currentThread()
144: .getContextClassLoader();
145: Thread.currentThread()
146: .setContextClassLoader(bf.myClassLoader());
147:
148: Context bnctx = bf.setComponentContext();
149: storeInstances();
150: bf.resetComponentContext(bnctx);
151: Thread.currentThread().setContextClassLoader(old);
152: }
153:
154: /**
155: * The afterCompletion method is called by the transaction
156: * manager after the transaction is committed or rolled back.
157: * This method executes without a transaction context.
158: *
159: * @param status The status of the transaction completion.
160: */
161: public void afterCompletion(int status) {
162: if (TraceEjb.isDebugTxlistener()) {
163: TraceEjb.txlistener.log(BasicLevel.DEBUG, "");
164: }
165:
166: // Send afterCompletion on each Context.
167: synchronized (this ) {
168: for (Iterator i = ctxList.iterator(); i.hasNext();) {
169: JEntityContext ec = (JEntityContext) i.next();
170: ec.afterCompletion(status);
171: }
172: }
173:
174: // Invalid the prefetch cache
175: if (bf instanceof JormFactory) {
176: PMapper mapper = ((JormFactory) bf).getMapper();
177: PrefetchCache pc = mapper.getPrefetchCache();
178: if (pc != null) {
179: pc.invalidatePrefetchBuffer(tx);
180: }
181: }
182:
183: // remove this in the tx HashMap.
184: bf.removeTxListener(tx);
185: }
186:
187: private void storeInstances(LinkedList contexts) {
188: if (TraceEjb.isDebugTxlistener()) {
189: TraceEjb.txlistener.log(BasicLevel.DEBUG, "");
190: }
191:
192: for (Iterator i = contexts.iterator(); i.hasNext();) {
193: JEntityContext ec = (JEntityContext) i.next();
194: ec.beforeCompletion();
195: if (TraceEjb.isDebugTxlistener()) {
196: TraceEjb.txlistener.log(BasicLevel.DEBUG,
197: "ec.beforeCompletion() on"
198: + ec.getEntityFactory().getEJBName());
199: }
200: }
201: }
202: }
|