blob: 33ba379d6b6306667ee282f983495e0ee2511205 [file] [log] [blame]
/**
* 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.stateful;
import junit.framework.TestCase;
import org.apache.openejb.OpenEJBException;
import org.apache.openejb.assembler.classic.AppInfo;
import org.apache.openejb.assembler.classic.Assembler;
import org.apache.openejb.assembler.classic.SecurityServiceInfo;
import org.apache.openejb.assembler.classic.TransactionServiceInfo;
import org.apache.openejb.config.AppModule;
import org.apache.openejb.config.ConfigurationFactory;
import org.apache.openejb.config.EjbModule;
import org.apache.openejb.config.PersistenceModule;
import org.apache.openejb.core.ivm.naming.InitContextFactory;
import org.apache.openejb.jee.AssemblyDescriptor;
import org.apache.openejb.jee.EjbJar;
import org.apache.openejb.jee.EjbLocalRef;
import org.apache.openejb.jee.StatefulBean;
import org.apache.openejb.jee.StatelessBean;
import org.apache.openejb.jee.jpa.unit.Persistence;
import org.apache.openejb.jee.jpa.unit.PersistenceUnit;
import org.apache.openjpa.persistence.ArgumentException;
import javax.ejb.EJB;
import javax.ejb.EJBException;
import javax.ejb.Local;
import javax.ejb.NoSuchEJBException;
import javax.ejb.Remove;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.Id;
import javax.persistence.PersistenceContext;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import static javax.persistence.PersistenceContextType.EXTENDED;
import static javax.persistence.PersistenceContextType.TRANSACTION;
public class EntityManagerPropogationTest extends TestCase {
public void test() throws Exception {
_testEMClose();
_testExtended();
_testExtendedRemove();
_testExtendedRandomRemove();
_testNotTooExtended();
_testTransaction();
_testSFTr2SFEx();
_testSFEx2SLTr2SFEx();
_testSLTr2SFEx();
}
private void _testEMClose() throws Exception {
InitialContext ctx = new InitialContext();
PleaseCloseMyExtendedEmBean checkExtendedWorks = (PleaseCloseMyExtendedEmBean) ctx.lookup("PleaseCloseMyExtendedEmLocalBean");
EntityManager savedReference = checkExtendedWorks.getDelegate();
checkExtendedWorks.remove();
assertFalse(savedReference.isOpen());
PleaseCloseMyEmBean please = (PleaseCloseMyEmBean) ctx.lookup("PleaseCloseMyEmLocalBean");
savedReference = please.getDelegate();
please.remove();
assertFalse(savedReference.isOpen());
PleaseCloseMyEmBean statelessIsEasier = (PleaseCloseMyEmBean) ctx.lookup("PleaseCloseMyLessEmLocalBean");
savedReference = statelessIsEasier.getDelegate();
statelessIsEasier.remove();
assertFalse(savedReference.isOpen());
}
public void _testExtended() throws Exception {
InitialContext ctx = new InitialContext();
Node node = (Node) ctx.lookup("ExtendedLocalBean");
// This bean should still be attached
// when the transaction commits
Color attached = node.create(1, "Red");
while (node.getChild() != null) {
assertTrue(node.contains(attached));
node = node.getChild();
}
}
/**
* Test that the extended persistence context can
* survive the removal of the parent
*
* @throws Exception
*/
public void _testExtendedRemove() throws Exception {
InitialContext ctx = new InitialContext();
Node node = (Node) ctx.lookup("ExtendedLocalBean");
// This bean should still be attached
// when the transaction commits
Color attached = node.create(2, "Blue");
while (node.getChild() != null) {
assertTrue(node.contains(attached));
Node next = node.getChild();
node.remove();
try {
node.contains(attached);
fail("The Stateful bean should have been removed");
} catch (NoSuchEJBException e) {
// good
}
node = next;
}
}
public void _testExtendedRandomRemove() throws Exception {
InitialContext ctx = new InitialContext();
int size;
Random rdm = new Random();
for (int l = 0 ; l < 10 ; l++) { // because Romain is not sure of the Random ;-)
Node node = (Node) ctx.lookup("ExtendedLocalBean");
List<Node> nodes = new ArrayList<Node>();
List<EntityManager> delegates = new ArrayList<EntityManager>();
while (node.getChild() != null) {
nodes.add(node);
delegates.add(node.getDelegateEntityManager());
node = node.getChild();
}
// random remove all stateful
do {
size = nodes.size();
int i = rdm.nextInt(size);
Node n = nodes.remove(i);
EntityManager entityManager = delegates.remove(i);
n.remove();
if (--size == 0) {
assertFalse(entityManager.isOpen());
} else {
assertTrue(entityManager.isOpen());
}
} while (size > 0);
}
}
/**
* Test that two Stateful session bean siblings
* do not share the same extended persistence context
*
* A stateful session bean must be a child in order
* for the context to be propogated to that bean.
*
* @throws Exception
*/
public void _testNotTooExtended() throws Exception {
InitialContext ctx = new InitialContext();
// Stateful Node tree A and Node tree B are syblings
Node chainA = (Node) ctx.lookup("ExtendedLocalBean");
Node chainB = (Node) ctx.lookup("ExtendedLocalBean");
// This bean should still be attached
// when the transaction commits
Color attachedA = chainA.create(3, "Green");
while (chainB.getChild() != null) {
assertFalse(chainB.contains(attachedA));
chainB = chainB.getChild();
}
}
public void _testTransaction() throws Exception {
InitialContext ctx = new InitialContext();
Node node = (Node) ctx.lookup("TransactionLocalBean");
// This bean should not still be attached
// when the transaction commits
Color detached = node.create(4, "Yellow");
while (node.getChild() != null) {
assertFalse(node.contains(detached));
node = node.getChild();
}
}
public void _testSFTr2SFEx() throws Exception {
InitialContext ctx = new InitialContext();
Node node = (Node) ctx.lookup("TransactionToExtendedLocalBean");
try {
// System.out.println("SFSB+TPC --> SFSB+EPC");
node.createUntilLeaf(5, "Yellow");
fail("5.6.3.1 Requirements for Persistence Context Propagation (persistence spec)" +
"\n\t--> we cannot have two persistence contexts associated with the transaction");
} catch (EJBException e) {
// OK
// System.out.println(e.getMessage());
}
}
public void _testSFEx2SLTr2SFEx() throws Exception {
InitialContext ctx = new InitialContext();
Node node = (Node) ctx.lookup("ExtendedToTransactionLocalBean");
try {
// System.out.println("SFSB+EPC --> SLSB+TPC --> SFSB+EPC");
node.createUntilLeaf(6, "red");
} catch (EJBException e) {
e.printStackTrace();
fail("5.6.3.1 Requirements for Persistence Context Propagation (persistence spec)" +
"\n\t--> the SFSB+EPC is the one who starts the transaction and then calls the " +
"SLSB+TPC who then calls back to the SFSB+EPC \n\t--> IT SHOULD WORK ...");
}
}
public void _testSLTr2SFEx() throws Exception {
InitialContext ctx = new InitialContext();
Node node = (Node) ctx.lookup("TransactionToExtendedLocalBean");
try {
// System.out.println("SLSB+TPC --> SFSB+EPC");
node.createUntilLeaf(8, "Yellow");
fail("5.6.3.1 Requirements for Persistence Context Propagation (persistence spec)" +
"\n\t--> we cannot have two persistence contexts associated with the transaction");
} catch (EJBException e) {
// OK
// System.out.println(e.getMessage());
}
}
public void setUp() throws OpenEJBException, IOException, NamingException {
System.setProperty(javax.naming.Context.INITIAL_CONTEXT_FACTORY, InitContextFactory.class.getName());
System.setProperty("openejb.embedded", "true");
// Boot up the minimum required OpenEJB components
Assembler assembler = new Assembler();
ConfigurationFactory config = new ConfigurationFactory();
assembler.createTransactionManager(config.configureService(TransactionServiceInfo.class));
assembler.createSecurityService(config.configureService(SecurityServiceInfo.class));
// Start creating the test application
// Create an ejb-jar.xml for this app
EjbJar ejbJar = new EjbJar();
ejbJar.addEnterpriseBean(new StatefulBean("PleaseCloseMyExtendedEm", PleaseCloseMyExtendedEmBean.class));
ejbJar.addEnterpriseBean(new StatefulBean("PleaseCloseMyEm", PleaseCloseMyEmBean.class));
ejbJar.addEnterpriseBean(new StatelessBean("PleaseCloseMyLessEm", PleaseCloseMyEmBean.class));
// Add six beans and link them all in a chain
addStatefulBean(ejbJar, ExtendedContextBean.class, "Extended", "Extendedx2");
addStatefulBean(ejbJar, ExtendedContextBean.class, "Extendedx2", "Extendedx3");
addStatefulBean(ejbJar, ExtendedContextBean.class, "Extendedx3", "Extendedx4");
addStatefulBean(ejbJar, ExtendedContextBean.class, "Extendedx4", "Extendedx5");
addStatefulBean(ejbJar, ExtendedContextBean.class, "ExtendedToTransaction", "StatelessTransactionToExtended");
addStatefulBean(ejbJar, ExtendedContextBean.class, "Extendedx5", "Extendedx6");
ejbJar.addEnterpriseBean(new StatefulBean("Extendedx6", EndNodeBean.class));
// Add six beans and link them all in a chain
addStatefulBean(ejbJar, TransactionContextBean.class, "Transaction", "Transactionx2");
addStatefulBean(ejbJar, TransactionContextBean.class, "Transactionx2", "Transactionx3");
addStatefulBean(ejbJar, TransactionContextBean.class, "Transactionx3", "Transactionx4");
addStatefulBean(ejbJar, TransactionContextBean.class, "Transactionx4", "Transactionx5");
addStatefulBean(ejbJar, TransactionContextBean.class, "Transactionx5", "Transactionx6");
addStatefulBean(ejbJar, TransactionContextBean.class, "TransactionToExtended", "Extendedx5");
addStatelessBean(ejbJar, TransactionContextBean.class, "StatelessTransactionToExtended", "Extendedx5");
ejbJar.addEnterpriseBean(new StatefulBean("Transactionx6", EndNodeBean.class));
ejbJar.setAssemblyDescriptor(new AssemblyDescriptor());
ejbJar.getAssemblyDescriptor().addApplicationException(IllegalArgumentException.class, false, true);
ejbJar.getAssemblyDescriptor().addApplicationException(ArgumentException.class, false, true);
// List<ContainerTransaction> declared = ejbJar.getAssemblyDescriptor().getContainerTransaction();
// declared.add(new ContainerTransaction(TransAttribute.REQUIRED, ExtendedContextBean.class.getName(), "Extendedx5", "*"));
// declared.add(new ContainerTransaction(TransAttribute.REQUIRED, ExtendedContextBean.class.getName(), "TransactionToExtended", "*"));
EjbModule ejbModule = new EjbModule(ejbJar);
// Create an "ear"
AppModule appModule = new AppModule(ejbModule.getClassLoader(), "test-app");
// Add the ejb-jar.xml to the ear
appModule.getEjbModules().add(ejbModule);
// Create a persistence-unit for this app
PersistenceUnit unit = new PersistenceUnit("testUnit");
unit.addClass(Color.class);
unit.setProperty("openjpa.jdbc.SynchronizeMappings", "buildSchema(ForeignKeys=true)");
unit.getProperties().setProperty("openjpa.RuntimeUnenhancedClasses", "supported");
// Add the persistence.xml to the "ear"
appModule.addPersistenceModule(new PersistenceModule("root", new Persistence(unit)));
// Configure and assemble the ear -- aka. deploy it
AppInfo info = config.configureApplication(appModule);
assembler.createApplication(info);
}
private void addStatefulBean(EjbJar ejbJar, Class<?> ejbClass, String name, String reference) {
StatefulBean bean = ejbJar.addEnterpriseBean(new StatefulBean(name, ejbClass));
bean.getEjbLocalRef().add(new EjbLocalRef("child", reference));
}
private void addStatelessBean(EjbJar ejbJar, Class<?> ejbClass, String name, String reference) {
StatelessBean bean = ejbJar.addEnterpriseBean(new StatelessBean(name, ejbClass));
bean.getEjbLocalRef().add(new EjbLocalRef("child", reference));
}
@Entity
public static class Color {
@Id
private int id;
private String name;
public Color() {
}
public Color(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Local
public static interface Node {
void remove();
Color create(int id, String name);
void createUntilLeaf(int id, String name);
boolean contains(Color bean);
Node getChild();
String getDelegateEntityManagerReference();
EntityManager getDelegateEntityManager();
}
public static class PleaseCloseMyExtendedEmBean {
@PersistenceContext(unitName = "testUnit", type = EXTENDED)
protected EntityManager context;
public EntityManager getDelegate() {
return (EntityManager) context.getDelegate();
}
@Remove
public void remove(){}
}
public static class PleaseCloseMyEmBean {
@PersistenceContext(unitName = "testUnit", type = TRANSACTION)
protected EntityManager context;
public EntityManager getDelegate() {
return (EntityManager) context.getDelegate();
}
@Remove
public void remove(){}
}
/**
* The root stateful session bean is essentially the "owner"
* of the persistence context and all children downstream
* with an EXTENDED persistence context should share the same
* persistence context and underlying EntityManager
* <p/>
* Transactions are dissabled to ensure that we aren't relying
* on the JTA propogation that also exists with an EXTENDED
* persistence context
*/
public static class ExtendedContextBean extends NodeBean {
@PersistenceContext(unitName = "testUnit", type = EXTENDED)
protected EntityManager context;
protected EntityManager getEntityManager() {
return context;
}
public EntityManager getDelegateEntityManager() {
return (EntityManager) context.getDelegate();
}
}
public static class TransactionContextBean extends NodeBean {
@PersistenceContext(unitName = "testUnit", type = TRANSACTION)
protected EntityManager context;
protected EntityManager getEntityManager() {
return context;
}
public EntityManager getDelegateEntityManager() {
return (EntityManager) context.getDelegate();
}
}
public static abstract class NodeBean implements Node {
@EJB(name = "child")
protected Node child;
public Color create(int id, String name) {
Color color = new Color(id, name);
getEntityManager().persist(color);
return color;
}
public void createUntilLeaf(int id, String name) {
this.create(id, name);
// recursively call until the leaf is achieved
getChild().createUntilLeaf(id * 10 + 1, name);
}
protected abstract EntityManager getEntityManager();
public Node getChild() {
return child;
}
public String getDelegateEntityManagerReference() {
return getEntityManager().getDelegate().toString();
}
public boolean contains(Color bean) throws IllegalArgumentException {
// This call should not fail as the bean was created
// via the extended persistence context we are now in
return getEntityManager().contains(bean);
}
@Remove
public void remove(){}
}
public static class EndNodeBean implements Node {
public boolean contains(Color color) {
return false;
}
public Color create(int id, String name) {
return null;
}
public void createUntilLeaf(int id, String name) {
return;
}
public Node getChild() {
return null;
}
public String getDelegateEntityManagerReference() {
return null;
}
public EntityManager getDelegateEntityManager() {
return null;
}
@Remove
public void remove(){}
}
}