00001
00026 package org.objectweb.jonas_ejb.container;
00027
00028 import java.rmi.NoSuchObjectException;
00029 import java.rmi.RemoteException;
00030
00031 import javax.ejb.EJBException;
00032 import javax.ejb.EntityBean;
00033 import javax.ejb.NoSuchObjectLocalException;
00034 import javax.ejb.ObjectNotFoundException;
00035 import javax.ejb.TimedObject;
00036 import javax.ejb.Timer;
00037 import javax.ejb.TimerService;
00038 import javax.transaction.Status;
00039 import javax.transaction.SystemException;
00040 import javax.transaction.Transaction;
00041
00042 import org.objectweb.jonas_ejb.deployment.api.EntityDesc;
00043 import org.objectweb.jonas_ejb.deployment.api.MethodDesc;
00044 import org.objectweb.jonas_timer.TraceTimer;
00045
00046 import org.objectweb.util.monolog.api.BasicLevel;
00047
00059 public abstract class JEntitySwitch {
00060
00064 protected JEntityFactory bf;
00065
00069 protected Object pk = null;
00070
00074 protected JEntityLocal local = null;
00075
00079 protected JEntityRemote remote = null;
00080
00084 protected long inactivityTimeout;
00085
00089 protected boolean shared;
00090
00101 protected int lockpolicy;
00102
00107 protected boolean txUpdates;
00108
00112 protected TimerService myTimerService = null;
00113
00117 protected int countIH = 0;
00118
00122 protected int countIT = 0;
00123
00127 protected int waiters = 0;
00128
00133 protected boolean inDirtyList = false;
00134
00140 protected boolean mustReload = false;
00141
00146 protected boolean mustStore = false;
00147
00152 protected boolean todiscard = false;
00153
00154
00155
00156 protected Transaction runningtx = null;
00157
00158
00159
00160 protected Transaction writingtx = null;
00161
00166 protected Transaction blockedtx = null;
00167
00168 protected boolean isremoved = false;
00169
00170
00171 protected static int counter = 0;
00172
00173 protected String ident;
00174
00178 protected long timestamp = System.currentTimeMillis();
00179
00180 private final static long FEW_MINUTS = 180000;
00181
00182 private static final int MAX_NB_RETRY = 2;
00183
00187 protected boolean lazyregister = false;
00188
00193 protected boolean reentrant;
00194
00195 abstract JEntityContext getContext4Tx(Transaction tx);
00196
00197 abstract void setContext4Tx(Transaction tx, JEntityContext ctx);
00198
00199 abstract void removeContext4Tx(Transaction tx);
00200
00201 abstract protected void initpolicy(JEntityFactory bf);
00202
00203 abstract public boolean passivateIH(boolean passivation);
00204
00205 abstract public void endIH();
00206
00207 abstract public void notifyWriting(Transaction tx, JEntityContext bctx);
00208
00213 public JEntitySwitch() {
00214 }
00215
00223 public void init(JEntityFactory bf, Object pk) {
00224 this.bf = bf;
00225 this.pk = pk;
00226 isremoved = false;
00227 if (pk == null) {
00228 TraceEjb.logger.log(BasicLevel.ERROR, ident + "Init Entity Switch with a null PK!");
00229 throw new EJBException("Init Entity Switch with a null PK!");
00230 }
00231
00232 shared = bf.isShared();
00233 reentrant = bf.isReentrant();
00234 inactivityTimeout = bf.getInactivityTimeout() * 1000;
00235 initpolicy(bf);
00236
00237
00238 ident = "<" + counter + ":" + pk + ">";
00239 counter++;
00240
00241 countIH = 0;
00242 countIT = 0;
00243 waiters = 0;
00244
00245
00246 if (bf.getHome() != null) {
00247 try {
00248 remote = ((JEntityHome) bf.getHome()).createRemoteObject();
00249 remote.setEntitySwitch(this);
00250 } catch (RemoteException e) {
00251 throw new EJBException("cannot create Remote Object", e);
00252 }
00253 }
00254
00255
00256 if (bf.getLocalHome() != null) {
00257 local = ((JEntityLocalHome) bf.getLocalHome()).createLocalObject();
00258 local.setEntitySwitch(this);
00259 }
00260
00261
00262
00263 if (inactivityTimeout > 0) {
00264 timestamp = System.currentTimeMillis() + FEW_MINUTS;
00265 TraceTimer.logger.log(BasicLevel.DEBUG, ident + timestamp);
00266 }
00267 }
00268
00272 public JEntityLocal getLocal() {
00273 return local;
00274 }
00275
00279 public JEntityRemote getRemote() {
00280 return remote;
00281 }
00282
00287 public TimerService getEntityTimerService() {
00288
00289
00290 if (pk == null) {
00291 throw new java.lang.IllegalStateException();
00292 }
00293 if (myTimerService == null) {
00294
00295 myTimerService = new JTimerService(this);
00296 }
00297 return myTimerService;
00298 }
00299
00304 public void notifyTimeout(Timer timer) {
00305 TraceTimer.logger.log(BasicLevel.DEBUG, ident);
00306
00307 boolean committed = false;
00308 for (int nbretry = 0; ! committed && nbretry < MAX_NB_RETRY; nbretry++) {
00309 RequestCtx rctx = bf.preInvoke(bf.getTimerTxAttribute());
00310 try {
00311 JEntityContext bctx = getICtx(rctx.currTx, false);
00312 EntityBean eb = bctx.getInstance();
00313 bf.checkSecurity(null);
00314 if (eb instanceof TimedObject) {
00315 TimedObject instance = (TimedObject) eb;
00316 instance.ejbTimeout(timer);
00317 } else {
00318 throw new EJBException("The bean does not implement the `TimedObject` interface");
00319 }
00320 committed = (rctx.currTx == null) ||
00321 (rctx.currTx.getStatus() != Status.STATUS_MARKED_ROLLBACK);
00322 } catch (EJBException e) {
00323 rctx.sysExc = e;
00324 throw e;
00325 } catch (RuntimeException e) {
00326 rctx.sysExc = e;
00327 throw new EJBException("RuntimeException thrown by an enterprise Bean", e);
00328 } catch (Error e) {
00329 rctx.sysExc = e;
00330 throw new EJBException("Error thrown by an enterprise Bean" + e);
00331 } catch (RemoteException e) {
00332 rctx.sysExc = e;
00333 throw new EJBException("Remote Exception raised:", e);
00334 } catch (SystemException e) {
00335 rctx.sysExc = e;
00336 throw new EJBException("Cannot get transaction status:", e);
00337 } finally {
00338 try {
00339 bf.postInvoke(rctx);
00340 } finally {
00341 if (rctx.sysExc != null) {
00342 discardICtx(rctx.currTx);
00343 } else {
00344 releaseICtx(rctx.currTx);
00345 }
00346 }
00347 }
00348 }
00349 }
00350
00354 public Object getPrimaryKey() {
00355 if (pk == null) {
00356 throw new java.lang.IllegalStateException();
00357 }
00358 return pk;
00359 }
00360
00366 public void bindICtx(Transaction tx, JEntityContext bctx) {
00367 mapICtx(tx, bctx, true, false, false);
00368 }
00369
00377 public synchronized boolean tryBindICtx(Transaction tx, JEntityContext bctx) throws ObjectNotFoundException {
00378
00379
00380 if (getContext4Tx(tx) != null) {
00381 TraceEjb.context.log(BasicLevel.DEBUG, ident + "context already mapped!");
00382 if (getContext4Tx(tx).isMarkedRemoved()) {
00383 TraceEjb.context.log(BasicLevel.DEBUG, ident + "Removed in the current tx");
00384 throw new ObjectNotFoundException("Removed in the current tx");
00385 }
00386
00387 return false;
00388 }
00389
00390
00391 bctx.initEntityContext(this);
00392 bctx.activate(true);
00393 setContext4Tx(tx, bctx);
00394 if (!lazyregister) {
00395 if (tx == null) {
00396 if (TraceEjb.isDebugSynchro()) TraceEjb.synchro.log(BasicLevel.DEBUG, ident + "IH find");
00397 } else {
00398 if (TraceEjb.isDebugSynchro()) TraceEjb.synchro.log(BasicLevel.DEBUG, ident + "IT find");
00399 registerCtx(tx, bctx);
00400 }
00401 }
00402 return true;
00403 }
00404
00412 public JEntityContext getICtx(Transaction tx, JEntityContext newctx) {
00413 return mapICtx(tx, newctx, false, false, false);
00414 }
00415
00422 public JEntityContext getICtx(Transaction tx, boolean checkr) {
00423 return mapICtx(tx, null, false, true, checkr);
00424 }
00425
00432 public synchronized boolean terminate(Transaction tx) {
00433 TraceEjb.swapper.log(BasicLevel.DEBUG, ident);
00434 waitmyturn(tx);
00435 JEntityContext jec = getContext4Tx(tx);
00436 if (jec != null) {
00437 if (todiscard || jec.isMarkedRemoved()) {
00438 discardContext(tx, true, true);
00439 } else {
00440 try {
00441 jec.storeIfModified();
00442 } catch (Exception e) {
00443 TraceEjb.logger.log(BasicLevel.ERROR, ident, "error while storing bean state:", e);
00444 }
00445 jec.passivate();
00446 discardContext(tx, false, true);
00447 }
00448 if (waiters > 0) {
00449 TraceEjb.synchro.log(BasicLevel.DEBUG, ident + " notify");
00450 notifyAll();
00451 }
00452 }
00453 return true;
00454 }
00455
00456 abstract void waitmyturn(Transaction tx);
00457
00467 public synchronized JEntityContext mapICtx(Transaction tx, JEntityContext bctx, boolean forced, boolean holdit, boolean checkreentrance) {
00468
00469
00470 if (bf == null) {
00471 TraceEjb.synchro.log(BasicLevel.ERROR, "JEntitySwitch not initialized!");
00472 throw new EJBException("JEntitySwitch not initialized");
00473 }
00474 if (TraceEjb.isDebugSynchro()) {
00475 TraceEjb.synchro.log(BasicLevel.DEBUG, ident + " tx=" + tx);
00476 }
00477
00478
00479 if (!reentrant && checkreentrance) {
00480 if (runningtx != null && countIT > 0 && tx != null && tx.equals(runningtx)) {
00481 throw new EJBException("non-reentrant bean accessed twice in same transaction");
00482 }
00483 if (tx == null && countIH > 0) {
00484 throw new EJBException("non-reentrant bean accessed twice outside transaction");
00485 }
00486 }
00487
00488 waitmyturn(tx);
00489
00490
00491 if (inactivityTimeout > 0) {
00492 timestamp = System.currentTimeMillis() + FEW_MINUTS;
00493 TraceTimer.logger.log(BasicLevel.DEBUG, ident + timestamp);
00494 }
00495
00496
00497 boolean newtrans = false;
00498 JEntityContext jec = getContext4Tx(tx);
00499 if (forced) {
00500
00501 if (jec != null) {
00502 TraceEjb.context.log(BasicLevel.DEBUG, ident + "new context is enforced!");
00503 discardContext(tx, false, true);
00504 }
00505 jec = bctx;
00506 setContext4Tx(tx, jec);
00507 jec.initEntityContext(this);
00508 newtrans = true;
00509 isremoved = false;
00510 } else {
00511
00512 if (isremoved) {
00513 TraceEjb.logger.log(BasicLevel.WARN, ident + " has been removed.");
00514 throw new NoSuchObjectLocalException("Try to access a bean previously removed");
00515 }
00516 if (jec != null) {
00517 if (todiscard) {
00518 TraceEjb.logger.log(BasicLevel.WARN, ident + " has been discarded.");
00519 throw new NoSuchObjectLocalException("Try to access a bean previously discarded");
00520 }
00521
00522
00523 if (bctx != null) {
00524 if (TraceEjb.isDebugContext())
00525 TraceEjb.context.log(BasicLevel.DEBUG, ident + "a context was supplied!");
00526 bf.releaseJContext(bctx);
00527 }
00528
00529
00530 if (runningtx == null && lockpolicy != EntityDesc.LOCK_DATABASE) {
00531 newtrans = true;
00532 }
00533 jec.reuseEntityContext(newtrans);
00534 } else {
00535 if (bctx != null) {
00536 jec = bctx;
00537 } else {
00538
00539 jec = (JEntityContext) bf.getJContext(this);
00540 }
00541 jec.initEntityContext(this);
00542 jec.activate(true);
00543 setContext4Tx(tx, jec);
00544 newtrans = true;
00545 }
00546 }
00547
00548 if (tx != null) {
00549 if (holdit) {
00550 countIT++;
00551 }
00552
00553 if (newtrans && !lazyregister) {
00554 try {
00555 registerCtx(tx, jec);
00556 if (TraceEjb.isDebugSynchro())
00557 TraceEjb.synchro.log(BasicLevel.DEBUG, ident + "mapICtx IT: new tx, registerSynchronization");
00558 } catch (IllegalStateException e) {
00559 TraceEjb.synchro.log(BasicLevel.WARN, ident + "mapICtx IT: not registered!");
00560 }
00561 }
00562 } else {
00563 if (holdit) {
00564 countIH++;
00565 if (TraceEjb.isDebugSynchro())
00566 TraceEjb.synchro.log(BasicLevel.DEBUG, ident + "mapICtx IH count=" + countIH);
00567 if ((shared || mustReload) && countIH == 1) {
00568
00569
00570 jec.activate(false);
00571 mustReload = false;
00572 }
00573 }
00574 if (!inDirtyList && !txUpdates) {
00575 inDirtyList = true;
00576 bf.registerEJB(this);
00577 }
00578 }
00579
00580 return jec;
00581 }
00582
00583 public Transaction getBlockedTx() {
00584 return blockedtx;
00585 }
00586
00587 public Transaction getBlockingTx() {
00588 return runningtx;
00589 }
00590
00595 public synchronized void releaseICtx(Transaction tx) {
00596
00597 if (tx == null) {
00598
00599 countIH--;
00600 if (TraceEjb.isDebugSynchro())
00601 TraceEjb.synchro.log(BasicLevel.DEBUG, ident + "releaseICtx IH count=" + countIH);
00602 if (countIH == 0) {
00603 JEntityContext jec = getContext4Tx(tx);
00604 if (jec == null) {
00605 TraceEjb.context.log(BasicLevel.ERROR, ident + " No context!");
00606 Thread.dumpStack();
00607 return;
00608 }
00609 if (todiscard || jec.isMarkedRemoved()) {
00610
00611 discardContext(tx, true, true);
00612 } else {
00613 if (mustStore) {
00614 try {
00615 jec.storeIfModified();
00616 } catch (EJBException e) {
00617 TraceEjb.logger.log(BasicLevel.WARN, ident + " ejbexception:" + e);
00618 }
00619 mustStore = false;
00620 }
00621 }
00622 if (waiters > 0) {
00623 TraceEjb.synchro.log(BasicLevel.DEBUG, ident + " notify");
00624 notifyAll();
00625 }
00626 }
00627 } else {
00628
00629
00630 if (TraceEjb.isDebugSynchro()) TraceEjb.synchro.log(BasicLevel.DEBUG, ident + "releaseICtx IT");
00631 countIT--;
00632 }
00633 }
00634
00640 public synchronized void discardICtx(Transaction tx) {
00641 if (tx == null) {
00642 countIH--;
00643 if (countIH == 0) {
00644 discardContext(tx, false, false);
00645 return;
00646 }
00647 } else {
00648 countIT--;
00649 if (runningtx == null) {
00650
00651 return;
00652 }
00653 }
00654
00655 todiscard = true;
00656 }
00657
00664 public synchronized void txCompleted(Transaction tx, boolean committed) {
00665 JEntityContext jec = getContext4Tx(tx);
00666 if (jec == null) {
00667 TraceEjb.context.log(BasicLevel.ERROR, ident + " No context for this tx");
00668 return;
00669 }
00670 runningtx = null;
00671 if (writingtx != null && tx.equals(writingtx)) {
00672 writingtx = null;
00673 }
00674
00675 if (TraceEjb.isDebugSynchro()) TraceEjb.synchro.log(BasicLevel.DEBUG, ident + " txCompleted " + committed);
00676
00677
00678
00679 if (todiscard || (jec.isNewInstance() && !committed)) {
00680 discardContext(tx, !todiscard, false);
00681 return;
00682 }
00683
00684 if (jec.isMarkedRemoved()) {
00685 if (TraceEjb.isDebugContext()) TraceEjb.context.log(BasicLevel.DEBUG, ident + "remove!");
00686 discardContext(tx, committed, true);
00687 } else {
00688 if (shared || !committed) {
00689
00690
00691 if (TraceEjb.isDebugContext()) TraceEjb.context.log(BasicLevel.DEBUG, jec + " passivated!");
00692 jec.passivate();
00693 if (committed) {
00694
00695 bf.releaseJContext(jec);
00696 }
00697 removeContext4Tx(tx);
00698 } else {
00699
00700
00701 jec.detachTx();
00702 }
00703
00704
00705 if (lockpolicy == EntityDesc.LOCK_CONTAINER_READ_COMMITTED || lockpolicy == EntityDesc.LOCK_DATABASE) {
00706 mustReload = true;
00707 }
00708 }
00709 if (waiters > 0) {
00710 TraceEjb.synchro.log(BasicLevel.DEBUG, ident + " notify");
00711 notifyAll();
00712 }
00713 }
00714
00722 protected void registerCtx(Transaction tx, JEntityContext bctx) {
00723
00724 try {
00725 if (bf.registerContext(tx, bctx)) {
00726 TraceEjb.context.log(BasicLevel.DEBUG, bctx + "registered!");
00727 bctx.setRunningTx(tx);
00728 runningtx = tx;
00729 } else {
00730 TraceEjb.context.log(BasicLevel.WARN, bctx + "could not be registered!");
00731 }
00732 } catch (IllegalStateException e) {
00733 TraceEjb.logger.log(BasicLevel.ERROR, ident + "Transaction is in an illegal state");
00734 throw e;
00735 }
00736 }
00737
00744 protected void discardContext(Transaction tx, boolean forgetpk, boolean pool) {
00745 TraceEjb.context.log(BasicLevel.DEBUG, "");
00746 JEntityContext jec = getContext4Tx(tx);
00747 if (jec != null) {
00748 if (pool) {
00749 bf.releaseJContext(jec);
00750 }
00751 removeContext4Tx(tx);
00752 }
00753 if (forgetpk) {
00754 if (remote != null) {
00755 try {
00756 remote.unexportObject();
00757 } catch (NoSuchObjectException e) {
00758 TraceEjb.logger.log(BasicLevel.ERROR, ident + " unexport entity failed: " + e);
00759 }
00760 }
00761 bf.removeEJB(getPrimaryKey());
00762
00763
00764 if (myTimerService != null) {
00765 ((JTimerService) myTimerService).cancelAllTimers();
00766 myTimerService = null;
00767 }
00768
00769 isremoved = true;
00770 }
00771 todiscard = false;
00772 }
00773
00777 public int getPolicy() {
00778 return lockpolicy;
00779 }
00780
00786 abstract public int getState();
00790 public JFactory getBeanFactory() {
00791 return bf;
00792 }
00793
00794 }