/* * Copyright 2011 The Apache OpenEJB development community. * * Licensed 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 org.apache.openejb.OpenEJB; 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.LocalInitialContextFactory; import org.apache.openejb.jee.ConcurrentMethod; import org.apache.openejb.jee.EjbJar; import org.apache.openejb.jee.NamedMethod; import org.apache.openejb.jee.StatefulBean; import org.apache.openejb.jee.Timeout; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import javax.ejb.Local; import javax.ejb.Stateful; import javax.naming.InitialContext; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** * Using real non-pooled multithreaded access to test concurrent code execution on a stateful bean. * The test also employs a test for failure. */ public class StatefulConcurrentLookupTest { private static final int THREAD_COUNT = 100; @BeforeClass public static synchronized void beforeClass() throws Exception { System.setProperty(javax.naming.Context.INITIAL_CONTEXT_FACTORY, LocalInitialContextFactory.class.getName()); final ConfigurationFactory config = new ConfigurationFactory(); final Assembler assembler = new Assembler(); assembler.createProxyFactory(config.configureService(ProxyFactoryInfo.class)); assembler.createTransactionManager(config.configureService(TransactionServiceInfo.class)); assembler.createSecurityService(config.configureService(SecurityServiceInfo.class)); final StatefulSessionContainerInfo statefulContainerInfo = config.configureService(StatefulSessionContainerInfo.class); assembler.createContainer(statefulContainerInfo); final EjbJar ejbJar = new EjbJar(); final StatefulBean bean1 = new StatefulBean(MyLocalBeanImpl.class); final Timeout timeout1 = new Timeout(); timeout1.setTimeout(10); timeout1.setUnit(TimeUnit.SECONDS); final ConcurrentMethod method1 = new ConcurrentMethod(); method1.setMethod(new NamedMethod("*")); method1.setAccessTimeout(timeout1); bean1.getConcurrentMethod().add(method1); ejbJar.addEnterpriseBean(bean1); assembler.createApplication(config.configureApplication(ejbJar)); } @AfterClass public static void afterClass() throws Exception { OpenEJB.destroy(); } @Test public void testLookup() throws Exception { runScenario(false); } @Test public void testLookupWithFail() throws Exception { runScenario(true); } private void runScenario(final boolean throwException) throws InterruptedException { final CountDownLatch startingLine = new CountDownLatch(THREAD_COUNT); final CountDownLatch finishingLine = new CountDownLatch(THREAD_COUNT); final List<TestRunnable> runnables = new ArrayList<TestRunnable>(); int i = 0; for (; i < THREAD_COUNT; i++) { final TestRunnable runnable = new TestRunnable("Lookup." + i, throwException, startingLine, finishingLine); runnables.add(runnable); final Thread thread = new Thread(runnable); thread.setDaemon(true); thread.start(); } assertTrue("Threads failed to start", startingLine.await(30, TimeUnit.SECONDS)); assertTrue("Threads failed to finish", finishingLine.await(30, TimeUnit.SECONDS)); for (final TestRunnable runnable : runnables) { if (!throwException == runnable.isSuccess()) { i--; } } assertEquals(THREAD_COUNT, (THREAD_COUNT - i)); } private static class TestRunnable implements Runnable { private final CountDownLatch startingLine; private final CountDownLatch finishingLine; private final String name; private final boolean throwException; private volatile boolean success = false; private TestRunnable(final String name, final boolean throwException, final CountDownLatch startingLine, final CountDownLatch finishingLine) { this.name = name; this.throwException = throwException; this.startingLine = startingLine; this.finishingLine = finishingLine; } @Override public void run() { startingLine.countDown(); try { startingLine.await(); final InitialContext ctx = new InitialContext(); final MyLocalBean bean = (MyLocalBean) ctx.lookup("MyLocalBeanImplLocal"); bean.set(name, throwException); success = name.equals(bean.get()); } catch (final Throwable t) { success = false; } finally { finishingLine.countDown(); } } public boolean isSuccess() { return success; } } @Local public static interface MyLocalBean { void set(final String txt, final boolean throwException); String get(); } @Stateful public static class MyLocalBeanImpl implements MyLocalBean { private String txt = "default"; private boolean throwException = false; @Override public void set(final String txt, final boolean throwException) { this.txt = txt; this.throwException = throwException; } @Override public String get() { if (this.throwException) { throw new UnsupportedOperationException(this.txt + " - This is an expected test Exception"); } return this.txt; } } }