You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by da...@apache.org on 2006/12/01 04:43:00 UTC
svn commit: r481138 - in
/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb:
./ core/ core/mdb/
Author: dain
Date: Thu Nov 30 19:42:58 2006
New Revision: 481138
URL: http://svn.apache.org/viewvc?view=rev&rev=481138
Log:
Finished initial mdb container implementation
Added:
incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContext.java
incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbInstanceFactory.java
Modified:
incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/DeploymentInfo.java
incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/CoreDeploymentInfo.java
incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/EndpointFactory.java
incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/EndpointHandler.java
incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContainer.java
Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/DeploymentInfo.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/DeploymentInfo.java?view=diff&rev=481138&r1=481137&r2=481138
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/DeploymentInfo.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/DeploymentInfo.java Thu Nov 30 19:42:58 2006
@@ -18,6 +18,7 @@
import java.lang.reflect.Method;
import java.util.Collection;
+import java.util.Map;
public interface DeploymentInfo {
@@ -73,7 +74,19 @@
Class getMdbInterface();
+ Map<String, String> getActivationProperties();
+
ClassLoader getClassLoader();
+
+ Method getPostActivate();
+
+ Method getPostConstruct();
+
+ Method getPreDestroy();
+
+ Method getPrePassivate();
+
+ void setContainer(Container container);
public interface BusinessLocalHome extends javax.ejb.EJBLocalHome {
Object create();
Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/CoreDeploymentInfo.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/CoreDeploymentInfo.java?view=diff&rev=481138&r1=481137&r2=481138
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/CoreDeploymentInfo.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/CoreDeploymentInfo.java Thu Nov 30 19:42:58 2006
@@ -106,6 +106,7 @@
private final Map<Method, Method> methodMap = new HashMap<Method, Method>();
private final Map<String, List<String>> securityRoleReferenceMap = new HashMap<String, List<String>>();
private String jarPath;
+ private final Map<String, String> activationProperties = new HashMap<String, String>();
public Class getInterface(InterfaceType interfaceType) {
switch(interfaceType){
@@ -173,10 +174,11 @@
}
}
- public CoreDeploymentInfo(DeploymentContext context, Class beanClass, Class mdbInterface) throws SystemException {
+ public CoreDeploymentInfo(DeploymentContext context, Class beanClass, Class mdbInterface, Map<String, String> activationProperties) throws SystemException {
this.context = context;
this.beanClass = beanClass;
this.mdbInterface = mdbInterface;
+ this.activationProperties.putAll(activationProperties);
this.componentType = BeanType.MESSAGE_DRIVEN;
if (MessageDrivenBean.class.isAssignableFrom(beanClass)){
@@ -197,8 +199,8 @@
this.containerData = containerData;
}
- public void setContainer(Container cont) {
- container = cont;
+ public void setContainer(Container container) {
+ this.container = container;
}
public BeanType getComponentType() {
@@ -305,6 +307,15 @@
public Class getMdbInterface() {
return mdbInterface;
+ }
+
+ public Map<String, String> getActivationProperties() {
+ return activationProperties;
+ }
+
+ public void setActivationProperties(Map<String, String> activationProperties) {
+ this.activationProperties.clear();
+ this.activationProperties.putAll(activationProperties);
}
public Class getPrimaryKeyClass() {
Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/EndpointFactory.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/EndpointFactory.java?view=diff&rev=481138&r1=481137&r2=481138
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/EndpointFactory.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/EndpointFactory.java Thu Nov 30 19:42:58 2006
@@ -20,6 +20,7 @@
import org.apache.openejb.DeploymentInfo;
import javax.resource.spi.UnavailableException;
+import javax.resource.spi.ActivationSpec;
import javax.resource.spi.endpoint.MessageEndpoint;
import javax.resource.spi.endpoint.MessageEndpointFactory;
import javax.transaction.xa.XAResource;
@@ -27,20 +28,28 @@
import java.lang.reflect.Proxy;
public class EndpointFactory implements MessageEndpointFactory {
- private final MdbContainer mdbContainer;
+ private final ActivationSpec activationSpec;
+ private final MdbContainer container;
private final DeploymentInfo deploymentInfo;
+ private final MdbInstanceFactory instanceFactory;
private final ClassLoader classLoader;
private final Class[] interfaces;
- public EndpointFactory(MdbContainer mdbContainer, DeploymentInfo mdbDeploymentInfo) {
- this.mdbContainer = mdbContainer;
- this.deploymentInfo = mdbDeploymentInfo;
- this.classLoader = mdbDeploymentInfo.getClassLoader();
- interfaces = new Class[]{mdbDeploymentInfo.getMdbInterface(), MessageEndpoint.class};
+ public EndpointFactory(ActivationSpec activationSpec, MdbContainer container, DeploymentInfo deploymentInfo, MdbInstanceFactory instanceFactory) {
+ this.activationSpec = activationSpec;
+ this.container = container;
+ this.deploymentInfo = deploymentInfo;
+ this.instanceFactory = instanceFactory;
+ classLoader = deploymentInfo.getClassLoader();
+ interfaces = new Class[]{deploymentInfo.getMdbInterface(), MessageEndpoint.class};
+ }
+
+ public ActivationSpec getActivationSpec() {
+ return activationSpec;
}
public MessageEndpoint createEndpoint(XAResource xaResource) throws UnavailableException {
- EndpointHandler endpointHandler = new EndpointHandler(mdbContainer, deploymentInfo, null, xaResource);
+ EndpointHandler endpointHandler = new EndpointHandler(container, deploymentInfo, instanceFactory, xaResource);
MessageEndpoint messageEndpoint = (MessageEndpoint) Proxy.newProxyInstance(classLoader, interfaces, endpointHandler);
return messageEndpoint;
}
Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/EndpointHandler.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/EndpointHandler.java?view=diff&rev=481138&r1=481137&r2=481138
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/EndpointHandler.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/EndpointHandler.java Thu Nov 30 19:42:58 2006
@@ -18,66 +18,64 @@
package org.apache.openejb.core.mdb;
import org.apache.openejb.DeploymentInfo;
+import org.apache.openejb.SystemException;
+import org.apache.openejb.ApplicationException;
-import javax.ejb.EJBException;
-import javax.resource.ResourceException;
+import javax.resource.spi.UnavailableException;
+import javax.resource.spi.ApplicationServerInternalException;
+import javax.resource.spi.endpoint.MessageEndpoint;
import javax.transaction.xa.XAResource;
+import javax.ejb.EJBException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
-/**
- * Container for the local interface of a Message Driven Bean.
- * This container owns implementations of EJBLocalHome and EJBLocalObject
- * that can be used by a client in the same classloader as the server.
- * <p/>
- * The implementation of the interfaces is generated using cglib FastClass
- * proxies to avoid the overhead of native Java reflection.
- * <p/>
- * <p/>
- * <p/>
- * <p/>
- * The J2EE connector and EJB specifications are not clear on what happens when beforeDelivery or
- * afterDelivery throw an exception, so here is what we have decided:
- * <p/>
- * Exception from beforeDelivery:
- * if container started TX, roll it back
- * reset class loader to adapter classloader
- * reset state to STATE_NONE
- * <p/>
- * Exception from delivery method:
- * if container started TX, roll it back
- * reset class loader to adapter classloader
- * if state was STATE_BEFORE_CALLED, set state to STATE_ERROR so after can still be called
- * <p/>
- * Exception from afterDelivery:
- * if container started TX, roll it back
- * reset class loader to adapter classloader
- * reset state to STATE_NONE
- * <p/>
- * One subtle side effect of this is if the adapter ignores an exception from beforeDelivery and
- * continues with delivery and afterDelivery, the delivery will be treated as a single standalone
- * delivery and the afterDelivery will throw an IllegalStateException.
- *
- * @version $Revision: 451417 $ $Date: 2006-09-29 13:13:22 -0700 (Fri, 29 Sep 2006) $
- */
-public class EndpointHandler implements InvocationHandler {
+public class EndpointHandler implements InvocationHandler, MessageEndpoint {
private static enum State {
- NONE, BEFORE_CALLED, METHOD_CALLED
+ /**
+ * The handler has been initialized and is ready for invoation
+ */
+ NONE,
+
+ /**
+ * The beforeDelivery method has been called, and the next method called must be a message delivery method
+ * or release.
+ */
+ BEFORE_CALLED,
+
+ /**
+ * The message delivery method has been called successfully, and the next method called must be afterDelivery
+ * or release.
+ */
+ METHOD_CALLED,
+
+ /**
+ * The message delivery threw a system exception, and the next method called must be afterDelivery
+ * or release. This state notified the afterDelivery method that the instace must be replaced with a new
+ * instance.
+ */
+ SYSTEM_EXCEPTION,
+
+ /**
+ * This message endpoint handler has been released and can no longer be used.
+ */
+ RELEASED
}
private final MdbContainer container;
private final DeploymentInfo deployment;
- private final Object instance;
+ private final MdbInstanceFactory instanceFactory;
+ private final XAResource xaResource;
- private boolean released = false;
private State state = State.NONE;
- private ClassLoader adapterClassLoader;
+ private Object instance;
- public EndpointHandler(MdbContainer container, DeploymentInfo deployment, Object instance, XAResource xaResource) {
+ public EndpointHandler(MdbContainer container, DeploymentInfo deployment, MdbInstanceFactory instanceFactory, XAResource xaResource) throws UnavailableException {
this.container = container;
this.deployment = deployment;
- this.instance = instance;
+ this.instanceFactory = instanceFactory;
+ this.xaResource = xaResource;
+ instance = instanceFactory.createInstance();
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
@@ -112,29 +110,24 @@
}
- public void beforeDelivery(Method method) throws NoSuchMethodException, ResourceException {
+ public void beforeDelivery(Method method) throws ApplicationServerInternalException {
// verify current state
- if (released) throw new IllegalStateException("Proxy has been released");
switch (state) {
+ case RELEASED:
+ throw new IllegalStateException("Message endpoint factory has been released");
case BEFORE_CALLED:
throw new IllegalStateException("beforeDelivery can not be called again until message is delivered and afterDelivery is called");
case METHOD_CALLED:
+ case SYSTEM_EXCEPTION:
throw new IllegalStateException("The last message delivery must be completed with an afterDeliver before beforeDeliver can be called again");
}
- // call afterDelivery on the container
- installAppClassLoader();
+ // call beforeDelivery on the container
try {
- container.beforeDelivery(deployment.getDeploymentID(), instance, method);
- } catch (NoSuchMethodException e) {
- restoreAdapterClassLoader();
- throw e;
- } catch (ResourceException e) {
- restoreAdapterClassLoader();
- throw e;
- } catch (Throwable throwable) {
- restoreAdapterClassLoader();
- throw new ResourceException(throwable);
+ container.beforeDelivery(deployment.getDeploymentID(), instance, method, xaResource);
+ } catch (SystemException se) {
+ Throwable throwable = (se.getRootCause() != null) ? se.getRootCause() : se;
+ throw new ApplicationServerInternalException(throwable);
}
// before completed successfully we are now ready to invoke bean
@@ -143,60 +136,65 @@
public Object deliverMessage(Method method, Object[] args) throws Throwable {
// verify current state
- if (released) throw new IllegalStateException("Proxy has been released");
switch (state) {
+ case RELEASED:
+ throw new IllegalStateException("Message endpoint factory has been released");
case BEFORE_CALLED:
state = State.METHOD_CALLED;
case METHOD_CALLED:
+ case SYSTEM_EXCEPTION:
throw new IllegalStateException("The last message delivery must be completed with an afterDeliver before another message can be delivered");
}
// if beforeDelivery was not called, call it now
- if (state == State.NONE) {
+ boolean callBeforeAfter = (state == State.NONE);
+ if (callBeforeAfter) {
try {
- container.beforeDelivery(deployment.getDeploymentID(), instance, method);
- } catch (Throwable throwable) {
- if (throwable instanceof EJBException) {
- throw (EJBException) throwable;
- }
- throw (EJBException) new EJBException().initCause(throwable);
+ beforeDelivery(method);
+ } catch (ApplicationServerInternalException e) {
+ throw (EJBException) new EJBException().initCause(e.getCause());
}
}
- boolean exceptionThrown = false;
+ Throwable throwable = null;
+ Object value = null;
try {
- Object value = container.invoke(instance, method, args);
- return value;
- } catch (Throwable throwable) {
- exceptionThrown = true;
- throw throwable;
+ // deliver the message
+ value = container.invoke(instance, method, args);
+ } catch (SystemException se) {
+ throwable = (se.getRootCause() != null) ? se.getRootCause() : se;
+ state = State.SYSTEM_EXCEPTION;
+ } catch (ApplicationException ae) {
+ throwable = (ae.getRootCause() != null) ? ae.getRootCause() : ae;
} finally {
// if the adapter is not using before/after, we must call afterDelivery to clean up
- if (state == State.NONE) {
+ if (callBeforeAfter) {
try {
- container.afterDelivery(instance);
- } catch (Throwable throwable) {
- // if bean threw an exception, do not override that exception
- if (!exceptionThrown) {
- EJBException ejbException;
- if (throwable instanceof EJBException) {
- ejbException = (EJBException) throwable;
- } else {
- ejbException = new EJBException();
- ejbException.initCause(throwable);
- }
- throw ejbException;
- }
+ afterDelivery();
+ } catch (ApplicationServerInternalException e) {
+ throwable = throwable == null ? e.getCause() : throwable;
+ } catch (UnavailableException e) {
+ throwable = throwable == null ? e : throwable;
}
}
}
+
+ if (throwable != null) {
+ if (isValidException(method, throwable)) {
+ throw throwable;
+ } else {
+ throw new EJBException().initCause(throwable);
+ }
+ }
+ return value;
}
- public void afterDelivery() throws ResourceException {
+ public void afterDelivery() throws ApplicationServerInternalException, UnavailableException {
// verify current state
- if (released) throw new IllegalStateException("Proxy has been released");
switch (state) {
+ case RELEASED:
+ throw new IllegalStateException("Message endpoint factory has been released");
case BEFORE_CALLED:
throw new IllegalStateException("Exactally one message must be delivered between beforeDelivery and afterDelivery");
case NONE:
@@ -205,44 +203,60 @@
// call afterDelivery on the container
+ boolean exceptionThrown = false;
try {
container.afterDelivery(instance);
- } catch (ResourceException e) {
- throw e;
- } catch (Throwable throwable) {
- throw new ResourceException(throwable);
+ } catch (SystemException se) {
+ exceptionThrown = true;
+
+ Throwable throwable = (se.getRootCause() != null) ? se.getRootCause() : se;
+ throw new ApplicationServerInternalException(throwable);
} finally {
+ if (state == State.SYSTEM_EXCEPTION) {
+ recreateInstance(exceptionThrown);
+ }
// we are now in the default NONE state
state = State.NONE;
- restoreAdapterClassLoader();
+ }
+ }
+
+ private void recreateInstance(boolean exceptionAlreadyThrown) throws UnavailableException {
+ try {
+ instance = instanceFactory.recreateInstance(instance);
+ } catch (UnavailableException e) {
+ // an error occured wile attempting to create the replacement instance
+ // this endpoint is now failed
+ state = State.RELEASED;
+
+ // if bean threw an exception, do not override that exception
+ if (!exceptionAlreadyThrown) {
+ throw e;
+ }
}
}
public void release() {
- if (released) return;
- released = true;
+ if (state == State.RELEASED) return;
+ state = State.RELEASED;
// notify the container
try {
container.release(instance);
} finally {
- restoreAdapterClassLoader();
+ instanceFactory.freeInstance(instance);
+ instance = null;
}
}
- private void installAppClassLoader() {
- Thread currentThread = Thread.currentThread();
-
- adapterClassLoader = currentThread.getContextClassLoader();
- if (adapterClassLoader != deployment.getClassLoader()) {
- currentThread.setContextClassLoader(deployment.getClassLoader());
- }
- }
+ private boolean isValidException(Method method, Throwable throwable) {
+ if (throwable instanceof RuntimeException || throwable instanceof Error) return true;
- private void restoreAdapterClassLoader() {
- if (adapterClassLoader != deployment.getClassLoader()) {
- Thread.currentThread().setContextClassLoader(adapterClassLoader);
+ Class<?>[] exceptionTypes = method.getExceptionTypes();
+ for (Class<?> exceptionType : exceptionTypes) {
+ if (exceptionType.isInstance(throwable)) {
+ return true;
+ }
}
- adapterClassLoader = null;
+ return false;
}
}
Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContainer.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContainer.java?view=diff&rev=481138&r1=481137&r2=481138
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContainer.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContainer.java Thu Nov 30 19:42:58 2006
@@ -20,17 +20,26 @@
import org.apache.openejb.OpenEJBException;
import org.apache.openejb.DeploymentInfo;
import org.apache.openejb.Container;
+import org.apache.openejb.SystemException;
+import org.apache.openejb.ApplicationException;
+import org.apache.openejb.spi.SecurityService;
import org.apache.openejb.core.ThreadContext;
import org.apache.openejb.core.CoreDeploymentInfo;
import org.apache.openejb.core.transaction.TransactionContext;
import org.apache.openejb.core.transaction.TransactionPolicy;
import org.apache.log4j.Logger;
+import org.apache.xbean.recipe.ObjectRecipe;
import javax.transaction.TransactionManager;
+import javax.transaction.Transaction;
+import javax.transaction.xa.XAResource;
+import javax.resource.spi.ResourceAdapter;
+import javax.resource.spi.ActivationSpec;
+import javax.resource.ResourceException;
import java.lang.reflect.Method;
-import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Map;
+import java.util.Arrays;
public class MdbContainer implements Container {
private static final Logger logger = Logger.getLogger("OpenEJB");
@@ -38,20 +47,27 @@
private final Object containerID;
private final TransactionManager transactionManager;
+ private final SecurityService securityService;
+ private final ResourceAdapter resourceAdapter;
+ private final Class activationSpecClass;
- private final Map<Object, DeploymentInfo> deploymentRegistry = new HashMap<Object, DeploymentInfo>();
+ private final Map<Object, DeploymentInfo> deployments = new HashMap<Object, DeploymentInfo>();
+ private final Map<Object, EndpointFactory> endpointFactories = new HashMap<Object, EndpointFactory>();
- public MdbContainer(Object containerID, TransactionManager transactionManager) {
+ public MdbContainer(Object containerID, TransactionManager transactionManager, SecurityService securityService, ResourceAdapter resourceAdapter, Class activationSpecClass) {
this.containerID = containerID;
this.transactionManager = transactionManager;
+ this.securityService = securityService;
+ this.resourceAdapter = resourceAdapter;
+ this.activationSpecClass = activationSpecClass;
}
public synchronized DeploymentInfo [] deployments() {
- return deploymentRegistry.values().toArray(new DeploymentInfo[deploymentRegistry.size()]);
+ return deployments.values().toArray(new DeploymentInfo[deployments.size()]);
}
public synchronized DeploymentInfo getDeploymentInfo(Object deploymentID) {
- return deploymentRegistry.get(deploymentID);
+ return deployments.get(deploymentID);
}
public int getContainerType() {
@@ -62,49 +78,152 @@
return containerID;
}
- public synchronized void deploy(Object deploymentID, DeploymentInfo info) throws OpenEJBException {
- deploymentRegistry.put(deploymentID, info);
- CoreDeploymentInfo di = (CoreDeploymentInfo) info;
- di.setContainer(this);
+ public void deploy(Object deploymentId, DeploymentInfo deploymentInfo) throws OpenEJBException {
+ // create the activation spec
+ ActivationSpec activationSpec = createActivationSpec(deploymentInfo);
+
+ // create the message endpoint
+ MdbInstanceFactory instanceFactory = new MdbInstanceFactory(deploymentInfo, transactionManager, securityService, 0);
+ EndpointFactory endpointFactory = new EndpointFactory(activationSpec, this, deploymentInfo, instanceFactory);
+
+ // update the data structures
+ // this must be done before activating the endpoint since the ra may immedately begin delivering messages
+ synchronized (this) {
+ deploymentInfo.setContainer(this);
+ deployments.put(deploymentId, deploymentInfo);
+ endpointFactories.put(deploymentId, endpointFactory);
+ }
+
+ // activate the endpoint
+ try {
+ resourceAdapter.endpointActivation(endpointFactory, activationSpec);
+ } catch (ResourceException e) {
+ // activation failed... clean up
+ synchronized (this) {
+ deploymentInfo.setContainer(null);
+ deployments.remove(deploymentId);
+ endpointFactories.remove(deploymentId);
+ }
+
+ throw new OpenEJBException(e);
+ }
+
+ }
+
+ private ActivationSpec createActivationSpec(DeploymentInfo deploymentInfo)throws OpenEJBException {
+ try {
+ // initialize the object recipe
+ ObjectRecipe objectRecipe = new ObjectRecipe(activationSpecClass);
+ Map<String, String> activationProperties = deploymentInfo.getActivationProperties();
+ for (Map.Entry<String, String> entry : activationProperties.entrySet()) {
+ objectRecipe.setMethodProperty(entry.getKey(), entry.getValue());
+ }
+
+ // create the activationSpec
+ ActivationSpec activationSpec = (ActivationSpec) objectRecipe.create(deploymentInfo.getClassLoader());
+
+ // validate the activation spec
+ activationSpec.validate();
+
+ // set the resource adapter into the activation spec
+ activationSpec.setResourceAdapter(resourceAdapter);
+
+ return activationSpec;
+ } catch (Exception e) {
+ throw new OpenEJBException("Unable to create activation spec");
+ }
}
- public synchronized void undeploy(Object deploymentID) throws OpenEJBException {
- CoreDeploymentInfo di = (CoreDeploymentInfo) deploymentRegistry.remove(deploymentID);
- di.setContainer(null);
+ public void undeploy(Object deploymentId) throws OpenEJBException {
+ try {
+ EndpointFactory endpointFactory;
+ synchronized (this) {
+ endpointFactory = endpointFactories.get(deploymentId);
+ }
+ if (endpointFactory != null) {
+ resourceAdapter.endpointDeactivation(endpointFactory, endpointFactory.getActivationSpec());
+ }
+ } finally {
+ synchronized (this) {
+ endpointFactories.remove(deploymentId);
+ DeploymentInfo deploymentInfo = deployments.remove(deploymentId);
+ if (deploymentInfo != null) {
+ deploymentInfo.setContainer(null);
+ }
+ }
+ }
}
- public void beforeDelivery(Object deployId, Object instance, Method method) throws Throwable {
+ public void beforeDelivery(Object deployId, Object instance, Method method, XAResource xaResource) throws SystemException {
// get the target deployment (MDB)
CoreDeploymentInfo deployInfo = (CoreDeploymentInfo) getDeploymentInfo(deployId);
+ if (deployInfo == null) throw new SystemException("Unknown deployment " + deployId);
- // obtain the context objects
+ // intialize call context
ThreadContext callContext = ThreadContext.getThreadContext();
+ callContext.setDeploymentInfo(deployInfo);
MdbCallContext mdbCallContext = new MdbCallContext();
+ callContext.setUnspecified(mdbCallContext);
+ mdbCallContext.deliveryMethod = method;
// create the tx data
mdbCallContext.txPolicy = deployInfo.getTransactionPolicy(method);
mdbCallContext.txContext = new TransactionContext(callContext, transactionManager);
+ // install the application classloader
+ installAppClassLoader(mdbCallContext, deployInfo.getClassLoader());
+
// call the tx before method
- mdbCallContext.txPolicy.beforeInvoke(instance, mdbCallContext.txContext);
+ try {
+ mdbCallContext.txPolicy.beforeInvoke(instance, mdbCallContext.txContext);
+ enlistResource(xaResource);
+ } catch (ApplicationException e) {
+ restoreAdapterClassLoader(mdbCallContext);
+
+ throw new SystemException("Should never get an Application exception", e);
+ } catch (SystemException e) {
+ restoreAdapterClassLoader(mdbCallContext);
+ throw e;
+ }
+ }
- // save the tx data into the thread context
- callContext.setDeploymentInfo(deployInfo);
- callContext.setUnspecified(mdbCallContext);
+ private void enlistResource(XAResource xaResource) throws SystemException {
+ if (xaResource == null) return;
+
+ try {
+ Transaction transaction = transactionManager.getTransaction();
+ if (transaction != null) {
+ transaction.enlistResource(xaResource);
+ }
+ } catch (Exception e) {
+ throw new SystemException("Unable to enlist xa resource in the transaction", e);
+ }
}
- public Object invoke(Object instance, Method method, Object... args) throws Throwable {
+ public Object invoke(Object instance, Method method, Object... args) throws SystemException, ApplicationException {
if (args == null) {
args = NO_ARGS;
}
+ // get the context data
+ ThreadContext callContext = ThreadContext.getThreadContext();
+ CoreDeploymentInfo deployInfo = callContext.getDeploymentInfo();
+ MdbCallContext mdbCallContext = (MdbCallContext) callContext.getUnspecified();
+
+ if (mdbCallContext == null) {
+ throw new IllegalStateException("beforeDelivery was not called");
+ }
+
+ // verify the delivery method passed to beforeDeliver is the same method that was invoked
+ if (!mdbCallContext.deliveryMethod.getName().equals(method.getName()) ||
+ !Arrays.deepEquals(mdbCallContext.deliveryMethod.getParameterTypes(), method.getParameterTypes())) {
+ throw new IllegalStateException("Delivery method specified in beforeDelivery is not the delivery method called");
+ }
+
+ // remember the return value or exception so it can be logged
Object returnValue = null;
- Throwable exception = null;
+ OpenEJBException openEjbException = null;
try {
- // get the context data
- ThreadContext callContext = ThreadContext.getThreadContext();
- CoreDeploymentInfo deployInfo = callContext.getDeploymentInfo();
-
if (logger.isInfoEnabled()) {
logger.info("invoking method " + method.getName() + " on " + deployInfo.getDeploymentID());
}
@@ -112,44 +231,29 @@
// determine the target method on the bean instance class
Method targetMethod = deployInfo.getMatchingBeanMethod(method);
- // ivoke the target method
- returnValue = _invoke(instance, targetMethod, args, (MdbCallContext) callContext.getUnspecified());
+ // invoke the target method
+ returnValue = _invoke(instance, targetMethod, args, mdbCallContext);
return returnValue;
- } catch (org.apache.openejb.ApplicationException ae) {
- // Application exceptions must be reported dirctly to the client. They
- // do not impact the viability of the proxy.
- exception = (ae.getRootCause() != null) ? ae.getRootCause() : ae;
- throw exception;
- } catch (org.apache.openejb.SystemException se) {
- // A system exception would be highly unusual and would indicate a sever
- // problem with the container system.
- exception = (se.getRootCause() != null) ? se.getRootCause() : se;
- logger.error("The container received an unexpected exception: ", exception);
- throw new RemoteException("Container has suffered a SystemException", exception);
- } catch (org.apache.openejb.OpenEJBException oe) {
- // This is a normal container exception thrown while processing the request
- exception = (oe.getRootCause() != null) ? oe.getRootCause() : oe;
- logger.warn("The container received an unexpected exception: ", exception);
- throw new RemoteException("Unknown Container Exception", oe.getRootCause());
+ } catch (ApplicationException e) {
+ openEjbException = e;
+ throw e;
+ } catch (SystemException e) {
+ openEjbException = e;
+ throw e;
} finally {
// Log the invocation results
if (logger.isDebugEnabled()) {
- if (exception == null) {
+ if (openEjbException == null) {
logger.debug("finished invoking method " + method.getName() + ". Return value:" + returnValue);
} else {
- logger.debug("finished invoking method " + method.getName() + " with exception " + exception);
- }
- } else if (logger.isInfoEnabled()) {
- if (exception == null) {
- logger.debug("finished invoking method " + method.getName());
- } else {
+ Throwable exception = (openEjbException.getRootCause() != null) ? openEjbException.getRootCause() : openEjbException;
logger.debug("finished invoking method " + method.getName() + " with exception " + exception);
}
}
}
}
- private Object _invoke(Object instance, Method runMethod, Object [] args, MdbCallContext mdbCallContext) throws OpenEJBException {
+ private Object _invoke(Object instance, Method runMethod, Object [] args, MdbCallContext mdbCallContext) throws SystemException, ApplicationException {
try {
Object returnValue = runMethod.invoke(instance, args);
return returnValue;
@@ -176,14 +280,20 @@
}
- public void afterDelivery(Object instance) throws Throwable {
+ public void afterDelivery(Object instance) throws SystemException {
// get the mdb call context
ThreadContext callContext = ThreadContext.getThreadContext();
MdbCallContext mdbCallContext = (MdbCallContext) callContext.getUnspecified();
ThreadContext.setThreadContext(null);
// invoke the tx after method
- mdbCallContext.txPolicy.afterInvoke(instance, mdbCallContext.txContext);
+ try {
+ mdbCallContext.txPolicy.afterInvoke(instance, mdbCallContext.txContext);
+ } catch (ApplicationException e) {
+ throw new SystemException("Should never get an Application exception", e);
+ } finally {
+ restoreAdapterClassLoader(mdbCallContext);
+ }
}
public void release(Object instance) {
@@ -198,12 +308,30 @@
mdbCallContext.txPolicy.afterInvoke(instance, mdbCallContext.txContext);
} catch (Exception e) {
logger.error("error while releasing message endpoint", e);
+ } finally {
+ restoreAdapterClassLoader(mdbCallContext);
}
}
}
private static class MdbCallContext {
+ private Method deliveryMethod;
+ private ClassLoader adapterClassLoader;
private TransactionPolicy txPolicy;
private TransactionContext txContext;
+ }
+
+ private void installAppClassLoader(MdbCallContext mdbCallContext, ClassLoader applicationClassLoader) {
+ Thread currentThread = Thread.currentThread();
+
+ mdbCallContext.adapterClassLoader = currentThread.getContextClassLoader();
+ if (mdbCallContext.adapterClassLoader != applicationClassLoader) {
+ currentThread.setContextClassLoader(applicationClassLoader);
+ }
+ }
+
+ private void restoreAdapterClassLoader(MdbCallContext mdbCallContext) {
+ Thread.currentThread().setContextClassLoader(mdbCallContext.adapterClassLoader);
+ mdbCallContext.adapterClassLoader = null;
}
}
Added: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContext.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContext.java?view=auto&rev=481138
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContext.java (added)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContext.java Thu Nov 30 19:42:58 2006
@@ -0,0 +1,145 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openejb.core.mdb;
+
+import org.apache.openejb.spi.SecurityService;
+import org.apache.openejb.core.ThreadContext;
+import org.apache.openejb.core.Operations;
+import org.apache.openejb.core.CoreContext;
+import org.apache.openejb.core.stateless.StatelessEjbObjectHandler;
+import org.apache.openejb.core.ivm.EjbObjectProxyHandler;
+import org.apache.openejb.RpcContainer;
+import org.apache.openejb.InterfaceType;
+
+import javax.transaction.TransactionManager;
+import javax.xml.rpc.handler.MessageContext;
+import javax.ejb.EJBHome;
+import javax.ejb.EJBObject;
+import javax.ejb.EJBLocalObject;
+import javax.ejb.EJBLocalHome;
+
+public class MdbContext extends CoreContext implements javax.ejb.MessageDrivenContext {
+ public MdbContext(TransactionManager transactionManager, SecurityService securityService) {
+ super(transactionManager, securityService);
+ }
+
+ public void checkBeanState(byte methodCategory) throws IllegalStateException {
+ /*
+ SECURITY_METHOD:
+ USER_TRANSACTION_METHOD:
+ ROLLBACK_METHOD:
+ EJBOBJECT_METHOD:
+
+ The super class, CoreContext determines if Context.getUserTransaction( ) method
+ maybe called before invoking this.checkBeanState( ). Only "bean managed" transaction
+ beans may access this method.
+
+ */
+ ThreadContext callContext = ThreadContext.getThreadContext();
+
+ switch (callContext.getCurrentOperation()) {
+ case Operations.OP_SET_CONTEXT:
+ /*
+ Allowed Operations:
+ getEJBHome
+ Prohibited Operations:
+ getCallerPrincipal
+ getRollbackOnly,
+ isCallerInRole
+ setRollbackOnly
+ getEJBObject
+ getPrimaryKey
+ getUserTransaction
+ */
+ if (methodCategory != EJBHOME_METHOD)
+ throw new IllegalStateException("Invalid operation attempted");
+ break;
+ case Operations.OP_CREATE:
+ case Operations.OP_REMOVE:
+ /*
+ Allowed Operations:
+ getEJBHome
+ getEJBObject
+ getPrimaryKey
+ getUserTransaction
+ Prohibited Operations:
+ getCallerPrincipal
+ getRollbackOnly,
+ isCallerInRole
+ setRollbackOnly
+ */
+ if (methodCategory == EJBHOME_METHOD
+ || methodCategory == EJBOBJECT_METHOD
+ || methodCategory == USER_TRANSACTION_METHOD)
+ break;
+ else
+ throw new IllegalStateException("Invalid operation attempted");
+ case Operations.OP_BUSINESS:
+ /*
+ Allowed Operations:
+ getEJBHome
+ getEJBObject
+ getPrimaryKey
+ getUserTransaction
+ getCallerPrincipal
+ getRollbackOnly,
+ isCallerInRole
+ setRollbackOnly
+ Prohibited Operations:
+ */
+ break;
+ }
+
+ }
+
+ protected EjbObjectProxyHandler newEjbObjectHandler(RpcContainer container, Object pk, Object depID, InterfaceType interfaceType) {
+ return new StatelessEjbObjectHandler(container, pk, depID, interfaceType);
+ }
+
+ public MessageContext getMessageContext() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ public Object getBusinessObject(Class businessInterface) {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ public Class getInvokedBusinessInterface() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ public Object getPrimaryKey() {
+ throw new UnsupportedOperationException("getPrimaryKey is not supported for a message driven bean");
+ }
+
+ public EJBHome getEJBHome() {
+ throw new UnsupportedOperationException("getEJBHome is not supported for a message driven bean");
+ }
+
+ public EJBObject getEJBObject() {
+ throw new UnsupportedOperationException("getEJBObject is not supported for a message driven bean");
+ }
+
+ public EJBLocalObject getEJBLocalObject() {
+ throw new UnsupportedOperationException("getEJBLocalObject is not supported for a message driven bean");
+ }
+
+ public EJBLocalHome getEJBLocalHome() {
+ throw new UnsupportedOperationException("getEJBLocalHome is not supported for a message driven bean");
+ }
+}
Added: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbInstanceFactory.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbInstanceFactory.java?view=auto&rev=481138
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbInstanceFactory.java (added)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbInstanceFactory.java Thu Nov 30 19:42:58 2006
@@ -0,0 +1,188 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openejb.core.mdb;
+
+import org.apache.log4j.Logger;
+import org.apache.openejb.DeploymentInfo;
+import org.apache.openejb.core.Operations;
+import org.apache.openejb.core.ThreadContext;
+import org.apache.openejb.spi.SecurityService;
+import org.apache.xbean.recipe.ObjectRecipe;
+import org.apache.xbean.recipe.StaticRecipe;
+
+import javax.resource.spi.UnavailableException;
+import javax.transaction.TransactionManager;
+import java.lang.reflect.Method;
+
+/**
+ * A MdbInstanceFactory creates instances of message driven beans for a single instance. This class differs from other
+ * instance managers in OpenEJB as it doesn't do pooling and it creates instances for only a single EJB deployment.
+ * </p>
+ * The MdbContainer assumes that the resouce adapter is pooling message endpoints so a second level of pooling in the
+ * container would be inefficient. This is true of all known resouce adapters in opensource (ActiveMQ), so if this is
+ * a poor assumption for your resource adapter, contact the OpenEJB developers.
+ * </p>
+ * This class can optionally limit the number of bean instances and therefore the message endpoints available to the
+ * resource adapter.
+ */
+public class MdbInstanceFactory {
+ private static final Logger logger = Logger.getLogger("OpenEJB");
+
+ private final DeploymentInfo deploymentInfo;
+ private final TransactionManager transactionManager;
+ private final SecurityService securityService;
+ private final int instanceLimit;
+ private int instanceCount;
+
+ /**
+ * Creates a MdbInstanceFactory for a single specific deployment.
+ * @param deploymentInfo the deployment for which instances will be created
+ * @param transactionManager the transaction manager for this container system
+ * @param securityService the transaction manager for this container system
+ * @param instanceLimit the maximal number of instances or <= 0 if unlimited
+ */
+ public MdbInstanceFactory(DeploymentInfo deploymentInfo, TransactionManager transactionManager, SecurityService securityService, int instanceLimit) {
+ this.deploymentInfo = deploymentInfo;
+ this.transactionManager = transactionManager;
+ this.securityService = securityService;
+ this.instanceLimit = instanceLimit;
+ }
+
+ /**
+ * Gets the maximal number of instances that can exist at any time.
+ * @return the maximum number of instances or <= 0 if unlimitied
+ */
+ public int getInstanceLimit() {
+ return instanceLimit;
+ }
+
+ /**
+ * Gets the current number of created instances.
+ * @return the current number of instances created
+ */
+ public synchronized int getInstanceCount() {
+ return instanceCount;
+ }
+
+ /**
+ * Creates a new mdb instance preforming all necessary lifecycle callbacks
+ * @return a new message driven bean instance
+ * @throws UnavailableException if the instance limit has been exceeded or
+ * if an exception occurs while creating the bean instance
+ */
+ public Object createInstance() throws UnavailableException {
+ synchronized (this) {
+ // check the instance limit
+ if (instanceLimit > 0 && instanceCount >= instanceLimit) {
+ throw new UnavailableException("Only " + instanceLimit + " instances can be created");
+ }
+ // increment the instance count
+ instanceCount++;
+ }
+
+ try {
+ Object bean = constructBean();
+ return bean;
+ } catch (UnavailableException e) {
+ // decrement the instance count
+ synchronized (this) {
+ instanceCount--;
+ }
+
+ throw e;
+ }
+ }
+
+ /**
+ * Frees an instance no longer needed by the resource adapter. This method makes all the necessary lifecycle
+ * callbacks and decrements the instance count. This method should not be used to disposed of beans that have
+ * thrown a system exception. Instead the discardInstance method should be called.
+ * @param bean the bean instance to free
+ */
+ public void freeInstance(Object bean) {
+ if (bean == null) throw new NullPointerException("bean is null");
+
+ // decrement the instance count
+ synchronized (this) {
+ instanceCount--;
+ }
+
+ ThreadContext callContext = ThreadContext.getThreadContext();
+ byte originalOperation = callContext.getCurrentOperation();
+ try {
+ // call post destroy method
+ callContext.setCurrentOperation(Operations.OP_REMOVE);
+ Method preDestroy = callContext.getDeploymentInfo().getPreDestroy();
+ if (preDestroy != null){
+ preDestroy.invoke(bean);
+ }
+ } catch (Throwable re) {
+ MdbInstanceFactory.logger.error("The bean instance " + bean + " threw a system exception:" + re, re);
+ } finally {
+ callContext.setCurrentOperation(originalOperation);
+ }
+ }
+
+ /**
+ * Recreates a bean instance that has thrown a system exception. As required by the EJB specification, lifecycle
+ * callbacks are not invoked. To normally free a bean instance call the freeInstance method.
+ * @param bean the bean instance to discard
+ * @return the new replacement bean instance
+ */
+ public Object recreateInstance(Object bean) throws UnavailableException {
+ if (bean == null) throw new NullPointerException("bean is null");
+ Object newBean = constructBean();
+ return newBean;
+ }
+
+ private Object constructBean() throws UnavailableException {
+ Class beanClass = deploymentInfo.getBeanClass();
+ ObjectRecipe objectRecipe = new ObjectRecipe(beanClass);
+
+ ThreadContext callContext = ThreadContext.getThreadContext();
+ byte originalOperation = callContext.getCurrentOperation();
+ try {
+
+ // construct the bean instance
+ callContext.setCurrentOperation(Operations.OP_SET_CONTEXT);
+ MdbContext mdbContext = new MdbContext(transactionManager, securityService);
+ objectRecipe.setProperty("messageDrivenContext", new StaticRecipe(mdbContext));
+ Object bean = objectRecipe.create();
+
+ // call the post construct method
+ callContext.setCurrentOperation(Operations.OP_CREATE);
+ Method postConstruct = deploymentInfo.getPostConstruct();
+ if (postConstruct != null){
+ postConstruct.invoke(bean);
+ }
+
+
+ return bean;
+ } catch (Throwable e) {
+ if (e instanceof java.lang.reflect.InvocationTargetException) {
+ e = ((java.lang.reflect.InvocationTargetException) e).getTargetException();
+ }
+ String message = "The bean instance threw a system exception:" + e;
+ MdbInstanceFactory.logger.error(message, e);
+ throw new UnavailableException(message, e);
+ } finally {
+ callContext.setCurrentOperation(originalOperation);
+ }
+ }
+
+}