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: JEntitySwitchCRW.java 9922 2007-01-15 15:24:14Z durieuxp $
023: * --------------------------------------------------------------------------
024: */package org.objectweb.jonas_ejb.container;
025:
026: import javax.ejb.EJBException;
027: import javax.ejb.NoSuchObjectLocalException;
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 Optimistic lock-policy.
039: * Only 1 thread can write. All other threads can read, without
040: * waiting for a committed state. There is no wait.
041: * This policy cannot be used with shared=true.
042: * This policy is only possible for CMP2 entitybeans.
043: * @author Philippe Durieux
044: */
045: public class JEntitySwitchCRW extends JEntitySwitchCST {
046:
047: /**
048: * empty constructor. Object is initialized via init() because it is
049: * implemented differently according to jorm mappers.
050: */
051: public JEntitySwitchCRW() {
052: lockpolicy = EntityDesc.LOCK_CONTAINER_READ_WRITE;
053: }
054:
055: protected void initpolicy(JEntityFactory bf) {
056: // This policy implies CMP2, necessary for lazy registering
057: lazyregister = !bf.isPrefetch();
058: }
059:
060: /**
061: * Wait until I'm allowed to work on this instance.
062: * Transaction isolation may be done here, depending on lock-policy.
063: * @param tx Transaction
064: */
065: public void waitmyturn(Transaction tx) {
066: if (tx != null) {
067: int waitcount = 0;
068: Transaction lastrunning = null;
069: // Must wait in case of other TX
070: while (runningtx != null && !tx.equals(runningtx)) {
071: if (TraceEjb.isDebugSynchro())
072: TraceEjb.synchro.log(BasicLevel.DEBUG, ident
073: + "mapICtx IT: WAIT end IT");
074: // deadlock detection
075: blockedtx.add(tx);
076: if (waitcount > 0 && runningtx.equals(lastrunning)
077: && bf.isDeadLocked(runningtx)) {
078: blockedtx.remove(tx);
079: try {
080: tx.setRollbackOnly();
081: } catch (SystemException e) {
082: TraceEjb.logger
083: .log(
084: BasicLevel.ERROR,
085: ident
086: + "getICtx IT: unexpected exception setting rollbackonly");
087: }
088: TraceEjb.logger.log(BasicLevel.WARN, ident
089: + "getICtx IT: transaction rolled back");
090: throw new TransactionRolledbackLocalException(
091: "possible deadlock");
092: }
093: lastrunning = runningtx;
094: waitcount++;
095: waiters++;
096: try {
097: wait(deadlockTimeout);
098: if (TraceEjb.isDebugSynchro())
099: TraceEjb.synchro.log(BasicLevel.DEBUG, ident
100: + "mapICtx IT: NOTIFIED");
101: } catch (InterruptedException e) {
102: if (TraceEjb.isDebugSynchro())
103: TraceEjb.synchro.log(BasicLevel.DEBUG, ident
104: + "mapICtx IT: INTERRUPTED");
105: } catch (Exception e) {
106: throw new EJBException(
107: "JEntitySwitch synchronization pb", e);
108: } finally {
109: waiters--;
110: blockedtx.remove(tx);
111: }
112: // If transaction has been rolledback or set rollback only, give
113: // up.
114: int status = Status.STATUS_ROLLEDBACK;
115: try {
116: status = tx.getStatus();
117: } catch (SystemException e) {
118: TraceEjb.logger
119: .log(
120: BasicLevel.ERROR,
121: ident
122: + "getICtx IT: unexpected exception getting transaction status");
123: }
124: switch (status) {
125: case Status.STATUS_MARKED_ROLLBACK:
126: case Status.STATUS_ROLLEDBACK:
127: case Status.STATUS_ROLLING_BACK:
128: TraceEjb.logger.log(BasicLevel.WARN, ident
129: + "getICtx IT: transaction rolled back");
130: throw new TransactionRolledbackLocalException(
131: "rollback occured while waiting");
132: }
133: }
134: }
135: }
136:
137: /**
138: * Map a context and its instance.
139: * Could use the inherited method here. This is just a simplified
140: * version for performances.
141: * @param tx - the Transaction object
142: * @param bctx - the JEntityContext to bind if not null
143: * @param forced - force to take this context. (case of create)
144: * @param holdit - increment count to hold it, a release will be called
145: * later.
146: * @return JEntityContext actually mapped
147: */
148: public synchronized JEntityContext mapICtx(Transaction tx,
149: JEntityContext bctx, boolean forced, boolean holdit,
150: boolean notused) {
151:
152: waitmyturn(tx);
153:
154: // Set the timestamp to a big value because instance will be used.
155: estimestamp = System.currentTimeMillis() + FEW_SECONDS;
156:
157: // Check the case where the object is detached
158: if (isdetached) {
159: JEntityFactory fact = (JEntityFactory) bf;
160: JEntitySwitch old = fact.existEJB(getPrimaryKey(), this );
161: if (old != null) {
162: throw new NoSuchObjectLocalException(
163: "Inactivity timeout expired");
164: }
165: if (TraceEjb.isDebugSwapper()) {
166: TraceEjb.swapper.log(BasicLevel.DEBUG, "*** Reuse "
167: + ident + " after timeout");
168: }
169: isdetached = false;
170: }
171:
172: // Choose the context to use.
173: boolean newtrans = false;
174: boolean isdirty = false;
175: JEntityContext jec = itContext;
176: if (forced) {
177: // If the new context is enforced, we must first release the older
178: if (jec != null) {
179: discardContext(tx, false, true);
180: }
181: jec = bctx;
182: itContext = jec;
183: isdirty = jec.initEntityContext(this );
184: newtrans = true;
185: } else {
186: // First check if bean still exists
187: if (isremoved) {
188: TraceEjb.logger.log(BasicLevel.WARN, ident
189: + " has been removed.");
190: throw new NoSuchObjectLocalException(
191: "Try to access a bean previously removed");
192: }
193: if (jec != null) {
194: if (todiscard) {
195: TraceEjb.logger.log(BasicLevel.WARN, ident
196: + " has been discarded.");
197: throw new NoSuchObjectLocalException(
198: "Try to access a bean previously discarded");
199: }
200: // Reuse the Context for this transaction.
201: // If a context was supplied, release it first.
202: if (bctx != null) {
203: bf.releaseJContext(bctx, 2);
204: }
205: if (runningtx == null) { // TODO change this checking
206: newtrans = true;
207: }
208: jec.reuseEntityContext(newtrans);
209: } else {
210: if (bctx != null) {
211: jec = bctx;
212: } else {
213: // no Context available : get one from the pool.
214: jec = (JEntityContext) bf.getJContext(this );
215: }
216: isdirty = jec.initEntityContext(this );
217: jec.activate(true);
218: itContext = jec; // after activate
219: newtrans = true;
220: }
221: }
222:
223: if (tx != null) {
224: // Register Context now, except if no new transaction
225: if (newtrans && (!lazyregister || isdirty)) {
226: try {
227: registerCtx(tx, jec);
228: } catch (IllegalStateException e) {
229: TraceEjb.synchro.log(BasicLevel.WARN, ident
230: + "mapICtx IT: not registered!", e);
231: }
232: }
233: if (holdit) {
234: countIT++;
235: }
236: } else {
237: if (holdit) {
238: countIH++;
239: if (shared && countIH == 1) {
240: // reload state that could have been modified by
241: // transactions.
242: jec.activate(false);
243: }
244: }
245: }
246: return jec;
247: }
248:
249: }
|