0001: /**
0002: * JOnAS: Java(TM) Open Application Server
0003: * Copyright (C) 1999-2005 Bull S.A.
0004: * Contact: jonas-team@objectweb.org
0005: *
0006: * This library is free software; you can redistribute it and/or
0007: * modify it under the terms of the GNU Lesser General Public
0008: * License as published by the Free Software Foundation; either
0009: * version 2.1 of the License, or any later version.
0010: *
0011: * This library is distributed in the hope that it will be useful,
0012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * Lesser General Public License for more details.
0015: *
0016: * You should have received a copy of the GNU Lesser General Public
0017: * License along with this library; if not, write to the Free Software
0018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
0019: * USA
0020: *
0021: * --------------------------------------------------------------------------
0022: * $Id: JFactory.java 10144 2007-04-05 06:48:57Z durieuxp $
0023: * --------------------------------------------------------------------------
0024: */package org.objectweb.jonas_ejb.container;
0025:
0026: import java.io.File;
0027: import java.io.FileInputStream;
0028: import java.io.IOException;
0029: import java.lang.reflect.Field;
0030: import java.net.URLClassLoader;
0031: import java.rmi.NoSuchObjectException;
0032: import java.rmi.RemoteException;
0033: import java.util.Enumeration;
0034: import java.util.Hashtable;
0035: import java.util.Properties;
0036:
0037: import javax.ejb.EJBException;
0038: import javax.ejb.Timer;
0039: import javax.ejb.TimerService;
0040: import javax.ejb.TransactionRequiredLocalException;
0041: import javax.ejb.TransactionRolledbackLocalException;
0042: import javax.naming.Context;
0043: import javax.naming.InitialContext;
0044: import javax.naming.NamingException;
0045: import javax.resource.spi.work.WorkManager;
0046: import javax.transaction.NotSupportedException;
0047: import javax.transaction.RollbackException;
0048: import javax.transaction.Status;
0049: import javax.transaction.SystemException;
0050: import javax.transaction.Transaction;
0051: import javax.transaction.TransactionRequiredException;
0052: import javax.transaction.TransactionRolledbackException;
0053:
0054: import org.objectweb.carol.cmi.configuration.TraceCmi;
0055: import org.objectweb.carol.cmi.ha.RequestId;
0056: import org.objectweb.carol.rmi.exception.NoSuchObjectExceptionHelper;
0057: import org.objectweb.carol.util.configuration.ConfigurationRepository;
0058: import org.objectweb.jonas.ha.interceptor.HACurrentDelegateImpl;
0059: import org.objectweb.jonas_ejb.deployment.api.BeanDesc;
0060: import org.objectweb.jonas_ejb.deployment.api.MethodDesc;
0061: import org.objectweb.jonas_ejb.lib.EJBInvocation;
0062: import org.objectweb.jonas_lib.files.FileUtils;
0063: import org.objectweb.jonas_lib.naming.ContainerNaming;
0064: import org.objectweb.jonas_lib.version.Version;
0065: import org.objectweb.jonas_timer.TraceTimer;
0066: import org.objectweb.jotm.TransactionContext;
0067: import org.objectweb.security.context.SecurityContext;
0068: import org.objectweb.security.context.SecurityCurrent;
0069: import org.objectweb.transaction.jta.TransactionManager;
0070: import org.objectweb.util.monolog.api.BasicLevel;
0071:
0072: /**
0073: * This class is a factory for beans.
0074: * @author Philippe Durieux
0075: * @author Florent Benoit (JACC security)
0076: */
0077: public abstract class JFactory implements BeanFactory {
0078:
0079: protected JContainer cont;
0080:
0081: protected boolean stopped = false;
0082:
0083: protected ContainerNaming naming = null;
0084:
0085: protected TransactionManager tm = null;
0086:
0087: protected WorkManager wm = null;
0088:
0089: protected Context JNDICtx = null; // JNDI Context for this component
0090:
0091: protected String ejbname = null;
0092:
0093: protected BeanDesc dd;
0094:
0095: protected Properties ejb10Env = null;
0096:
0097: protected TimerService myTimerService = null;
0098:
0099: protected boolean txbeanmanaged = false; // always false if entity bean
0100:
0101: protected Class beanclass = null; // class of the bean instance
0102:
0103: protected File passivationDir;
0104:
0105: /**
0106: * initial value for pool size
0107: */
0108: protected int minPoolSize;
0109:
0110: protected int maxCacheSize;
0111:
0112: protected boolean isClusterReplicated;
0113:
0114: /**
0115: * Need for checking transaction interoperability which is only needs when
0116: * using iiop
0117: */
0118: private boolean iiopProtocolAvailable = false;
0119:
0120: /**
0121: * Transational attribute for ejbTimeout method.
0122: * default is TX_REQUIRES_NEW
0123: */
0124: protected int timerTxAttr;
0125:
0126: /**
0127: * Signature for ejbTimeout
0128: */
0129: protected String ejbTimeoutSignature;
0130:
0131: /**
0132: * constructor (for entity) must be without parameters (required by Jorm)
0133: */
0134: public JFactory() {
0135: if (TraceEjb.isDebugIc()) {
0136: TraceEjb.interp.log(BasicLevel.DEBUG, ejbname);
0137: }
0138: }
0139:
0140: public void stopContainer() {
0141: stopped = true;
0142: }
0143:
0144: public boolean isStopped() {
0145: return stopped;
0146: }
0147:
0148: /**
0149: * Get the directory where to store stateful state and timers
0150: */
0151: public File getPassivationDir() {
0152: return passivationDir;
0153: }
0154:
0155: /**
0156: * constructor (for session)
0157: * @param dd The bean deployment descriptor
0158: * @param cont the container for this bean
0159: */
0160: public JFactory(BeanDesc dd, JContainer cont) {
0161: if (TraceEjb.isDebugIc()) {
0162: TraceEjb.interp.log(BasicLevel.DEBUG, "");
0163: }
0164: init(dd, cont);
0165: }
0166:
0167: /**
0168: * Init this object
0169: * @param dd the deployment descriptor
0170: * @param cont the Container
0171: */
0172: public void init(BeanDesc dd, JContainer cont) {
0173: this .cont = cont;
0174: this .dd = dd;
0175: naming = cont.getContainerNaming();
0176: tm = cont.getTransactionManager();
0177: wm = cont.getWorkManager();
0178: ejbname = dd.getEjbName();
0179: ejb10Env = dd.getEjb10Environment();
0180: maxCacheSize = dd.getCacheMax();
0181: minPoolSize = dd.getPoolMin();
0182: isClusterReplicated = dd.isClusterReplicated();
0183:
0184: // Check the JOnAS version used for deployment in case of Session or
0185: // Entity
0186: String cln = dd.getFullWrpHomeName() != null ? dd
0187: .getFullWrpHomeName() : dd.getFullWrpLocalHomeName();
0188: if (cln != null) {
0189: checkJonasVersion(cln);
0190: }
0191:
0192: // Create a JNDI context for this bean component
0193: try {
0194: JNDICtx = naming.createEnvironmentContext(ejbname);
0195: cont.setBeanEnvironment(JNDICtx, dd);
0196: } catch (NamingException e) {
0197: TraceEjb.logger.log(BasicLevel.ERROR,
0198: "cannot build naming for this component", e);
0199: throw new EJBException(
0200: "Cannot build naming for this component", e);
0201: }
0202:
0203: // Get the class of the bean instance (for ejbCreate, ejbRemove, ...)
0204: String cn = null;
0205: try {
0206: cn = dd.getFullDerivedBeanName();
0207: beanclass = cont.getClassLoader().loadClass(cn);
0208: /*
0209: * This is currently being commented out. The spec doesn't exclude
0210: * having the same class multiple times, just that the first class
0211: * will be the one that is loaded. With this code included, this
0212: * precludes a user from using the same bean classes but with
0213: * different transactional attributes for example within the same
0214: * ear file. // prevent from generated class name collision in a
0215: * J2EE application if (!isClassAvailable(cn, true)) { throw new
0216: * EJBException("Container has detected duplicated class " + cn); }
0217: */
0218: } catch (ClassNotFoundException e) {
0219: TraceEjb.logger.log(BasicLevel.ERROR,
0220: "failed to find class " + cn + e);
0221: throw new EJBException("Container failed to find class "
0222: + cn, e);
0223: }
0224:
0225: timerTxAttr = dd.getTimerTxAttribute();
0226: ejbTimeoutSignature = dd.getEjbTimeoutSignature();
0227:
0228: // check TX interop if using iiop.
0229: String protocol = ConfigurationRepository
0230: .getCurrentConfiguration().getProtocol().getName();
0231: if (protocol.equals("iiop")) {
0232: iiopProtocolAvailable = true;
0233: }
0234:
0235: // mkdir a passivation directory if not exist yet.
0236: String pds = cont.getTmpDirName() + File.separator
0237: + dd.getIdentifier();
0238: passivationDir = new File(pds);
0239: if (!passivationDir.isDirectory()) {
0240: passivationDir.mkdir();
0241: TraceEjb.factory.log(BasicLevel.DEBUG, "created: " + pds);
0242: }
0243: }
0244:
0245: /**
0246: * Return the WorkManager
0247: */
0248: public WorkManager getWorkManager() {
0249: return wm;
0250: }
0251:
0252: /**
0253: * Init the pool of instances. Not used for stateful beans
0254: */
0255: public abstract void initInstancePool();
0256:
0257: /**
0258: * Restart Timers
0259: */
0260: public void restartTimers() {
0261: // Look for passivated Timers to reactivate.
0262: String[] timerList = FileUtils.getFileList(passivationDir,
0263: null, ".tim");
0264: for (int i = 0; i < timerList.length; i++) {
0265: File file = new File(passivationDir, timerList[i]);
0266: Timer timer = null;
0267: JObjectInputStream ois = null;
0268: try {
0269: TraceTimer.logger.log(BasicLevel.DEBUG, "restarting: "
0270: + timerList[i]);
0271: ois = new JObjectInputStream(new FileInputStream(file),
0272: cont.getClassLoader());
0273: JTimerHandle th = (JTimerHandle) ois.readObject();
0274: timer = th.restartTimer();
0275: file.delete();
0276: ois.close();
0277: } catch (Exception e) {
0278: // A Timer that cannot be restarted should not prevent the
0279: // Container
0280: // to be started. So, don't throw exception here.
0281: TraceTimer.logger.log(BasicLevel.ERROR,
0282: "Cannot restart timer: " + timerList[i]);
0283: TraceTimer.logger.log(BasicLevel.ERROR, "For bean: "
0284: + passivationDir);
0285: TraceTimer.logger.log(BasicLevel.ERROR, "Exception: "
0286: + e);
0287: if (timer != null) {
0288: try {
0289: timer.cancel();
0290: } catch (Exception ign) {
0291: TraceTimer.logger.log(BasicLevel.ERROR,
0292: "Cancel timer: " + e);
0293: }
0294: }
0295: } finally {
0296: if (ois != null) {
0297: try {
0298: ois.close();
0299: } catch (Exception ig) {
0300: TraceTimer.logger
0301: .log(BasicLevel.WARN,
0302: "Cannot close ObjectInputStream: "
0303: + ig);
0304: }
0305: }
0306: }
0307: }
0308: }
0309:
0310: /**
0311: * Assess availability of a class in a given class loader
0312: * @param className the name of the class without the .class extension
0313: * @param unique flag indicating if the class should be found only once in
0314: * the classloader
0315: * @return True if class is available in the current class loader.
0316: */
0317: protected boolean isClassAvailable(String className, boolean unique) {
0318: className = className.replace('.', '/') + ".class";
0319: Enumeration e = null;
0320: try {
0321: e = ((URLClassLoader) cont.getClassLoader())
0322: .findResources(className);
0323: } catch (IOException e1) {
0324: return false;
0325: }
0326: if (e.hasMoreElements()) {
0327: e.nextElement();
0328: return unique ? !e.hasMoreElements() : true;
0329: } else {
0330: return false;
0331: }
0332: }
0333:
0334: // ---------------------------------------------------------------
0335: // BeanFactory implementation
0336: // ---------------------------------------------------------------
0337:
0338: /**
0339: * @return the bean name
0340: */
0341: public String getEJBName() {
0342: return ejbname;
0343: }
0344:
0345: /**
0346: * @return the Instance pool size for this Ejb
0347: */
0348: public abstract int getPoolSize();
0349:
0350: /**
0351: * Get the Deployement descriptor of this Ejb
0352: * @return BeanDesc The bean deployment descriptor
0353: */
0354: public BeanDesc getDeploymentDescriptor() {
0355: return dd;
0356: }
0357:
0358: /**
0359: * @return the TransactionManager
0360: */
0361: public TransactionManager getTransactionManager() {
0362: return tm;
0363: }
0364:
0365: /**
0366: * @return the JContainer object
0367: */
0368: public JContainer getContainer() {
0369: return cont;
0370: }
0371:
0372: /**
0373: * @return A Hashtable containing the JNDI Environment
0374: */
0375: public Hashtable getEnv() {
0376: return naming.getEnv();
0377: }
0378:
0379: /**
0380: * @return the InitialContext
0381: */
0382: public InitialContext getInitialContext() {
0383: return naming.getInitialContext();
0384: }
0385:
0386: // ---------------------------------------------------------------
0387: // other public methods
0388: // ---------------------------------------------------------------
0389:
0390: /**
0391: * @return the TimerService for this bean.
0392: * This works only for stateless or message driven beans.
0393: */
0394: public abstract TimerService getTimerService();
0395:
0396: /**
0397: * @return the EJB 1.0 style environment associated with the Bean
0398: */
0399: public Properties getEjb10Environment() {
0400: return ejb10Env;
0401: }
0402:
0403: /**
0404: * @return true if transactions are managed inside the bean false if
0405: * transactions are managed by the container
0406: */
0407: public boolean isTxBeanManaged() {
0408: return txbeanmanaged;
0409: }
0410:
0411: /**
0412: * set the Component Context for JNDI environment
0413: * @return previous Context
0414: */
0415: public Context setComponentContext() {
0416: Context oldctx = naming.setComponentContext(JNDICtx);
0417: if (TraceEjb.isDebugLoaderLog() && oldctx == null) {
0418: TraceEjb.loaderlog.log(BasicLevel.DEBUG,
0419: "previous ctx was null");
0420: }
0421: return oldctx;
0422: }
0423:
0424: /**
0425: * reset old Component Context for JNDI environment
0426: * @param oldctx previous Component Context to restore.
0427: */
0428: public void resetComponentContext(Context oldctx) {
0429: if (TraceEjb.isDebugLoaderLog() && oldctx == null) {
0430: TraceEjb.loaderlog.log(BasicLevel.DEBUG, "to null");
0431: }
0432: naming.resetComponentContext(oldctx);
0433: }
0434:
0435: /**
0436: * @return the transaction attribute for ejbTimeout method
0437: */
0438: public int getTimerTxAttribute() {
0439: return timerTxAttr;
0440: }
0441:
0442: /**
0443: * @return the security signature for ejbTimeout method.
0444: */
0445: public String getEjbTimeoutSignature() {
0446: return ejbTimeoutSignature;
0447: }
0448:
0449: /**
0450: * @return min pool size for Jmx
0451: */
0452: public int getMinPoolSize() {
0453: return minPoolSize;
0454: }
0455:
0456: /**
0457: * @return max cache size for Jmx
0458: */
0459: public int getMaxCacheSize() {
0460: return maxCacheSize;
0461: }
0462:
0463: /**
0464: * @return max cache size for Jmx
0465: */
0466: public int getCacheSize() {
0467: throw new IllegalStateException();
0468: }
0469:
0470: /**
0471: * Check if the access to the bean is authorized
0472: * @param ejbInv object containing security signature of the method, args of
0473: * method, etc
0474: */
0475: public void checkSecurity(EJBInvocation ejbInv) {
0476:
0477: String runAsRoleDD = null;
0478:
0479: try {
0480: // security controls (except for Message Driven Beans)
0481: if (ejbInv != null
0482: && ejbInv.methodPermissionSignature != null) {
0483: // Check the security with the identities of the callers
0484: // Establishing a run-as identity for an enterprise bean does
0485: // not affect the
0486: // identities of its callers, which are the identities tested
0487: // for the permission
0488: // to access the methods of the enterprise bean.
0489: // The run-as identity establishes the identity the enterprise
0490: // bean will
0491: // use when it makes calls
0492: // see 21.3.4.1 EJB 2.0 (Security Management / Run-as)
0493: if (ejbInv.methodPermissionSignature.length() != 0) {
0494: cont.checkSecurity(ejbname, ejbInv, (dd
0495: .getRunAsRole() != null));
0496: }
0497: }
0498:
0499: runAsRoleDD = dd.getRunAsRole();
0500: // And now, push the run-as role if any
0501: // This role will be used for the calls of the bean
0502: if (runAsRoleDD != null) {
0503: SecurityCurrent current = SecurityCurrent.getCurrent();
0504: if (current != null) {
0505: SecurityContext sctx = current.getSecurityContext();
0506: if (sctx == null) {
0507: if (TraceEjb.isDebugSecurity()) {
0508: TraceEjb.security.log(BasicLevel.DEBUG,
0509: "runas : Security context is null, create a new one"
0510: + " in ejb " + ejbname);
0511: }
0512: sctx = new SecurityContext();
0513: current.setSecurityContext(sctx);
0514: }
0515: String principalName = dd.getRunAsPrincipalName();
0516: String[] runAsRoles = dd.getDeploymentDesc()
0517: .getRolesForRunAsPrincipal(principalName);
0518: if (runAsRoles == null) {
0519: runAsRoles = new String[] { runAsRoleDD };
0520: }
0521: if (TraceEjb.isDebugSecurity()) {
0522: TraceEjb.security.log(BasicLevel.DEBUG,
0523: "runAs roles are ");
0524: for (int r = 0; r < runAsRoles.length; r++) {
0525: TraceEjb.security.log(BasicLevel.DEBUG,
0526: "Role[" + r + "] = "
0527: + runAsRoles[r]);
0528: }
0529: TraceEjb.security.log(BasicLevel.DEBUG,
0530: "RunAs principal name = "
0531: + principalName);
0532: }
0533: sctx.pushRunAs(runAsRoleDD, principalName,
0534: runAsRoles);
0535: } else {
0536: TraceEjb.security.log(BasicLevel.ERROR,
0537: "Can't push runas role as security current is null"
0538: + " in ejb " + ejbname);
0539: }
0540: }
0541: } catch (RuntimeException re) {
0542: // pop run-as role
0543: if (runAsRoleDD != null) {
0544: SecurityCurrent current = SecurityCurrent.getCurrent();
0545: if (current != null) {
0546: SecurityContext sctx = current.getSecurityContext();
0547: if (sctx == null) {
0548: if (TraceEjb.isDebugSecurity()) {
0549: TraceEjb.security.log(BasicLevel.DEBUG,
0550: "runas : Security context is null "
0551: + " in ejb " + ejbname);
0552: }
0553: } else {
0554: sctx.popRunAs();
0555: }
0556: } else {
0557: if (TraceEjb.isDebugSecurity()) {
0558: TraceEjb.security.log(BasicLevel.DEBUG,
0559: "Can't pop runas role as security current is null"
0560: + " in ejb " + ejbname);
0561: }
0562: }
0563: }
0564: if (TraceEjb.isDebugSecurity()) {
0565: TraceEjb.logger.log(BasicLevel.DEBUG,
0566: "Security Runtime Exception", re);
0567: }
0568: throw re;
0569: }
0570:
0571: }
0572:
0573: /**
0574: * Common preInvoke
0575: * @param txa Transaction Attribute (Supports, Required, ...)
0576: * @return A RequestCtx object
0577: * @throws EJBException
0578: */
0579: public RequestCtx preInvoke(int txa) {
0580:
0581: if (TraceEjb.isDebugIc()) {
0582: TraceEjb.interp.log(BasicLevel.DEBUG, "");
0583: }
0584: RequestCtx rctx = null;
0585:
0586: try {
0587: // Build a RequestCtx to save information about this request
0588: // until the postInvoke time.
0589: rctx = new RequestCtx(txa);
0590:
0591: // Set classLoader to the correct value and save the previous one
0592: // in the RequestCtx to restore it at the end of the Request.
0593: rctx.cloader = Thread.currentThread()
0594: .getContextClassLoader();
0595: Thread.currentThread().setContextClassLoader(
0596: myClassLoader());
0597:
0598: // Set bean context for JNDI and save the previous one in the
0599: // RequestCtx to restore it at the end of the Request.
0600: rctx.jndiCtx = setComponentContext();
0601:
0602: // Check Transaction conformance to bean settings
0603: checkTransaction(rctx);
0604:
0605: } catch (RuntimeException e) {
0606: // Log RuntimeException before rethrowing it
0607: // -> Print Stack Trace to see where the problem is.
0608: TraceEjb.logger.log(BasicLevel.ERROR,
0609: "unexpected Runtime Exception", e);
0610: throw e;
0611: }
0612: return rctx;
0613: }
0614:
0615: /**
0616: * Send commit/abort message
0617: * @param reqId the request id
0618: * @param committed true if the transaction has commited
0619: */
0620: private void replicateCommit(RequestId reqId, boolean committed) {
0621: if (reqId != null) {
0622: try {
0623: JRepUtil.getRepMgr().replicateCommit(reqId, committed);
0624: } catch (Exception e) {
0625: TraceCmi.error("Unable to replicate commit/abor", e);
0626: }
0627: }
0628: }
0629:
0630: /**
0631: * Common postInvoke
0632: * @param rctx The RequestCtx that was returned at preInvoke()
0633: * @throws EJBException
0634: */
0635: public void postInvoke(RequestCtx rctx) {
0636:
0637: if (TraceEjb.isDebugIc()) {
0638: TraceEjb.interp.log(BasicLevel.DEBUG, "");
0639: }
0640:
0641: // WARNING: All exceptions raised here will overload those raised
0642: // in the caller. (postInvoke is usually in a "finally" block)
0643: try {
0644: String runAsRoleDD = dd.getRunAsRole();
0645: // Pop the run-as role if any
0646: if (runAsRoleDD != null) {
0647: // Pop run-as role
0648: SecurityCurrent current = SecurityCurrent.getCurrent();
0649: if (current != null) {
0650: SecurityContext sctx = current.getSecurityContext();
0651: if (sctx == null) {
0652: TraceEjb.security.log(BasicLevel.ERROR,
0653: "runas: Security context is null "
0654: + " in ejb " + ejbname);
0655: } else {
0656: sctx.popRunAs();
0657: }
0658: } else {
0659: TraceEjb.security.log(BasicLevel.ERROR,
0660: "Can't pop runas role as security current is null"
0661: + " in ejb " + ejbname);
0662: }
0663: }
0664:
0665: // Commit the transaction started in preInvoke
0666: if (rctx.mustCommit) {
0667: // Sanity check : transaction should be the same
0668: try {
0669: Transaction t = tm.getTransaction();
0670: if (t == null) {
0671: TraceEjb.logger.log(BasicLevel.ERROR,
0672: "Transaction disappeared: "
0673: + rctx.currTx);
0674: Thread.dumpStack();
0675: throw new EJBException("null transaction");
0676: }
0677: if (rctx.currTx != t) {
0678: TraceEjb.logger.log(BasicLevel.ERROR,
0679: "Transaction changed: " + rctx.currTx);
0680: Thread.dumpStack();
0681: throw new EJBException("bad transaction :" + t);
0682: }
0683: } catch (Exception e) {
0684: throw new EJBException(
0685: "Cannot get current transaction:", e);
0686: }
0687: if (rctx.sysExc != null) {
0688: TraceEjb.logger.log(BasicLevel.ERROR,
0689: "system exception raised by request:",
0690: rctx.sysExc);
0691: // rollback the transaction
0692: try {
0693: tm.rollback();
0694: } catch (Exception e) {
0695: TraceEjb.logger.log(BasicLevel.ERROR,
0696: "exception during rollback:", e);
0697: }
0698: } else {
0699: /*** REPLICATION CODE ***/
0700: // COMPLETE: Send and abort message if the transaction has aborted
0701: RequestId reqId = null;
0702: if (isClusterReplicated) {
0703: HACurrentDelegateImpl current = HACurrentDelegateImpl
0704: .getCurrent();
0705: if (current.getRequests().size() == 1) {
0706: reqId = (RequestId) current.getRequests()
0707: .peek();
0708: }
0709: }
0710:
0711: try {
0712: // Check status to avoid returning RollbackException to
0713: // the client
0714: // instead of the application exception
0715: switch (tm.getStatus()) {
0716: case Status.STATUS_ACTIVE:
0717: /*** REPLICATION CODE ***/
0718: if (reqId != null) {
0719: try {
0720: JRepUtil.getRepMgr().replicate(
0721: reqId);
0722: } catch (Exception e) {
0723: TraceCmi.error(
0724: "Unable to replicate", e);
0725: }
0726: }
0727:
0728: /*** REPLICATION CODE ***/
0729:
0730: if (TraceEjb.isDebugTx()) {
0731: TraceEjb.tx.log(BasicLevel.DEBUG,
0732: "committing transaction: "
0733: + rctx.currTx);
0734: }
0735: tm.commit();
0736: /*** REPLICATION CODE ***/
0737: replicateCommit(reqId, true);
0738: /*** REPLICATION CODE ***/
0739: break;
0740: case Status.STATUS_MARKED_ROLLBACK:
0741: if (TraceEjb.isDebugTx()) {
0742: TraceEjb.tx.log(BasicLevel.DEBUG,
0743: "rolling back transaction: "
0744: + rctx.currTx);
0745: }
0746: tm.rollback();
0747: /*** REPLICATION CODE ***/
0748: replicateCommit(reqId, false);
0749: /*** REPLICATION CODE ***/
0750: break;
0751: default:
0752: TraceEjb.logger.log(BasicLevel.ERROR,
0753: "unexpected transaction status "
0754: + tm.getStatus());
0755: TraceEjb.logger.log(BasicLevel.ERROR,
0756: "transaction: " + rctx.currTx);
0757: Thread.dumpStack();
0758: /*** REPLICATION CODE ***/
0759: replicateCommit(reqId, false);
0760: /*** REPLICATION CODE ***/
0761: throw new EJBException(
0762: "unexpected transaction status :"
0763: + tm.getStatus());
0764: }
0765: } catch (RollbackException e) {
0766: TraceEjb.logger
0767: .log(BasicLevel.WARN,
0768: "Could not commit transaction (rolled back)");
0769: /*** REPLICATION CODE ***/
0770: replicateCommit(reqId, false);
0771: /*** REPLICATION CODE ***/
0772: throw new TransactionRolledbackLocalException(
0773: "Could not commit transaction", e);
0774: } catch (Exception e) {
0775: TraceEjb.logger.log(BasicLevel.WARN,
0776: "Could not commit transaction:" + e);
0777: /*** REPLICATION CODE ***/
0778: replicateCommit(reqId, false);
0779: /*** REPLICATION CODE ***/
0780: throw new EJBException("Container exception", e);
0781: }
0782: }
0783: }
0784:
0785: // Reset class loader to the original value
0786: Thread.currentThread().setContextClassLoader(rctx.cloader);
0787:
0788: // Resume client transaction suspended in preInvoke
0789: Transaction tx = rctx.clientTx;
0790: if (tx != null) {
0791: try {
0792: if (TraceEjb.isDebugTx()) {
0793: TraceEjb.tx.log(BasicLevel.DEBUG,
0794: "resuming transaction");
0795: }
0796: tm.resume(tx);
0797: } catch (Exception e) {
0798: TraceEjb.logger.log(BasicLevel.ERROR,
0799: "cannot resume transaction", e);
0800: }
0801: }
0802:
0803: // Reset bean context for JNDI
0804: resetComponentContext(rctx.jndiCtx);
0805:
0806: // We got a system Exception in business method:
0807: // - log exception
0808: // - discard instance
0809: // - set client transaction rollback only
0810: // - throw EJBException
0811: if (rctx.sysExc != null) {
0812: // Log system exception
0813: TraceEjb.logger.log(BasicLevel.ERROR,
0814: "system exception in business method:",
0815: rctx.sysExc);
0816:
0817: // If client transaction: set it rollback only and throw
0818: // TransactionRolledBackLocalException
0819: Transaction currentTx = null;
0820: try {
0821: currentTx = tm.getTransaction();
0822: // Must not rollback a suspended transaction just resumed (bug #306840)
0823: if (currentTx != null && rctx.clientTx == null) {
0824: TraceEjb.logger.log(BasicLevel.WARN,
0825: "Client transaction will rollback");
0826: currentTx.setRollbackOnly();
0827: // See spec EJB 18.3.1 + 18.4.2.3
0828: if (rctx.bmcalled) {
0829: throw (TransactionRolledbackLocalException) new TransactionRolledbackLocalException(
0830: rctx.sysExc.getMessage())
0831: .initCause(rctx.sysExc);
0832: }
0833: }
0834: } catch (SystemException e) {
0835: TraceEjb.logger.log(BasicLevel.ERROR,
0836: "cannot set rollback only current tx:", e);
0837: }
0838: return;
0839: }
0840:
0841: } catch (TransactionRolledbackLocalException e) {
0842: throw e;
0843: } catch (EJBException e) {
0844: TraceEjb.logger.log(BasicLevel.ERROR, "ejbexception: ", e);
0845: throw e;
0846: } catch (RuntimeException e) {
0847: // Log RuntimeException before rethrowing it
0848: // -> Print Stack Trace to see where the problem is.
0849: TraceEjb.logger.log(BasicLevel.ERROR,
0850: "unexpected runtime exception: ", e);
0851: throw e;
0852: }
0853: }
0854:
0855: /**
0856: * preInvoke for Remote access
0857: * @param txa Transaction Attribute (Supports, Required, ...)
0858: * @return A RequestCtx object
0859: * @throws java.rmi.TransactionRequiredException
0860: * @throws java.rmi.TransactionRolledbackException
0861: * @throws java.rmi.NoSuchObjectException
0862: * @throws RemoteException preinvoke raised an EJBException
0863: */
0864: public RequestCtx preInvokeRemote(int txa) throws RemoteException {
0865: if (TraceEjb.isDebugIc()) {
0866: TraceEjb.interp.log(BasicLevel.DEBUG, "");
0867: }
0868: try {
0869: return preInvoke(txa);
0870: } catch (javax.ejb.TransactionRequiredLocalException e) {
0871: TransactionRequiredException tr = new TransactionRequiredException(
0872: e.getMessage());
0873: tr.detail = e;
0874: throw tr;
0875: } catch (javax.ejb.TransactionRolledbackLocalException e) {
0876: TransactionRolledbackException tr = new TransactionRolledbackException(
0877: e.getMessage());
0878: tr.detail = e;
0879: throw tr;
0880: } catch (javax.ejb.NoSuchObjectLocalException e) {
0881: throw NoSuchObjectExceptionHelper.create(e);
0882: } catch (javax.ejb.EJBException e) {
0883: RemoteException re = new RemoteException(e.getMessage());
0884: re.detail = e;
0885: throw re;
0886: }
0887: }
0888:
0889: /**
0890: * postInvoke for Remote access
0891: * @param rctx The RequestCtx that was returne t preInvoke()
0892: * @throws TransactionRequiredException
0893: * @throws TransactionRolledbackException
0894: * @throws NoSuchObjectException
0895: * @throws RemoteException postinvoke failed
0896: */
0897: public void postInvokeRemote(RequestCtx rctx)
0898: throws RemoteException {
0899: if (TraceEjb.isDebugIc()) {
0900: TraceEjb.interp.log(BasicLevel.DEBUG, "");
0901: }
0902: try {
0903: postInvoke(rctx);
0904: } catch (javax.ejb.TransactionRequiredLocalException e) {
0905: throw new javax.transaction.TransactionRequiredException(e
0906: .getMessage());
0907: } catch (javax.ejb.TransactionRolledbackLocalException e) {
0908: throw new javax.transaction.TransactionRolledbackException(
0909: e.getMessage());
0910: } catch (javax.ejb.NoSuchObjectLocalException e) {
0911: throw new java.rmi.NoSuchObjectException(e.getMessage());
0912: } catch (javax.ejb.EJBException e) {
0913: throw new java.rmi.RemoteException(e.getMessage(), e);
0914: }
0915: }
0916:
0917: /**
0918: * check Transaction attribute
0919: * @param rctx the Request Context
0920: */
0921: abstract void checkTransaction(RequestCtx rctx);
0922:
0923: // ---------------------------------------------------------------
0924: // private methods
0925: // ---------------------------------------------------------------
0926: /**
0927: * Check Transaction Interoperability requirements if the Tx comes
0928: * from another vendor.
0929: * For the moment JOnAS doesn't support tx interop.
0930: * See EJB 2.1 ?19.6.2.2.2
0931: *
0932: * @param txa transaction attribute
0933: *
0934: */
0935: private void checkTransactionInteroperability(int txa) {
0936:
0937: // tm is never null.
0938: TransactionContext transContext = ((org.objectweb.jotm.Current) tm)
0939: .getPropagationContext(false);
0940: if (transContext == null) {
0941: return;
0942: }
0943:
0944: boolean isNullTxCtx = !transContext.isJotmCtx();
0945:
0946: if (isNullTxCtx) {
0947: switch (txa) {
0948: // in the cases below, an exception is returned to the client side in order
0949: // to rollback the tx
0950: case MethodDesc.TX_MANDATORY:
0951: TraceEjb.logger.log(BasicLevel.WARN,
0952: "mandatory and tx from another vendor");
0953: throw new EJBException(
0954: "Doesn't support transaction interoperability");
0955:
0956: case MethodDesc.TX_REQUIRED:
0957: TraceEjb.logger.log(BasicLevel.WARN,
0958: "required and tx from another vendor");
0959: throw new EJBException(
0960: "Doesn't support transaction interoperability");
0961:
0962: case MethodDesc.TX_SUPPORTS:
0963: TraceEjb.logger.log(BasicLevel.WARN,
0964: "supports and tx from another vendor");
0965: throw new EJBException(
0966: "Doesn't support transaction interoperability");
0967: default:
0968: // nothing to do in particular for interop requirement
0969: break;
0970: }
0971: }
0972: }
0973:
0974: /**
0975: * Process Transaction Attribute before calling a business method
0976: * @param rctx the Request Context
0977: * @throws EJBException
0978: * @throws TransactionRequiredLocalException
0979: */
0980: protected void checkTransactionContainer(RequestCtx rctx) {
0981:
0982: int txa = rctx.txAttr;
0983:
0984: if (txa == MethodDesc.TX_NOT_SET) {
0985: // No check to do (for example: session home)
0986: return;
0987: }
0988:
0989: rctx.mustCommit = false;
0990: Transaction cltx = null;
0991:
0992: // First of all, get the current transaction
0993: Transaction currtx = null;
0994: try {
0995: currtx = tm.getTransaction();
0996: } catch (SystemException e) {
0997: TraceEjb.logger.log(BasicLevel.ERROR,
0998: "system exception while getting transaction:", e);
0999: }
1000:
1001: // Check requirements for transaction interoperability (iiop)
1002: if (iiopProtocolAvailable) {
1003: checkTransactionInteroperability(txa);
1004: }
1005:
1006: // Raises exception if "Never" and we are in a transaction
1007: if (txa == MethodDesc.TX_NEVER && currtx != null) {
1008: TraceEjb.logger.log(BasicLevel.WARN,
1009: "never and transaction not null");
1010: throw new EJBException(
1011: "Never attribute = caller must not be in a transaction");
1012: }
1013:
1014: // Raises exception if "Mandatory" and we are not in a transaction
1015: if (txa == MethodDesc.TX_MANDATORY && currtx == null) {
1016: TraceEjb.logger.log(BasicLevel.WARN,
1017: "mandatory and not in transaction");
1018: throw new TransactionRequiredLocalException(
1019: "Mandatory attribute = caller must be in a transaction");
1020: }
1021:
1022: // Suspend transaction if "NotSupported" or "RequiresNew"
1023: if (currtx != null
1024: && (txa == MethodDesc.TX_REQUIRES_NEW || txa == MethodDesc.TX_NOT_SUPPORTED)) {
1025: try {
1026: cltx = tm.suspend();
1027: if (cltx != null && TraceEjb.isDebugTx()) {
1028: TraceEjb.tx.log(BasicLevel.DEBUG,
1029: "Suspending client tx:" + cltx);
1030: }
1031: currtx = null;
1032: } catch (SystemException e) {
1033: TraceEjb.logger.log(BasicLevel.ERROR,
1034: "cannot suspend transaction:\n", e);
1035: throw new EJBException("Cannot suspend transaction", e);
1036: }
1037: }
1038:
1039: // Start a new transaction in 2 cases:
1040: // - "RequiresNew" attribute
1041: // - "Required" and no current transaction
1042: // this transaction will be closed at postInvoke.
1043: if (txa == MethodDesc.TX_REQUIRES_NEW
1044: || (txa == MethodDesc.TX_REQUIRED && currtx == null)) {
1045: try {
1046: tm.begin();
1047: rctx.mustCommit = true;
1048: currtx = tm.getTransaction();
1049: if (TraceEjb.isDebugTx()) {
1050: TraceEjb.tx.log(BasicLevel.DEBUG, "Start tx: "
1051: + currtx);
1052: }
1053: } catch (NotSupportedException e) {
1054: TraceEjb.logger
1055: .log(BasicLevel.ERROR,
1056: "cannot start a transaction: NotSupportedException");
1057: throw new EJBException(
1058: "Nested Transactions Not Supported", e);
1059: } catch (SystemException e) {
1060: TraceEjb.logger.log(BasicLevel.ERROR,
1061: "cannot start a transaction:\n", e);
1062: throw new EJBException(
1063: "Cannot start a transaction: SystemException",
1064: e);
1065: }
1066: }
1067:
1068: // update RequestCtx
1069: rctx.currTx = currtx;
1070: rctx.clientTx = cltx;
1071: }
1072:
1073: /**
1074: * Check if the given class have been generated by GenIC tool with a correct
1075: * version. Trace an error message, if not.
1076: * @param clName class name
1077: */
1078: protected void checkJonasVersion(String clName) {
1079: /*
1080: * Check if the static 'JONAS_VERSION' variable defined in the given
1081: * class (ie GenIC's Version used to deploy the bean), has the same
1082: * value as the JOnAS Version.NUMBER variable.
1083: */
1084: String fdName = "JONAS_VERSION";
1085: String gVersion = null;
1086: try {
1087: // The name of the resource ends with .class and the '.' are replace
1088: // with '/'
1089: String resourceName = clName.replace('.', '/') + ".class";
1090:
1091: // get the list of the resources for this class
1092: Enumeration e = null;
1093: try {
1094: e = cont.getClassLoader().getResources(resourceName);
1095: } catch (IOException ioe) {
1096: TraceEjb.logger.log(BasicLevel.ERROR,
1097: "failed to find class " + clName + ioe);
1098: throw new EJBException(
1099: "Container failed to find class " + clName, ioe);
1100: }
1101:
1102: // Count the resources
1103: int nbCls = 0;
1104: String urls = "";
1105: while (e.hasMoreElements()) {
1106: nbCls++;
1107: urls += e.nextElement() + "\n";
1108: }
1109:
1110: // More than one resource.
1111: if (nbCls > 1) {
1112: TraceEjb.logger
1113: .log(
1114: BasicLevel.WARN,
1115: "there are "
1116: + nbCls
1117: + " resources for the class "
1118: + clName
1119: + ". Some problems can occur because it's the first resource which will be loaded. The list of resources is : \n"
1120: + urls);
1121: }
1122:
1123: // Get the class and get the value of the "JONAS_VERSION" static
1124: // field
1125: Class cl = cont.getClassLoader().loadClass(clName);
1126: Field fd = cl.getDeclaredField("JONAS_VERSION");
1127: gVersion = (String) fd.get(null);
1128: } catch (ClassNotFoundException e) {
1129: TraceEjb.logger.log(BasicLevel.ERROR,
1130: "failed to find class " + clName, e);
1131: return;
1132: } catch (NoSuchFieldException e) {
1133: TraceEjb.logger.log(BasicLevel.ERROR,
1134: "failed to find field " + fdName + " of class "
1135: + clName, e);
1136: return;
1137: } catch (IllegalAccessException e) {
1138: TraceEjb.logger.log(BasicLevel.ERROR,
1139: "failed to get the value of the field " + fdName
1140: + " of class " + clName, e);
1141: return;
1142: }
1143: // Compare the current JOnAS version to the GenIC's Version used to
1144: // deploy the bean
1145: if (!Version.getNumber().equals(gVersion)) {
1146: TraceEjb.logger
1147: .log(
1148: BasicLevel.WARN,
1149: ejbname
1150: + "(Ver. "
1151: + gVersion
1152: + ") bean not deployed with the same JOnAS version("
1153: + Version.getNumber()
1154: + "). You have to redeploy "
1155: + cont.getFileName() + ".");
1156: }
1157: }
1158:
1159: /**
1160: * @return the classloader for this container.
1161: */
1162: public ClassLoader myClassLoader() {
1163: return cont.getClassLoader();
1164: }
1165: }
|