blob: 07737620d794a670422be7da4583a68cb33ba5de [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.singleton;
import junit.framework.TestCase;
import org.apache.openejb.assembler.classic.Assembler;
import org.apache.openejb.assembler.classic.SecurityServiceInfo;
import org.apache.openejb.assembler.classic.SingletonSessionContainerInfo;
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.SingletonBean;
import javax.annotation.PostConstruct;
import javax.ejb.NoSuchEJBException;
import javax.naming.Context;
import javax.naming.InitialContext;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @version $Rev: 894333 $ $Date: 2009-12-29 09:58:53 +0000 (Tue, 29 Dec 2009) $
*/
public class SingletonLazyInstantiationTest extends TestCase {
private static final AtomicInteger accesses = new AtomicInteger();
private static final AtomicBoolean exception = new AtomicBoolean();
@Override
protected void setUp() throws Exception {
exception.set(false);
MySingleton.instances.set(0);
System.setProperty(javax.naming.Context.INITIAL_CONTEXT_FACTORY, InitContextFactory.class.getName());
ConfigurationFactory config = new ConfigurationFactory();
Assembler assembler = new Assembler();
assembler.createTransactionManager(config.configureService(TransactionServiceInfo.class));
assembler.createSecurityService(config.configureService(SecurityServiceInfo.class));
// containers
assembler.createContainer(config.configureService(SingletonSessionContainerInfo.class));
// Setup the descriptor information
EjbJar ejbJar = new EjbJar();
ejbJar.addEnterpriseBean(new SingletonBean(MySingleton.class));
assembler.createApplication(config.configureApplication(ejbJar));
}
public void testSuccess() throws Exception {
Context context = new InitialContext();
int threads = 200;
CyclicBarrier start = new CyclicBarrier(threads + 1);
CountDownLatch finish = new CountDownLatch(threads);
for (int i = threads; i > 0; i--) {
Thread thread = new Thread(new Client(context, start, finish));
thread.setDaemon(true);
thread.start();
}
start.await(30, TimeUnit.SECONDS);
assertFalse("All threads did not start", start.isBroken());
assertTrue("Client threads did not complete", finish.await(30, TimeUnit.SECONDS));
assertEquals("incorrect number of instances", 1, MySingleton.instances.get());
// Invoke a business method just to be sure
MySingletonLocal singletonLocal = (MySingletonLocal) context.lookup("MySingletonLocal");
assertEquals(1, singletonLocal.getId());
}
public void testFailure() throws Throwable {
Exception exception1 = new Exception("Inner exception");
exception1.fillInStackTrace();
exception.set(true);
Context context = new InitialContext();
int threads = 200;
CyclicBarrier start = new CyclicBarrier(threads + 1);
CountDownLatch finish = new CountDownLatch(threads);
for (int i = threads; i > 0; i--) {
Thread thread = new Thread(new Client(context, start, finish));
thread.setDaemon(true);
thread.start();
}
start.await(30, TimeUnit.SECONDS);
assertFalse("All threads did not start", start.isBroken());
assertTrue("Client threads did not complete", finish.await(30, TimeUnit.SECONDS));
assertEquals("incorrect number of instances", 1, MySingleton.instances.get());
// Invoke a business method just to be sure
MySingletonLocal singletonLocal = (MySingletonLocal) context.lookup("MySingletonLocal");
try {
assertEquals(1, singletonLocal.getId());
fail("Expected NoSuchEJBException");
} catch (NoSuchEJBException e) {
// pass
}
}
public static class Client implements Runnable {
private final CyclicBarrier start;
private final CountDownLatch finish;
private final Context context;
public Client(Context context, CyclicBarrier start, CountDownLatch finish) {
this.context = context;
this.start = start;
this.finish = finish;
}
public void run() {
try {
log("waiting to start");
if (start != null) start.await(20, TimeUnit.SECONDS);
log("looking up the singleton");
MySingletonLocal singletonLocal = (MySingletonLocal) context.lookup("MySingletonLocal");
// Have to invoke a method to ensure creation
singletonLocal.getId();
log("singleton retrieved " + singletonLocal);
} catch (NoSuchEJBException e) {
if (!exception.get()) {
synchronized (System.out) {
log("exception");
e.printStackTrace(System.out);
}
throw new RuntimeException(e);
}
} catch (Exception e) {
synchronized (System.out) {
log("exception");
e.printStackTrace(System.out);
}
throw new RuntimeException(e);
} finally {
log("finished");
finish.countDown();
}
}
}
public static void log(String s) {
// System.out.println(Thread.currentThread().getName() + " : " + s);
}
public static interface MySingletonLocal {
public int getId();
}
public static class MySingleton implements MySingletonLocal {
public static final AtomicInteger instances = new AtomicInteger();
private int id;
@PostConstruct
public void construct() throws Exception {
id = instances.incrementAndGet();
log("constructing singleton: " + id);
Thread.sleep(5000);
if (exception.get()) throw new Exception("I threw an exception");
}
public int getId() {
return id;
}
}
}