Modified: openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml?rev=688602&r1=688601&r2=688602&view=diff ============================================================================== --- openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml (original) +++ openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml Sun Aug 24 19:39:06 2008 @@ -61,7 +61,7 @@ id="Default BMP Container" service="Container" types="BMP_ENTITY" - constructor="id, transactionManager, securityService, PoolSize" + constructor="id, securityService, PoolSize" class-name="org.apache.openejb.core.entity.EntityContainer"> # Specifies the size of the bean pools for this @@ -82,7 +82,7 @@ id="Default Stateless Container" service="Container" types="STATELESS" - constructor="id, transactionManager, securityService, TimeOut, PoolSize, StrictPooling" + constructor="id, securityService, TimeOut, PoolSize, StrictPooling" class-name="org.apache.openejb.core.stateless.StatelessContainer"> # Specifies the time to wait between invocations. This @@ -124,7 +124,7 @@ id="Default Singleton Container" service="Container" types="SINGLETON" - constructor="id, transactionManager, securityService" + constructor="id, securityService" class-name="org.apache.openejb.core.singleton.SingletonContainer"> AccessTimeout = 30 seconds @@ -141,7 +141,7 @@ id="Default Stateful Container" service="Container" types="STATEFUL" - constructor="id, transactionManager, securityService, Passivator, TimeOut, PoolSize, BulkPassivate" + constructor="id, securityService, Passivator, TimeOut, PoolSize, BulkPassivate" class-name="org.apache.openejb.core.stateful.StatefulContainer"> # The passivator is responsible for writing beans to disk @@ -186,7 +186,7 @@ id="Default MDB Container" service="Container" types="MESSAGE" - constructor="id, transactionManager, securityService, ResourceAdapter, MessageListenerInterface, ActivationSpecClass, InstanceLimit" + constructor="id, securityService, ResourceAdapter, MessageListenerInterface, ActivationSpecClass, InstanceLimit" class-name="org.apache.openejb.core.mdb.MdbContainer"> # The resource adapter delivers messages to the container
Modified: openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulContainerTest.java URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulContainerTest.java?rev=688602&r1=688601&r2=688602&view=diff ============================================================================== --- openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulContainerTest.java (original) +++ openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulContainerTest.java Sun Aug 24 19:39:06 2008 @@ -17,17 +17,11 @@ */ package org.apache.openejb.core.stateful; -import junit.framework.TestCase; -import org.apache.openejb.assembler.classic.Assembler; -import org.apache.openejb.assembler.classic.ProxyFactoryInfo; -import org.apache.openejb.assembler.classic.SecurityServiceInfo; -import org.apache.openejb.assembler.classic.StatefulSessionContainerInfo; -import org.apache.openejb.assembler.classic.TransactionServiceInfo; -import org.apache.openejb.config.ConfigurationFactory; -import org.apache.openejb.core.ivm.naming.InitContextFactory; -import org.apache.openejb.jee.EjbJar; -import org.apache.openejb.jee.StatefulBean; - +import java.sql.SQLException; +import java.util.Arrays; +import java.util.List; +import java.util.Stack; +import java.util.ArrayList; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.annotation.Resource; @@ -38,18 +32,42 @@ import javax.ejb.Remove; import javax.ejb.SessionContext; import javax.naming.InitialContext; -import java.io.Serializable; -import java.util.Arrays; -import java.util.List; -import java.util.Stack; -import java.util.ArrayList; +import javax.transaction.TransactionManager; + +import junit.framework.TestCase; +import org.apache.openejb.assembler.classic.Assembler; +import org.apache.openejb.assembler.classic.ProxyFactoryInfo; +import org.apache.openejb.assembler.classic.SecurityServiceInfo; +import org.apache.openejb.assembler.classic.StatefulSessionContainerInfo; +import org.apache.openejb.assembler.classic.TransactionServiceInfo; +import org.apache.openejb.config.ConfigurationFactory; +import org.apache.openejb.core.ivm.naming.InitContextFactory; +import org.apache.openejb.jee.EjbJar; +import org.apache.openejb.jee.StatefulBean; +import org.apache.openejb.loader.SystemInstance; /** * @version $Revision$ $Date$ */ public class StatefulContainerTest extends TestCase { + private List<Lifecycle> inTxExpectedLifecycle; + private List expectedLifecycle; public void testBusinessLocalInterface() throws Exception { + testBusinessLocalInterface(expectedLifecycle); + } + + public void testBusinessRemoteInterfaceInTx() throws Exception { + TransactionManager transactionManager = SystemInstance.get().getComponent(TransactionManager.class); + transactionManager.begin(); + try { + testBusinessRemoteInterface(inTxExpectedLifecycle); + } finally { + transactionManager.commit(); + } + } + + protected void testBusinessLocalInterface(List expectedLifecycle) throws Exception { // Do a create... @@ -61,19 +79,42 @@ Widget widget = (Widget) object; // Do a business method... - Stack<Lifecycle> lifecycle = widget.getLifecycle(); - assertNotNull("lifecycle",lifecycle); + Stack<Object> actual = widget.getLifecycle(); + assertNotNull("lifecycle", actual); + + // test app exception + try { + widget.throwAppException(); + fail("Expected application exception"); + } catch (SQLException e) { + assertEquals("test", e.getMessage()); + } + + // Do another business method... + widget.afterAppException(); // Do a remove... widget.destroy(); // Check the lifecycle of the bean - List expected = Arrays.asList(StatefulContainerTest.Lifecycle.values()); - - assertEquals(StatefulContainerTest.join("\n", expected) , join("\n", WidgetBean.lifecycle)); + assertEquals(StatefulContainerTest.join("\n", expectedLifecycle) , join("\n", WidgetBean.lifecycle)); } public void testBusinessRemoteInterface() throws Exception { + testBusinessRemoteInterface(expectedLifecycle); + } + + public void testBusinessLocalInterfaceInTx() throws Exception { + TransactionManager transactionManager = SystemInstance.get().getComponent(TransactionManager.class); + transactionManager.begin(); + try { + testBusinessLocalInterface(inTxExpectedLifecycle); + } finally { + transactionManager.commit(); + } + } + + protected void testBusinessRemoteInterface(List expectedLifecycle) throws Exception { WidgetBean.lifecycle.clear(); // Do a create... @@ -86,10 +127,21 @@ RemoteWidget widget = (RemoteWidget) object; // Do a business method... - Stack<Lifecycle> lifecycle = widget.getLifecycle(); + Stack<Object> lifecycle = widget.getLifecycle(); assertNotNull("lifecycle",lifecycle); assertNotSame("is copy", lifecycle, WidgetBean.lifecycle); + // test app exception + try { + widget.throwAppException(); + fail("Expected application exception"); + } catch (SQLException e) { + assertEquals("test", e.getMessage()); + } + + // Do another business method... + widget.afterAppException(); + // Do a remove... widget.destroy(); @@ -100,9 +152,7 @@ } // Check the lifecycle of the bean - List expected = Arrays.asList(StatefulContainerTest.Lifecycle.values()); - - assertEquals(StatefulContainerTest.join("\n", expected) , join("\n", WidgetBean.lifecycle)); + assertEquals(StatefulContainerTest.join("\n", expectedLifecycle) , join("\n", WidgetBean.lifecycle)); } protected void setUp() throws Exception { @@ -132,6 +182,14 @@ WidgetBean.lifecycle.clear(); + expectedLifecycle = Arrays.asList(Lifecycle.values()); + inTxExpectedLifecycle = new ArrayList<Lifecycle>(); + for (Lifecycle lifecycle : Lifecycle.values()) { + if (!lifecycle.name().startsWith("PRE_PASSIVATE") && + !lifecycle.name().startsWith("POST_ACTIVATE")) { + inTxExpectedLifecycle.add(lifecycle); + } + } } private static String join(String delimeter, List items){ @@ -144,7 +202,9 @@ @Local public static interface Widget { - Stack<Lifecycle> getLifecycle(); + Stack<Object> getLifecycle(); + void throwAppException() throws SQLException; + void afterAppException(); void destroy(); } @@ -154,39 +214,63 @@ } public static enum Lifecycle { - CONSTRUCTOR, INJECTION, POST_CONSTRUCT, PRE_PASSIVATE1, POST_ACTIVATE1, BUSINESS_METHOD, PRE_PASSIVATE2, POST_ACTIVATE2,REMOVE, PRE_DESTROY, + // construction + CONSTRUCTOR, INJECTION, POST_CONSTRUCT, PRE_PASSIVATE1, + // business method + POST_ACTIVATE1, BUSINESS_METHOD, PRE_PASSIVATE2, + // throw app exception + POST_ACTIVATE2, PRE_PASSIVATE3, + // business method after app exception + POST_ACTIVATE3, PRE_PASSIVATE4, + // remove + POST_ACTIVATE4, REMOVE, PRE_DESTROY, } public static class WidgetBean implements Widget, RemoteWidget { - private static final long serialVersionUID = -8499745487520955081L; - private int activates = 0; private int passivates = 0; - public static Stack<Lifecycle> lifecycle = new Stack<Lifecycle>(); + public static Stack<Object> lifecycle = new Stack<Object>(); public WidgetBean() { lifecycle.push(Lifecycle.CONSTRUCTOR); } + public void throwAppException() throws SQLException { + throw new SQLException("test"); + } + + public void afterAppException() { + } + @Resource public void setContext(SessionContext context){ lifecycle.push(Lifecycle.INJECTION); } - public Stack<Lifecycle> getLifecycle() { + public Stack<Object> getLifecycle() { lifecycle.push(Lifecycle.BUSINESS_METHOD); return lifecycle; } @PostActivate public void activate(){ - lifecycle.push(Enum.valueOf(Lifecycle.class, "POST_ACTIVATE" + (++activates))); + String name = "POST_ACTIVATE" + (++activates); + try { + lifecycle.push(Enum.valueOf(Lifecycle.class, name)); + } catch (Exception e) { + lifecycle.push(name); + } } @PrePassivate public void passivate(){ - lifecycle.push(Enum.valueOf(Lifecycle.class, "PRE_PASSIVATE" + (++passivates))); + String name = "PRE_PASSIVATE" + (++passivates); + try { + lifecycle.push(Enum.valueOf(Lifecycle.class, name)); + } catch (Exception e) { + lifecycle.push(name); + } } @PostConstruct Modified: openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulSecurityPermissionsTest.java URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulSecurityPermissionsTest.java?rev=688602&r1=688601&r2=688602&view=diff ============================================================================== --- openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulSecurityPermissionsTest.java (original) +++ openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulSecurityPermissionsTest.java Sun Aug 24 19:39:06 2008 @@ -16,63 +16,54 @@ */ package org.apache.openejb.core.stateful; +import java.rmi.RemoteException; +import java.security.Permission; +import java.security.Permissions; +import java.security.Principal; +import java.security.ProtectionDomain; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import javax.annotation.security.DenyAll; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; +import javax.ejb.CreateException; +import javax.ejb.EJBAccessException; +import javax.ejb.EJBHome; +import javax.ejb.EJBLocalHome; +import javax.ejb.EJBLocalObject; +import javax.ejb.EJBObject; +import javax.ejb.Init; +import javax.ejb.Local; +import javax.ejb.LocalHome; +import javax.ejb.Remote; +import javax.ejb.RemoteHome; +import javax.ejb.Remove; +import javax.naming.InitialContext; +import javax.security.auth.Subject; +import javax.security.auth.login.LoginException; + import junit.framework.TestCase; -import org.apache.openejb.core.ivm.naming.InitContextFactory; -import org.apache.openejb.core.ThreadContext; -import org.apache.openejb.core.security.jacc.BasicJaccProvider; -import org.apache.openejb.core.security.jacc.BasicPolicyConfiguration; -import org.apache.openejb.core.security.AbstractSecurityService; -import org.apache.openejb.core.security.jaas.UserPrincipal; -import org.apache.openejb.core.security.jaas.GroupPrincipal; -import org.apache.openejb.core.transaction.TransactionPolicy; import org.apache.openejb.assembler.classic.Assembler; +import org.apache.openejb.assembler.classic.EjbJarInfo; import org.apache.openejb.assembler.classic.ProxyFactoryInfo; -import org.apache.openejb.assembler.classic.TransactionServiceInfo; import org.apache.openejb.assembler.classic.SecurityServiceInfo; -import org.apache.openejb.assembler.classic.EjbJarInfo; +import org.apache.openejb.assembler.classic.TransactionServiceInfo; import org.apache.openejb.config.ConfigurationFactory; +import org.apache.openejb.core.ivm.naming.InitContextFactory; +import org.apache.openejb.core.security.AbstractSecurityService; +import org.apache.openejb.core.security.jaas.GroupPrincipal; +import org.apache.openejb.core.security.jaas.UserPrincipal; +import org.apache.openejb.core.security.jacc.BasicJaccProvider; +import org.apache.openejb.core.security.jacc.BasicPolicyConfiguration; import org.apache.openejb.jee.EjbJar; -import org.apache.openejb.jee.StatefulBean; -import org.apache.openejb.jee.ContainerTransaction; -import org.apache.openejb.jee.TransAttribute; -import org.apache.openejb.jee.MethodIntf; import org.apache.openejb.jee.MethodPermission; +import org.apache.openejb.jee.StatefulBean; import org.apache.openejb.loader.SystemInstance; import org.apache.openejb.spi.SecurityService; -import javax.naming.InitialContext; -import javax.ejb.LocalHome; -import javax.ejb.RemoteHome; -import javax.ejb.TransactionAttribute; -import javax.ejb.TransactionAttributeType; -import javax.ejb.Init; -import javax.ejb.Remove; -import javax.ejb.Local; -import javax.ejb.Remote; -import javax.ejb.EJBHome; -import javax.ejb.CreateException; -import javax.ejb.EJBObject; -import javax.ejb.EJBLocalHome; -import javax.ejb.EJBLocalObject; -import javax.ejb.EJBAccessException; -import javax.annotation.security.RolesAllowed; -import javax.annotation.security.PermitAll; -import javax.annotation.security.DenyAll; -import javax.security.auth.login.LoginException; -import javax.security.auth.Subject; -import javax.security.jacc.PolicyContextException; -import java.util.List; -import java.util.Set; -import java.util.UUID; -import java.util.HashSet; -import java.util.Collections; -import java.rmi.RemoteException; -import java.security.ProtectionDomain; -import java.security.Permission; -import java.security.Principal; -import java.security.Permissions; -import java.security.PermissionCollection; - /** * @version $Rev$ $Date$ */ Modified: openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulTransactionAttributesTest.java URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulTransactionAttributesTest.java?rev=688602&r1=688601&r2=688602&view=diff ============================================================================== --- openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulTransactionAttributesTest.java (original) +++ openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulTransactionAttributesTest.java Sun Aug 24 19:39:06 2008 @@ -19,17 +19,14 @@ import junit.framework.TestCase; import org.apache.openejb.assembler.classic.Assembler; import org.apache.openejb.assembler.classic.EjbJarInfo; -import org.apache.openejb.assembler.classic.MethodAttributeInfo; import org.apache.openejb.assembler.classic.ProxyFactoryInfo; import org.apache.openejb.assembler.classic.SecurityServiceInfo; import org.apache.openejb.assembler.classic.TransactionServiceInfo; import org.apache.openejb.config.ConfigurationFactory; import org.apache.openejb.core.ThreadContext; -import org.apache.openejb.core.transaction.TransactionPolicy; import org.apache.openejb.core.ivm.naming.InitContextFactory; import org.apache.openejb.jee.ContainerTransaction; import org.apache.openejb.jee.EjbJar; -import org.apache.openejb.jee.StatelessBean; import org.apache.openejb.jee.TransAttribute; import org.apache.openejb.jee.MethodIntf; import org.apache.openejb.jee.StatefulBean; @@ -48,10 +45,8 @@ import javax.ejb.Init; import javax.ejb.Remove; import javax.naming.InitialContext; -import java.lang.reflect.Method; import java.rmi.RemoteException; import java.util.List; -import java.util.Map; /** * @version $Rev$ $Date$ @@ -326,7 +321,7 @@ public String attribute() { ThreadContext context = ThreadContext.getThreadContext(); - return context.get(TransactionPolicy.Type.class).toString(); + return context.getTransactionPolicy().toString(); } @Init Added: openejb/trunk/openejb3/container/openejb-spring/src/main/java/org/apache/openejb/spring/SpringBeanTransactionPolicy.java URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-spring/src/main/java/org/apache/openejb/spring/SpringBeanTransactionPolicy.java?rev=688602&view=auto ============================================================================== --- openejb/trunk/openejb3/container/openejb-spring/src/main/java/org/apache/openejb/spring/SpringBeanTransactionPolicy.java (added) +++ openejb/trunk/openejb3/container/openejb-spring/src/main/java/org/apache/openejb/spring/SpringBeanTransactionPolicy.java Sun Aug 24 19:39:06 2008 @@ -0,0 +1,245 @@ +/** + * + * 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.spring; + +import javax.transaction.HeuristicMixedException; +import javax.transaction.HeuristicRollbackException; +import javax.transaction.NotSupportedException; +import javax.transaction.RollbackException; +import javax.transaction.UserTransaction; +import static javax.transaction.Status.STATUS_NO_TRANSACTION; +import static javax.transaction.Status.STATUS_MARKED_ROLLBACK; +import static javax.transaction.Status.STATUS_ACTIVE; + +import org.apache.openejb.SystemException; +import org.apache.openejb.core.transaction.BeanTransactionPolicy; +import org.apache.openejb.core.transaction.TransactionType; +import org.springframework.transaction.HeuristicCompletionException; +import org.springframework.transaction.NestedTransactionNotSupportedException; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionException; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.UnexpectedRollbackException; +import static org.springframework.transaction.TransactionDefinition.PROPAGATION_REQUIRES_NEW; +import org.springframework.transaction.support.DefaultTransactionDefinition; +import org.springframework.transaction.support.DefaultTransactionStatus; + +public class SpringBeanTransactionPolicy extends SpringTransactionPolicy implements BeanTransactionPolicy { + private final UserTransaction userTransaction; + private DefaultTransactionStatus beanTransaction; + private int timeout; + + public SpringBeanTransactionPolicy(PlatformTransactionManager transactionManager) { + super(transactionManager, TransactionType.BeanManaged); + userTransaction = new SpringUserTransaction(); + } + + protected DefaultTransactionStatus getTransactionStatus() { + return beanTransaction != null ? beanTransaction : super.getTransactionStatus(); + } + + public SuspendedTransaction suspendUserTransaction() throws SystemException { + throw new SystemException(new UnsupportedOperationException("SpringTransactionPolicy does not support transaction suspension")); + } + + public void resumeUserTransaction(SuspendedTransaction suspendedTransaction) throws SystemException { + throw new SystemException(new UnsupportedOperationException("SpringTransactionPolicy does not support transaction resumption")); + } + + public UserTransaction getUserTransaction() { + if (getTransactionStatus().isCompleted()) { + throw new IllegalStateException("SpringBeanTransactionPolicy transaction has been completed"); + } + return userTransaction; + } + + private class SpringUserTransaction implements UserTransaction { + public int getStatus() { + if (getTransactionStatus().isCompleted() || beanTransaction == null) { + return STATUS_NO_TRANSACTION; + } else if (isRollbackOnly()) { + return STATUS_MARKED_ROLLBACK; + } else { + return STATUS_ACTIVE; + } + } + + public void begin() throws NotSupportedException, javax.transaction.SystemException { + if (getTransactionStatus().isCompleted()) { + throw new IllegalStateException("SpringBeanTransactionPolicy transaction has been completed"); + } + + if (beanTransaction != null) { + // we could support nested transactions + throw new NotSupportedException("Current thread is already associated with a transaction"); + } + + try { + // create transaction definition + DefaultTransactionDefinition definition = new DefaultTransactionDefinition(); + definition.setPropagationBehavior(PROPAGATION_REQUIRES_NEW); + if (timeout > 0) { + definition.setTimeout(timeout); + } + + // start the transaction + TransactionStatus transactionStatus = transactionManager.getTransaction(definition); + + // TransactionStatus must be a DefaultTransactionStatus so we can implement isTransactionActive() + if (!(transactionManager instanceof DefaultTransactionStatus)) { + transactionManager.rollback(transactionStatus); + throw new IllegalArgumentException("SpringBeanTransactionPolicy only works with a PlatformTransactionManager that uses DefaultTransactionStatus"); + } + beanTransaction = (DefaultTransactionStatus) transactionStatus; + } catch (TransactionException e) { + // check if exception is simply wrapping a JTA exception + Throwable cause = e.getCause(); + if (cause instanceof NotSupportedException) { + throw (NotSupportedException) cause; + } else if (cause instanceof javax.transaction.SystemException) { + throw (javax.transaction.SystemException) cause; + } + + // convert to JTA exception + if (e instanceof NestedTransactionNotSupportedException) { + throw createJtaException(NotSupportedException.class, e); + } + throw createJtaException(javax.transaction.SystemException.class, e); + } + } + + public void commit() throws HeuristicMixedException, HeuristicRollbackException, IllegalStateException, RollbackException, SecurityException, javax.transaction.SystemException { + if (getTransactionStatus().isCompleted()) { + throw new IllegalStateException("SpringBeanTransactionPolicy transaction has been completed"); + } + + if (beanTransaction == null) { + throw new IllegalStateException("Current thread is not associated with a transaction"); + } + + try { + transactionManager.commit(beanTransaction); + } catch (TransactionException e) { + // check if exception is simply wrapping a JTA exception + Throwable cause = e.getCause(); + if (cause instanceof HeuristicMixedException) { + throw (HeuristicMixedException) cause; + } else if (cause instanceof HeuristicRollbackException) { + throw (HeuristicRollbackException) cause; + } else if (cause instanceof IllegalStateException) { + throw (IllegalStateException) cause; + } else if (cause instanceof RollbackException) { + throw (RollbackException) cause; + } else if (cause instanceof SecurityException) { + throw (SecurityException) cause; + } else if (cause instanceof javax.transaction.SystemException) { + throw (javax.transaction.SystemException) cause; + } + + // convert to JTA exception + if (e instanceof HeuristicCompletionException) { + HeuristicCompletionException heuristicCompletionException = (HeuristicCompletionException) e; + if (heuristicCompletionException.getOutcomeState() == HeuristicCompletionException.STATE_MIXED) { + throw createJtaException(HeuristicMixedException.class, e); + } else if (heuristicCompletionException.getOutcomeState() == HeuristicCompletionException.STATE_ROLLED_BACK) { + throw createJtaException(HeuristicRollbackException.class, e); + } + } else if (e instanceof UnexpectedRollbackException) { + throw createJtaException(RollbackException.class, e); + } + throw createJtaException(javax.transaction.SystemException.class, e); + } + } + + public void rollback() throws IllegalStateException, javax.transaction.SystemException { + if (getTransactionStatus().isCompleted()) { + throw new IllegalStateException("SpringBeanTransactionPolicy transaction has been completed"); + } + + if (beanTransaction == null) { + throw new IllegalStateException("Current thread is not associated with a transaction"); + } + + try { + transactionManager.rollback(beanTransaction); + } catch (TransactionException e) { + // check if exception is simply wrapping a JTA exception + Throwable cause = e.getCause(); + if (cause instanceof IllegalStateException) { + throw (IllegalStateException) cause; + } else if (cause instanceof SecurityException) { + throw (SecurityException) cause; + } else if (cause instanceof javax.transaction.SystemException) { + throw (javax.transaction.SystemException) cause; + } + + // convert to JTA exception + throw createJtaException(javax.transaction.SystemException.class, e); + } + } + + public void setRollbackOnly() throws IllegalStateException, javax.transaction.SystemException { + if (getTransactionStatus().isCompleted()) { + throw new IllegalStateException("SpringBeanTransactionPolicy transaction has been completed"); + } + + if (beanTransaction == null) { + throw new IllegalStateException("Current thread is not associated with a transaction"); + } + + try { + beanTransaction.setRollbackOnly(); + } catch (Exception e) { + // check if exception is simply wrapping a JTA exception + Throwable cause = e.getCause(); + if (cause instanceof IllegalStateException) { + throw (IllegalStateException) cause; + } else if (cause instanceof javax.transaction.SystemException) { + throw (javax.transaction.SystemException) cause; + } + + // convert to JTA exception + throw createJtaException(javax.transaction.SystemException.class, e); + } + } + + public void setTransactionTimeout(int timeout) throws javax.transaction.SystemException { + if (getTransactionStatus().isCompleted()) { + throw new IllegalStateException("SpringBeanTransactionPolicy transaction has been completed"); + } + + if (timeout < 0) { + throw new javax.transaction.SystemException("timeout is negative"); + } + SpringBeanTransactionPolicy.this.timeout = timeout; + } + + private <T extends Exception> T createJtaException(Class<T> type, Throwable cause) throws javax.transaction.SystemException { + T exception; + try { + exception = type.getConstructor(String.class).newInstance("Spring PlatformTransactionManager threw an exception"); + } catch (Exception e) { + javax.transaction.SystemException systemException = new javax.transaction.SystemException("Spring PlatformTransactionManager threw an exception"); + systemException.initCause(cause); + throw systemException; + } + exception.initCause(cause); + return exception; + } + } +} Added: openejb/trunk/openejb3/container/openejb-spring/src/main/java/org/apache/openejb/spring/SpringTransactionPolicy.java URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-spring/src/main/java/org/apache/openejb/spring/SpringTransactionPolicy.java?rev=688602&view=auto ============================================================================== --- openejb/trunk/openejb3/container/openejb-spring/src/main/java/org/apache/openejb/spring/SpringTransactionPolicy.java (added) +++ openejb/trunk/openejb3/container/openejb-spring/src/main/java/org/apache/openejb/spring/SpringTransactionPolicy.java Sun Aug 24 19:39:06 2008 @@ -0,0 +1,172 @@ +/** + * + * 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.spring; + +import javax.transaction.RollbackException; +import javax.transaction.HeuristicMixedException; +import javax.transaction.HeuristicRollbackException; +import javax.transaction.xa.XAResource; + +import org.apache.openejb.core.transaction.TransactionPolicy; +import org.apache.openejb.core.transaction.TransactionType; +import org.apache.openejb.core.transaction.TransactionPolicy.TransactionSynchronization.Status; +import org.apache.openejb.ApplicationException; +import org.apache.openejb.SystemException; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.TransactionException; +import org.springframework.transaction.HeuristicCompletionException; +import org.springframework.transaction.UnexpectedRollbackException; +import org.springframework.transaction.support.DefaultTransactionStatus; +import org.springframework.transaction.support.DefaultTransactionDefinition; +import org.springframework.transaction.support.TransactionSynchronizationManager; +import org.springframework.transaction.support.TransactionSynchronizationAdapter; + +public class SpringTransactionPolicy implements TransactionPolicy { + protected final PlatformTransactionManager transactionManager; + protected final TransactionType type; + private final DefaultTransactionStatus transactionStatus; + + public SpringTransactionPolicy(PlatformTransactionManager transactionManager, TransactionType type) { + this.transactionManager = transactionManager; + this.type = type; + + DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition(); + switch (type) { + case BeanManaged: + transactionDefinition.setPropagationBehavior(org.springframework.transaction.TransactionDefinition.PROPAGATION_NOT_SUPPORTED); + break; + case Mandatory: + transactionDefinition.setPropagationBehavior(org.springframework.transaction.TransactionDefinition.PROPAGATION_MANDATORY); + break; + case Never: + transactionDefinition.setPropagationBehavior(org.springframework.transaction.TransactionDefinition.PROPAGATION_NEVER); + break; + case NotSupported: + transactionDefinition.setPropagationBehavior(org.springframework.transaction.TransactionDefinition.PROPAGATION_NOT_SUPPORTED); + break; + case Required: + transactionDefinition.setPropagationBehavior(org.springframework.transaction.TransactionDefinition.PROPAGATION_REQUIRED); + break; + case RequiresNew: + transactionDefinition.setPropagationBehavior(org.springframework.transaction.TransactionDefinition.PROPAGATION_REQUIRES_NEW); + break; + case Supports: + transactionDefinition.setPropagationBehavior(org.springframework.transaction.TransactionDefinition.PROPAGATION_SUPPORTS); + break; + } + + TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition); + if (!(transactionManager instanceof DefaultTransactionStatus)) { + throw new IllegalArgumentException("SpringTransactionPolicy only works with a PlatformTransactionManager that uses DefaultTransactionStatus"); + } + this.transactionStatus = (DefaultTransactionStatus) transactionStatus; + } + + public TransactionType getTransactionType() { + return type; + } + + public boolean isNewTransaction() { + return getTransactionStatus().isNewTransaction(); + } + + public boolean isTransactionActive() { + return !getTransactionStatus().isCompleted() && getTransactionStatus().hasTransaction(); + } + + public boolean isRollbackOnly() { + return getTransactionStatus().isRollbackOnly(); + } + + public void setRollbackOnly() { + getTransactionStatus().setRollbackOnly(); + } + + public void commit() throws ApplicationException, SystemException { + try { + transactionManager.commit(getTransactionStatus()); + } catch (TransactionException e) { + // check if exception is simply wrapping a supported JTA exception + Throwable cause = e.getCause(); + if (cause instanceof RollbackException) { + throw new ApplicationException(cause); + } else if (cause instanceof HeuristicMixedException) { + throw new ApplicationException(cause); + } else if (cause instanceof HeuristicRollbackException) { + throw new ApplicationException(cause); + } else if (cause instanceof IllegalStateException) { + throw new SystemException(cause); + } else if (cause instanceof SecurityException) { + throw new SystemException(cause); + } else if (cause instanceof javax.transaction.SystemException) { + throw new SystemException(cause); + } + + // wrap with application or system exception based on type + if (e instanceof HeuristicCompletionException) { + throw new ApplicationException(e); + } else if (e instanceof UnexpectedRollbackException) { + throw new ApplicationException(e); + } + throw new SystemException(e); + } + } + + public Object getResource(Object key) { + Object resource = TransactionSynchronizationManager.getResource(key); + return resource; + } + + public void putResource(Object key, Object value) { + TransactionSynchronizationManager.bindResource(key, value); + } + + public Object removeResource(Object key) { + Object resource = TransactionSynchronizationManager.unbindResource(key); + return resource; + } + + public void registerSynchronization(final TransactionSynchronization synchronization) { + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { + public void beforeCompletion() { + synchronization.beforeCompletion(); + } + + public void afterCompletion(int status) { + Status s = null; + if (status == STATUS_COMMITTED) { + s = Status.COMMITTED; + } else if (status == STATUS_ROLLED_BACK) { + s = Status.ROLLEDBACK; + } else if (status == STATUS_UNKNOWN) { + s = Status.UNKNOWN; + } + synchronization.afterCompletion(s); + } + }); + } + + public void enlistResource(XAResource xaResource) throws SystemException { + throw new SystemException(new UnsupportedOperationException("SpringTransactionPolicy does not support XAResource enlistment")); + } + + protected DefaultTransactionStatus getTransactionStatus() { + return transactionStatus; + } +} Added: openejb/trunk/openejb3/container/openejb-spring/src/main/java/org/apache/openejb/spring/SpringTransactionPolicyFactory.java URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-spring/src/main/java/org/apache/openejb/spring/SpringTransactionPolicyFactory.java?rev=688602&view=auto ============================================================================== --- openejb/trunk/openejb3/container/openejb-spring/src/main/java/org/apache/openejb/spring/SpringTransactionPolicyFactory.java (added) +++ openejb/trunk/openejb3/container/openejb-spring/src/main/java/org/apache/openejb/spring/SpringTransactionPolicyFactory.java Sun Aug 24 19:39:06 2008 @@ -0,0 +1,44 @@ +/** + * + * 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.spring; + +import org.apache.openejb.core.transaction.TransactionPolicyFactory; +import org.apache.openejb.core.transaction.TransactionPolicy; +import org.apache.openejb.core.transaction.TransactionType; +import org.apache.openejb.SystemException; +import org.apache.openejb.ApplicationException; +import org.springframework.transaction.PlatformTransactionManager; + +public class SpringTransactionPolicyFactory implements TransactionPolicyFactory { + private final PlatformTransactionManager transactionManager; + + public SpringTransactionPolicyFactory(PlatformTransactionManager transactionManager) { + if (transactionManager == null) throw new NullPointerException("transactionManager is null"); + this.transactionManager = transactionManager; + } + + public TransactionPolicy createTransactionPolicy(TransactionType type) throws SystemException, ApplicationException { + SpringTransactionPolicy policy; + if (type == TransactionType.BeanManaged) { + policy = new SpringBeanTransactionPolicy(transactionManager); + } else { + policy = new SpringTransactionPolicy(transactionManager, type); + } + return policy; + } +}
