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: JEntitySwitchCST.java 9860 2006-11-23 16:40:22Z durieuxp $
023: * --------------------------------------------------------------------------
024: */package org.objectweb.jonas_ejb.container;
025:
026: import javax.ejb.EJBException;
027: import javax.ejb.ObjectNotFoundException;
028: import javax.ejb.TransactionRolledbackLocalException;
029: import javax.transaction.Status;
030: import javax.transaction.SystemException;
031: import javax.transaction.Transaction;
032:
033: import org.objectweb.jonas_ejb.deployment.api.EntityDesc;
034:
035: import org.objectweb.util.monolog.api.BasicLevel;
036:
037: /**
038: * Container Serialized Transacted (CST) lock-policy.
039: * Transaction Isolation managed by the container.
040: * All transactions are serialized.
041: * @author Philippe Durieux
042: */
043: public class JEntitySwitchCST extends JEntitySwitch {
044:
045: /**
046: * unique EntityContext
047: */
048: protected JEntityContext itContext = null;
049:
050: /**
051: * empty constructor. Object is initialized via init() because it is
052: * implemented differently according to jorm mappers.
053: */
054: public JEntitySwitchCST() {
055: lockpolicy = EntityDesc.LOCK_CONTAINER_SERIALIZED_TRANSACTED;
056: }
057:
058: protected void initpolicy(JEntityFactory bf) {
059: // Setting this to true (if CMP2, !prefetch, and !shared)
060: // would be equivalent to the CRW policy.
061: lazyregister = false;
062: }
063:
064: protected JEntityContext getContext4Tx(Transaction tx) {
065: return itContext;
066: }
067:
068: protected void setContext4Tx(Transaction tx, JEntityContext ctx) {
069: if (TraceEjb.isDebugContext()) {
070: TraceEjb.context.log(BasicLevel.DEBUG, "set itContext="
071: + ctx);
072: }
073: itContext = ctx;
074: }
075:
076: protected void removeContext4Tx(Transaction tx) {
077: if (TraceEjb.isDebugContext()) {
078: TraceEjb.context.log(BasicLevel.DEBUG, "unset itContext="
079: + itContext);
080: }
081: itContext = null;
082: }
083:
084: /**
085: * Wait until I'm allowed to work on this instance.
086: * Transaction isolation may be done here, depending on lock-policy.
087: * @param tx Transaction
088: */
089: public void waitmyturn(Transaction tx) {
090: if (tx == null) {
091: // Must wait in case of TX
092: while (runningtx != null) {
093: if (TraceEjb.isDebugSynchro()) {
094: TraceEjb.synchro.log(BasicLevel.DEBUG, ident
095: + "mapICtx IH: WAIT end IT");
096: }
097: waiters++;
098: try {
099: wait(deadlockTimeout);
100: if (TraceEjb.isDebugSynchro())
101: TraceEjb.synchro.log(BasicLevel.DEBUG, ident
102: + "mapICtx IH: NOTIFIED");
103: } catch (InterruptedException e) {
104: if (TraceEjb.isDebugSynchro())
105: TraceEjb.synchro.log(BasicLevel.DEBUG, ident
106: + "mapICtx IH: INTERRUPTED");
107: } catch (Exception e) {
108: throw new EJBException(
109: "JEntitySwitch synchronization pb", e);
110: } finally {
111: waiters--;
112: }
113: }
114: } else {
115: int waitcount = 0;
116: Transaction lastrunning = null;
117: // Must wait in case of other TX, or if non tx methods are using instance
118: while (countIH > 0
119: || (runningtx != null && !tx.equals(runningtx))) {
120: if (countIH > 0) {
121: if (TraceEjb.isDebugSynchro())
122: TraceEjb.synchro.log(BasicLevel.DEBUG, ident
123: + "mapICtx IT: WAIT end IH");
124: } else {
125: if (TraceEjb.isDebugSynchro())
126: TraceEjb.synchro.log(BasicLevel.DEBUG, ident
127: + "mapICtx IT: WAIT end IT");
128: // deadlock detection
129: blockedtx.add(tx);
130: if (waitcount > 0 && runningtx.equals(lastrunning)
131: && bf.isDeadLocked(runningtx)) {
132: blockedtx.remove(tx);
133: try {
134: tx.setRollbackOnly();
135: } catch (SystemException e) {
136: TraceEjb.logger
137: .log(
138: BasicLevel.ERROR,
139: ident
140: + "getICtx IT: unexpected exception setting rollbackonly");
141: }
142: TraceEjb.logger
143: .log(
144: BasicLevel.WARN,
145: ident
146: + "getICtx IT: transaction rolled back");
147: throw new TransactionRolledbackLocalException(
148: "possible deadlock");
149: }
150: lastrunning = runningtx;
151: }
152: waitcount++;
153: waiters++;
154: try {
155: wait(deadlockTimeout);
156: if (TraceEjb.isDebugSynchro())
157: TraceEjb.synchro.log(BasicLevel.DEBUG, ident
158: + "mapICtx IT: NOTIFIED");
159: } catch (InterruptedException e) {
160: if (TraceEjb.isDebugSynchro())
161: TraceEjb.synchro.log(BasicLevel.DEBUG, ident
162: + "mapICtx IT: INTERRUPTED");
163: } catch (Exception e) {
164: throw new EJBException(
165: "JEntitySwitch synchronization pb", e);
166: } finally {
167: waiters--;
168: if (lastrunning != null) {
169: blockedtx.remove(tx);
170: }
171: }
172: // If transaction has been rolledback or set rollback only, give
173: // up.
174: int status = Status.STATUS_ROLLEDBACK;
175: try {
176: status = tx.getStatus();
177: } catch (SystemException e) {
178: TraceEjb.logger
179: .log(
180: BasicLevel.ERROR,
181: ident
182: + "getICtx IT: unexpected exception getting transaction status");
183: }
184: switch (status) {
185: case Status.STATUS_MARKED_ROLLBACK:
186: case Status.STATUS_ROLLEDBACK:
187: case Status.STATUS_ROLLING_BACK:
188: TraceEjb.logger.log(BasicLevel.WARN, ident
189: + "getICtx IT: transaction rolled back");
190: throw new TransactionRolledbackLocalException(
191: "rollback occured while waiting");
192: }
193: }
194: }
195:
196: }
197:
198: /**
199: * Try to bind a JEntityContext if none already bound. Called by finder
200: * methods. This is actually kind of optimization.
201: * Can be bypassed if problems: just return false.
202: * @param tx - the Transaction object
203: * @param bctx The Entity Context
204: * @param simple True if simple finder method
205: * @return true if context has been bound to this EntitySwitch.
206: */
207: public synchronized boolean tryBindICtx(Transaction tx,
208: JEntityContext bctx, boolean simple)
209: throws ObjectNotFoundException {
210: if (shared) {
211: // In case of shared=true (and select4update) don't try to cache an instance too early.
212: // This sometimes leaded to problems.
213: // Must check if removed and simple finder
214: if (simple && getContext4Tx(tx) != null
215: && getContext4Tx(tx).isMarkedRemoved()) {
216: throw new ObjectNotFoundException(
217: "Instance is currently being removed");
218: }
219: TraceEjb.context.log(BasicLevel.DEBUG,
220: "shared => don't cache too early");
221: return false;
222: }
223: return super .tryBindICtx(tx, bctx, simple);
224: }
225:
226: /**
227: * try to passivate instances
228: * @param store not used for this policy
229: * @param passivate always true for this policy
230: * @return result of operation: (not really used here)
231: * ALL_DONE = instances passivated
232: * NOT_DONE = not all passivated
233: */
234: public synchronized int passivateIH(boolean store, boolean passivate) {
235: if (isdetached) {
236: return NOT_DONE;
237: }
238: // Don't passivate too recent instances to avoid problems at create
239: long ttd = estimestamp - System.currentTimeMillis();
240: if (ttd > 0) {
241: TraceEjb.context.log(BasicLevel.DEBUG, "too recent");
242: return NOT_DONE;
243: }
244:
245: // Only 1 instance with this policy
246: JEntityContext jec = getContext4Tx(null);
247: if (countIH == 0 && runningtx == null && countIT == 0) {
248: if (jec != null) {
249: if (jec.isMarkedRemoved()) {
250: // should not go here ?
251: TraceEjb.context.log(BasicLevel.ERROR,
252: "marked removed");
253: discardContext(null, true, true);
254: return ALL_DONE;
255: }
256: if (TraceEjb.isDebugContext()) {
257: TraceEjb.context.log(BasicLevel.DEBUG,
258: "passivate: " + jec);
259: }
260: if (!jec.passivate()) {
261: TraceEjb.context.log(BasicLevel.DEBUG, ident
262: + " not passivated ");
263: return NOT_DONE;
264: }
265: if (jec.getMyTx() != null) {
266: TraceEjb.context.log(BasicLevel.WARN,
267: "Will forget Tx???");
268: }
269: // Will be pooled only if min-pool-size not reached in free list.
270: bf.releaseJContext(jec, 1);
271: removeContext4Tx(null);
272: // notify waiters for new instances
273: if (waiters > 0) {
274: notifyAll();
275: }
276: }
277: // look if we can destroy the objects
278: if (inactivityTimeout > 0) {
279: ttd = inactivityTimeout + estimestamp
280: - System.currentTimeMillis();
281: if (ttd <= 0) {
282: detachPk();
283: estimestamp = System.currentTimeMillis();
284: }
285: }
286:
287: return ALL_DONE;
288: }
289: return NOT_DONE;
290: }
291:
292: /**
293: * Instance is ready to use for new transaction.
294: */
295: public synchronized void endIH() {
296: TraceEjb.synchro.log(BasicLevel.ERROR, ident);
297: return; // NEVER
298: }
299:
300: /**
301: * @return State of this instance. State values are 0=in-tx, 1=out-tx, 2=idle,
302: * 3=passive, 4=removed. we don't synchronize this method to avoid
303: * jadmin blocks
304: */
305: public int getState() {
306: if (itContext != null) {
307: if (itContext.isMarkedRemoved()) {
308: return 4;
309: } else {
310: if (runningtx != null) {
311: return 0;
312: } else {
313: return inDirtyList ? 1 : 2;
314: }
315: }
316: }
317: return 3;
318: }
319:
320: }
|