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: JEntitySwitchCS.java 9805 2006-10-27 08:28:32Z durieuxp $
023: * --------------------------------------------------------------------------
024: */package org.objectweb.jonas_ejb.container;
025:
026: import javax.ejb.EJBException;
027: import javax.ejb.TransactionRolledbackLocalException;
028: import javax.transaction.Status;
029: import javax.transaction.SystemException;
030: import javax.transaction.Transaction;
031:
032: import org.objectweb.jonas_ejb.deployment.api.EntityDesc;
033:
034: import org.objectweb.util.monolog.api.BasicLevel;
035:
036: /**
037: * Container Serialized (CS) lock-policy.
038: * Transaction Isolation managed by the container.
039: * Non transactional methods of the bean can modify the bean.
040: * @author Philippe Durieux
041: */
042: public class JEntitySwitchCS extends JEntitySwitchCST {
043:
044: /**
045: * time in millisec. to keep instance state in memory without storing it
046: * After this time, instance is stored on database.
047: * before.
048: */
049: protected long passivationTimeout; // millisec.
050:
051: /**
052: * timestamp uses for passivationTimeout
053: */
054: protected long ptimestamp;
055:
056: /**
057: * empty constructor. Object is initialized via init() because it is
058: * implemented differently according to jorm mappers.
059: */
060: public JEntitySwitchCS() {
061: lockpolicy = EntityDesc.LOCK_CONTAINER_SERIALIZED;
062: txUpdates = false;
063: }
064:
065: protected void initpolicy(JEntityFactory bf) {
066: lazyregister = false;
067: passivationTimeout = bf.getPassivationTimeout() * 1000;
068: ptimestamp = System.currentTimeMillis();
069: }
070:
071: public void waitmyturn(Transaction tx) {
072: // Synchronization.
073: if (tx == null) {
074: // Must wait in case of TX
075: while (runningtx != null) {
076: if (TraceEjb.isDebugSynchro()) {
077: TraceEjb.synchro.log(BasicLevel.DEBUG, ident
078: + "mapICtx IH: WAIT end IT");
079: }
080: waiters++;
081: try {
082: wait(10000L);
083: if (TraceEjb.isDebugSynchro())
084: TraceEjb.synchro.log(BasicLevel.DEBUG, ident
085: + "mapICtx IH: NOTIFIED");
086: } catch (InterruptedException e) {
087: if (TraceEjb.isDebugSynchro())
088: TraceEjb.synchro.log(BasicLevel.DEBUG, ident
089: + "mapICtx IH: INTERRUPTED");
090: } catch (Exception e) {
091: throw new EJBException(
092: "JEntitySwitch synchronization pb", e);
093: } finally {
094: waiters--;
095: }
096: }
097: } else {
098: int waitcount = 0;
099: Transaction lastrunning = null;
100:
101: // Must wait in case of TX or if instance has been modified outside
102: // transaction.
103: // Don't wait transactions if 1 instance per transaction.
104: while (inDirtyList
105: || (runningtx != null && !tx.equals(runningtx))) {
106: if (inDirtyList) {
107: // instance is dirty : must write it before working on it again.
108: // this is mandatory to be able to retrieve a good state in case
109: // of rollback.
110: if (TraceEjb.isDebugSynchro())
111: TraceEjb.synchro.log(BasicLevel.DEBUG, ident
112: + "mapICtx IT: WAIT end IH");
113: // Cannot passivate here, because must be done outside tx
114: // context
115: bf.synchronizeEntities();
116: } else {
117: if (TraceEjb.isDebugSynchro())
118: TraceEjb.synchro.log(BasicLevel.DEBUG, ident
119: + "mapICtx IT: WAIT end IT");
120: // deadlock detection
121: blockedtx.add(tx);
122: if (waitcount > 0 && runningtx.equals(lastrunning)
123: && bf.isDeadLocked(runningtx)) {
124: blockedtx.remove(tx);
125: try {
126: tx.setRollbackOnly();
127: } catch (SystemException e) {
128: TraceEjb.logger
129: .log(
130: BasicLevel.ERROR,
131: ident
132: + "getICtx IT: unexpected exception setting rollbackonly");
133: }
134: TraceEjb.logger
135: .log(
136: BasicLevel.WARN,
137: ident
138: + "getICtx IT: transaction rolled back");
139: throw new TransactionRolledbackLocalException(
140: "possible deadlock");
141: }
142: lastrunning = runningtx;
143: }
144: waitcount++;
145: waiters++;
146: try {
147: wait(deadlockTimeout);
148: if (TraceEjb.isDebugSynchro())
149: TraceEjb.synchro.log(BasicLevel.DEBUG, ident
150: + "mapICtx IT: NOTIFIED");
151: } catch (InterruptedException e) {
152: if (TraceEjb.isDebugSynchro())
153: TraceEjb.synchro.log(BasicLevel.DEBUG, ident
154: + "mapICtx IT: INTERRUPTED");
155: } catch (Exception e) {
156: throw new EJBException(
157: "JEntitySwitch synchronization pb", e);
158: } finally {
159: waiters--;
160: if (lastrunning != null) {
161: blockedtx.remove(tx);
162: }
163: }
164: // If transaction has been rolledback or set rollback only, give
165: // up.
166: int status = Status.STATUS_ROLLEDBACK;
167: try {
168: status = tx.getStatus();
169: } catch (SystemException e) {
170: TraceEjb.logger
171: .log(
172: BasicLevel.ERROR,
173: ident
174: + "getICtx IT: unexpected exception getting transaction status");
175: }
176: switch (status) {
177: case Status.STATUS_MARKED_ROLLBACK:
178: case Status.STATUS_ROLLEDBACK:
179: case Status.STATUS_ROLLING_BACK:
180: TraceEjb.logger.log(BasicLevel.WARN, ident
181: + "getICtx IT: transaction rolled back");
182: throw new TransactionRolledbackLocalException(
183: "rollback occured while waiting");
184: }
185: }
186: }
187:
188: }
189:
190: /**
191: * try to passivate instances
192: * @param store If we want to store the bean state first.
193: * @param passivate If we want to pasivate the bean also
194: * @return int result of operation:
195: * ALL_DONE = just remove from dirty list.
196: * STORED = remove from dirty list and call endIH
197: * NOT_DONE = keep it in the list.
198: */
199: public synchronized int passivateIH(boolean store, boolean passivate) {
200:
201: JEntityContext jec = getContext4Tx(null);
202:
203: // Check if we should store the bean state first.
204: // This is CS policy specific.
205: boolean toStore = store;
206: if (!store && inDirtyList) {
207: toStore = System.currentTimeMillis() - ptimestamp > passivationTimeout;
208: }
209:
210: // If instance busy, do nothing now.
211: if (runningtx != null || countIT > 0) {
212: if (TraceEjb.isDebugSynchro()) {
213: TraceEjb.synchro.log(BasicLevel.DEBUG, ident
214: + " used in TX");
215: }
216: return NOT_DONE;
217: }
218: if (countIH > 0) {
219: // will be stored later, when released by last thread (countIH = 0)
220: if (TraceEjb.isDebugSynchro()) {
221: TraceEjb.synchro.log(BasicLevel.DEBUG, ident
222: + " used off TX");
223: }
224: mustStore = toStore;
225: return toStore ? ALL_DONE : STORED;
226: }
227:
228: int ret = toStore ? STORED : NOT_DONE;
229: if (jec != null) {
230: // If marked removed, don't need to store.
231: if (jec.isMarkedRemoved()) {
232: discardContext(null, true, true);
233: return STORED;
234: }
235: // Store the bean state
236: if (toStore) {
237: if (TraceEjb.isDebugContext()) {
238: TraceEjb.context.log(BasicLevel.DEBUG, ident
239: + " store object");
240: }
241: try {
242: jec.storeIfModified();
243: } catch (Exception e) {
244: TraceEjb.logger.log(BasicLevel.ERROR, ident,
245: " error while storing bean state:", e);
246: }
247: mustStore = false;
248: // restart timer to compute next time to store.
249: ptimestamp = System.currentTimeMillis();
250: // don't set dirty false here, will be done in endIH.
251: // this prevent duplicates in dirty list.
252: }
253: // passivation has not been required: just return now.
254: if (!passivate) {
255: return ret;
256: }
257: // Don't passivate too recent instances to avoid problems at create
258: if (System.currentTimeMillis() < estimestamp) {
259: TraceEjb.context.log(BasicLevel.DEBUG, "too recent ");
260: return ret;
261: }
262:
263: // Passivate the instance.
264: if (TraceEjb.isDebugContext()) {
265: TraceEjb.context.log(BasicLevel.DEBUG, "passivate: "
266: + jec);
267: }
268: if (!jec.passivate()) {
269: return ret;
270: }
271: if (jec.getMyTx() != null) {
272: TraceEjb.context.log(BasicLevel.WARN,
273: "Will forget Tx???");
274: }
275: // Will be pooled only if min-pool-size not reached in free list.
276: bf.releaseJContext(jec, 1);
277: removeContext4Tx(null);
278: // notify waiters for new instances
279: if (waiters > 0) {
280: notifyAll();
281: }
282: }
283:
284: // Instance is passivated, look if we can destroy the objects.
285: if (inactivityTimeout > 0
286: && System.currentTimeMillis() - estimestamp > inactivityTimeout) {
287: detachPk();
288: estimestamp = System.currentTimeMillis();
289: ret = ALL_DONE;
290: }
291: return ret;
292: }
293:
294: /**
295: * Instance is ready to use for new transaction.
296: */
297: public synchronized void endIH() {
298: inDirtyList = false;
299: if (getContext4Tx(null) == null) {
300: if (TraceEjb.isDebugSynchro())
301: TraceEjb.synchro.log(BasicLevel.DEBUG, ident
302: + " discarded!");
303: return;
304: }
305: if (TraceEjb.isDebugSynchro()) {
306: if (countIH == 0) {
307: TraceEjb.synchro.log(BasicLevel.DEBUG, ident
308: + " ready again");
309: } else {
310: TraceEjb.synchro
311: .log(BasicLevel.DEBUG, ident + " busy!");
312: }
313: }
314: if (waiters > 0) {
315: if (TraceEjb.isDebugSynchro()) {
316: TraceEjb.synchro.log(BasicLevel.DEBUG, ident
317: + " notify");
318: }
319: notifyAll();
320: }
321: }
322:
323: }
|