Modified: openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/PoolTest.java URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/PoolTest.java?rev=924925&r1=924924&r2=924925&view=diff ============================================================================== --- openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/PoolTest.java (original) +++ openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/PoolTest.java Thu Mar 18 18:09:27 2010 @@ -18,15 +18,13 @@ package org.apache.openejb.util; import junit.framework.TestCase; -import javax.ejb.Remote; -import javax.ejb.Stateless; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import static java.util.concurrent.TimeUnit.MILLISECONDS; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; /** @@ -160,8 +158,8 @@ public class PoolTest extends TestCase { final CountDownLatch finishingLine = new CountDownLatch(threadCount); // Do a business method... - Runnable r = new Runnable(){ - public void run(){ + Runnable r = new Runnable() { + public void run() { startingLine.countDown(); try { startPistol.await(); @@ -210,17 +208,656 @@ public class PoolTest extends TestCase { } - private void checkMax(int max, List<Pool.Entry<String>> entries) { + /** + * Tests the idle timeout as well as the Thread pool + * used to invoke the discard/create jobs. + * + * + * @throws Exception exception + */ + public void testIdleTimeout() throws Exception { + + final int min = 4; + final int max = 9; + final int idleTimeout = 500; + + final CountDownLatch discarded = new CountDownLatch(max - min); + final CountDownLatch hold = new CountDownLatch(1); + + final Pool.Builder builder = new Pool.Builder(); + builder.setPoolMin(min); + builder.setPoolMax(max); + builder.setIdleTimeout(new Duration(idleTimeout, TimeUnit.MILLISECONDS)); + builder.setPollInterval(new Duration(idleTimeout / 2, TimeUnit.MILLISECONDS)); + builder.setSupplier(new Pool.Supplier() { + public void discard(Object o) { + discarded.countDown(); + try { + // Executor should have enough threads + // to execute removes on all the + // timed out objects. + hold.await(); + } catch (InterruptedException e) { + Thread.interrupted(); + } + } + + public Object create() { + // Should never be called + return new CounterBean(); + } + }); + + + final Pool pool = builder.build(); + + // Fill pool to max + CounterBean.instances.set(0); + for (int i = 0; i < max; i++) { + assertTrue(pool.add(new CounterBean())); + } + + + { // Should have a full, non-null pool + final List entries = drain(pool, 100); + checkMin(min, entries); + checkEntries(max, entries); + push(pool, entries); + } + + + discarded.await(); +// assertTrue(discarded.await(idleTimeout * 2, TimeUnit.MILLISECONDS)); + + { // Pool should only have min number of non-null entries + final List entries = drain(pool, 100); + checkMin(min, entries); + checkEntries(min, entries); + push(pool, entries); + } + + // -- DONE -- + + assertEquals(max, CounterBean.instances.get()); + } + + public void testFlush() throws Exception { + + final int min = 4; + final int max = 9; + final int poll = 200; + + final CountDownLatch discarded = new CountDownLatch(max); + final CountDownLatch created = new CountDownLatch(min); + final CountDownLatch createInstances = new CountDownLatch(1); + + final Pool.Builder builder = new Pool.Builder(); + builder.setPoolMin(min); + builder.setPoolMax(max); + builder.setPollInterval(new Duration(poll, TimeUnit.MILLISECONDS)); + builder.setSupplier(new Pool.Supplier() { + public void discard(Object o) { + discarded.countDown(); + } + + public Object create() { + try { + createInstances.await(); + } catch (InterruptedException e) { + Thread.interrupted(); + } + try { + return new CounterBean(); + } finally { + created.countDown(); + } + } + }); + + + final Pool pool = builder.build(); + + // Fill pool to max + CounterBean.instances.set(0); + for (int i = 0; i < max; i++) { + assertTrue(pool.add(new CounterBean())); + } + + + { // Should have a full, non-null pool + final List entries = drain(pool, 100); + checkMin(min, entries); + checkEntries(max, entries); + push(pool, entries); + } + + pool.flush(); + + // Wait for the Evictor to come around and sweep out the pool + assertTrue(discarded.await(poll * 10, TimeUnit.MILLISECONDS)); + + // Minimum instances are still being created + // The rest of the pool should be empty (null) + { + final List entries = drain(pool, 100); + // Should have "non-min" number of entries + checkMax(max - min, entries); + + // Nothing should be a "min" item as those + // are still being created + checkMin(0, entries); + + // And as the pool was just drained all the + // entries we get should be null + checkNull(entries); + + push(pool, entries); + } + + CounterBean.instances.set(0); + + // Try and trick the pool into adding more "min" items + // Fill the pool as much as we can -- should only let us + // fill to the max factoring in the "min" instances that + // are currently being created + { + final List entries = drain(pool, 100); + + // Should reject the instance we try to add + + assertFalse(pool.add(new CounterBean())); + + + // Empty the pool + discard(pool, entries); + + + // Now count how many instances it lets us add + CounterBean.instances.set(0); + while (pool.add(new CounterBean())) ; + + // As the "min" instances are still being created + // it should only let us fill max - min, then + 1 + // to account for the instance that gets rejected + // and terminates the while loop + final int expected = max - min + 1; + + assertEquals(expected, CounterBean.instances.getAndSet(0)); + } + + // Ok, let the "min" instance creation threads continue + createInstances.countDown(); + + // Wait for the "min" instance creation to complete + assertTrue(created.await(poll * 10, TimeUnit.MILLISECONDS)); + + { // Pool should be full again + final List entries = drain(pool, 100); + checkMin(min, entries); + checkEntries(max, entries); + push(pool, entries); + } + + // -- DONE -- + } + + public void testMaxAge() throws Exception { + System.out.println("PoolTest.testMaxAge"); + final int min = 4; + final int max = 9; + final int maxAge = 500; + final int poll = maxAge / 2; + + final CountDownLatch discarded = new CountDownLatch(max); + final CountDownLatch created = new CountDownLatch(min); + final CountDownLatch createInstances = new CountDownLatch(1); + + final Pool.Builder builder = new Pool.Builder(); + builder.setPoolMin(min); + builder.setPoolMax(max); + builder.setMaxAge(new Duration(maxAge, MILLISECONDS)); + builder.setPollInterval(new Duration(poll, MILLISECONDS)); + builder.setSupplier(new Pool.Supplier<CounterBean>() { + public void discard(CounterBean o) { + countDown(discarded, o); + } + + public CounterBean create() { + try { + createInstances.await(); + } catch (InterruptedException e) { + Thread.interrupted(); + } + try { + return new CounterBean(); + } finally { + created.countDown(); + } + } + }); + + + final Pool pool = builder.build(); + + // Fill pool to max + CounterBean.instances.set(0); + for (int i = 0; i < max; i++) { + assertTrue(pool.add(new CounterBean())); + } + + + { // Should have a full, non-null pool + final List entries = drain(pool, 100); + checkMin(min, entries); + checkEntries(max, entries); + push(pool, entries); + } + + // Now wait for the instances in the pool to expire + assertTrue(discarded.await(maxAge * 4, TimeUnit.MILLISECONDS)); + + // Minimum instances are still being created + // The rest of the pool should be empty (null) + { + final List entries = drain(pool, 100); + + // Nothing should be a "min" item as those + // are still being created + checkMin(0, entries); + + // And as the pool was just drained all the + // entries we get should be null + checkNull(entries); + + // Should have "non-min" number of entries + checkMax(max - min, entries); + + push(pool, entries); + } + + CounterBean.instances.set(0); + + // Try and trick the pool into adding more "min" items + // Fill the pool as much as we can -- should only let us + // fill to the max factoring in the "min" instances that + // are currently being created + { + final List entries = drain(pool, 100); + + // Should reject the instance we try to add + + assertFalse(pool.add(new CounterBean())); + + + // Empty the pool + discard(pool, entries); + + + // Now count how many instances it lets us add + CounterBean.instances.set(0); + while (pool.add(new CounterBean())) ; + + // As the "min" instances are still being created + // it should only let us fill max - min, then + 1 + // to account for the instance that gets rejected + // and terminates the while loop + final int expected = max - min + 1; + + assertEquals(expected, CounterBean.instances.getAndSet(0)); + } + + // Ok, let the "min" instance creation threads continue + createInstances.countDown(); + + // Wait for the "min" instance creation to complete + assertTrue(created.await(poll * 10, TimeUnit.MILLISECONDS)); + + { // Pool should be full again + final List entries = drain(pool, 100); + checkMin(min, entries); + checkEntries(max, entries); + push(pool, entries); + } + + // -- DONE -- + } + + private void countDown(CountDownLatch discarded, CounterBean o) { + discarded.countDown(); + System.out.format("%1$tH:%1$tM:%1$tS.%1$tL %2$s\n", System.currentTimeMillis(), o.count()); +// try { +// Thread.sleep(50); +// } catch (InterruptedException e) { +// Thread.interrupted(); +// } + } + + /** + * What happens if we fail to create a "min" instance after a flush? + * + * @throws Exception exception + */ + public void testFlushFailedCreation() throws Exception { + + final int min = 4; + final int max = 9; + final int poll = 200; + + final CountDownLatch discarded = new CountDownLatch(max); + final CountDownLatch created = new CountDownLatch(min); + final CountDownLatch createInstances = new CountDownLatch(1); + + final Pool.Builder builder = new Pool.Builder(); + builder.setPoolMin(min); + builder.setPoolMax(max); + builder.setPollInterval(new Duration(poll, TimeUnit.MILLISECONDS)); + builder.setSupplier(new Pool.Supplier() { + public void discard(Object o) { + discarded.countDown(); + } + + public Object create() { + try { + createInstances.await(); + } catch (InterruptedException e) { + Thread.interrupted(); + } + try { + throw new RuntimeException(); + } finally { + created.countDown(); + } + } + }); + + + final Pool pool = builder.build(); + + // Fill pool to max + CounterBean.instances.set(0); + for (int i = 0; i < max; i++) { + assertTrue(pool.add(new CounterBean())); + } + + + { // Should have a full, non-null pool + final List entries = drain(pool, 100); + checkMin(min, entries); + checkEntries(max, entries); + push(pool, entries); + } + + pool.flush(); + + // Wait for the Evictor to come around and sweep out the pool + assertTrue(discarded.await(poll * 10, TimeUnit.MILLISECONDS)); + + // Minimum instances are still being created + // The rest of the pool should be empty (null) + { + final List entries = drain(pool, 100); + // Should have "non-min" number of entries + checkMax(max - min, entries); + + // Nothing should be a "min" item as those + // are still being created + checkMin(0, entries); + + // And as the pool was just drained all the + // entries we get should be null + checkNull(entries); + + push(pool, entries); + } + + CounterBean.instances.set(0); + + // Try and trick the pool into adding more "min" items + // Fill the pool as much as we can -- should only let us + // fill to the max factoring in the "min" instances that + // are currently being created + { + final List entries = drain(pool, 100); + + // Should reject the instance we try to add + + assertFalse(pool.add(new CounterBean())); + + + // Empty the pool + discard(pool, entries); + + + // Now count how many instances it lets us add + CounterBean.instances.set(0); + while (pool.add(new CounterBean())) ; + + // As the "min" instances are still being created + // it should only let us fill max - min, then + 1 + // to account for the instance that gets rejected + // and terminates the while loop + final int expected = max - min + 1; + + assertEquals(expected, CounterBean.instances.getAndSet(0)); + } + + // Ok, let the "min" instance creation threads continue + createInstances.countDown(); + + // Wait for the "min" instance creation to complete + assertTrue(created.await(poll * 10, TimeUnit.MILLISECONDS)); + + { // Pool should be full but... + final List entries = drain(pool, 100); + // we failed to create the min instances + checkMin(0, entries); + + // the "min" entries should have been freed up + // and we should have all the possible entires + checkMax(max, entries); + + // though there should be "min" quantities of nulls + checkEntries(max-min, entries); + + // Now when we push these back in, the right number + // of entries should be converted to "min" entries + push(pool, entries); + + } + + { // Pool should be full but... + final List entries = drain(pool, 100); + + // now we should have the right number of mins + checkMin(min, entries); + + // should still have a full pool + checkMax(max, entries); + + // though there should still be "min" quantities of nulls + // as we still haven't created any more instances, we just + // converted some of our instances into "min" entries + checkEntries(max-min, entries); + + } + + // -- DONE -- + } + + /** + * What happens if we fail to create a "min" instance after a maxAge expiration? + * + * @throws Exception exception + */ + public void testMaxAgeFailedCreation() throws Exception { + System.out.println("PoolTest.testMaxAgeFailedCreation"); + final int min = 4; + final int max = 9; + final int maxAge = 1000; + final int poll = 100; + + final CountDownLatch discarded = new CountDownLatch(max); + final CountDownLatch created = new CountDownLatch(min); + final CountDownLatch createInstances = new CountDownLatch(1); + + final Pool.Builder builder = new Pool.Builder(); + builder.setPoolMin(min); + builder.setPoolMax(max); + builder.setMaxAge(new Duration(maxAge, MILLISECONDS)); + builder.setPollInterval(new Duration(poll, MILLISECONDS)); + builder.setSupplier(new Pool.Supplier<CounterBean>() { + public void discard(CounterBean o) { + countDown(discarded, o); + } + + public CounterBean create() { + try { + createInstances.await(); + } catch (InterruptedException e) { + Thread.interrupted(); + } + try { + throw new RuntimeException(); + } finally { + created.countDown(); + } + } + }); + + + final Pool pool = builder.build(); + + // Fill pool to max + CounterBean.instances.set(0); + for (int i = 0; i < max; i++) { + assertTrue(pool.add(new CounterBean())); + } + + + { // Should have a full, non-null pool + final List entries = drain(pool, 100); + checkMin(min, entries); + checkEntries(max, entries); + push(pool, entries); + } + + // Now wait for the instances in the pool to expire + assertTrue(discarded.await(maxAge * 4, TimeUnit.MILLISECONDS)); + + // Minimum instances are still being created + // The rest of the pool should be empty (null) + { + final List entries = drain(pool, 100); + // Should have "non-min" number of entries + checkMax(max - min, entries); + + // Nothing should be a "min" item as those + // are still being created + checkMin(0, entries); + + // And as the pool was just drained all the + // entries we get should be null + checkNull(entries); + + push(pool, entries); + } + + CounterBean.instances.set(0); + + // Try and trick the pool into adding more "min" items + // Fill the pool as much as we can -- should only let us + // fill to the max factoring in the "min" instances that + // are currently being created + { + final List entries = drain(pool, 100); + + // Should reject the instance we try to add + + assertFalse(pool.add(new CounterBean())); + + + // Empty the pool + discard(pool, entries); + + + // Now count how many instances it lets us add + CounterBean.instances.set(0); + while (pool.add(new CounterBean())) ; + + // As the "min" instances are still being created + // it should only let us fill max - min, then + 1 + // to account for the instance that gets rejected + // and terminates the while loop + final int expected = max - min + 1; + + assertEquals(expected, CounterBean.instances.getAndSet(0)); + } + + // Ok, let the "min" instance creation threads continue + createInstances.countDown(); + + // Wait for the "min" instance creation to complete + assertTrue(created.await(poll * 10, TimeUnit.MILLISECONDS)); + + { // Pool should be full but... + final List entries = drain(pool, 100); + // we failed to create the min instances + checkMin(0, entries); + + // the "min" entries should have been freed up + // and we should have all the possible entires + checkMax(max, entries); + + // though there should be "min" quantities of nulls + checkEntries(max-min, entries); + + // Now when we push these back in, the right number + // of entries should be converted to "min" entries + push(pool, entries); + + } + + { // Pool should be full but... + final List entries = drain(pool, 100); + + // now we should have the right number of mins + checkMin(min, entries); + + // should still have a full pool + checkMax(max, entries); + + // though there should still be "min" quantities of nulls + // as we still haven't created any more instances, we just + // converted some of our instances into "min" entries + checkEntries(max-min, entries); + + } + + // -- DONE -- + } + + + private <T> void checkMax(int max, List<Pool.Entry<T>> entries) { assertEquals(max, entries.size()); } - private void checkMin(int min, List<Pool.Entry<String>> entries) { - int actualMin = 0; - for (Pool.Entry<String> entry : entries) { - if (entry != null && entry.hasHardReference()) actualMin++; + private <T> void checkMin(int min, List<Pool.Entry<T>> entries) { + assertEquals(min, getMin(entries).size()); + } + + private <T> void checkNull(List<Pool.Entry<T>> entries) { + for (Pool.Entry<T> entry : entries) { + assertNull(entry); } + } + + private <T> List<Pool.Entry<T>> getMin(List<Pool.Entry<T>> entries) { + List<Pool.Entry<T>> list = new ArrayList<Pool.Entry<T>>(); - assertEquals(min, actualMin); + for (Pool.Entry<T> entry : entries) { + if (entry != null && entry.hasHardReference()) list.add(entry); + } + return list; } private void checkEntries(int expected, List<Pool.Entry<String>> entries) { @@ -235,10 +872,14 @@ public class PoolTest extends TestCase { } private <T> List<Pool.Entry<T>> drain(Pool<T> pool) throws InterruptedException { + return drain(pool, 0); + } + + private <T> List<Pool.Entry<T>> drain(Pool<T> pool, int timeout) throws InterruptedException { List<Pool.Entry<T>> entries = new ArrayList<Pool.Entry<T>>(); try { while (true) { - entries.add(pool.pop(0, MILLISECONDS)); + entries.add(pool.pop(timeout, MILLISECONDS)); } } catch (TimeoutException e) { // pool drained @@ -257,7 +898,7 @@ public class PoolTest extends TestCase { } public int count() { - return instances.get(); + return count; } }
Added: openejb/trunk/openejb3/container/openejb-junit/src/main/java/org/apache/openejb/junit/LocalClientRunner.java URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-junit/src/main/java/org/apache/openejb/junit/LocalClientRunner.java?rev=924925&view=auto ============================================================================== --- openejb/trunk/openejb3/container/openejb-junit/src/main/java/org/apache/openejb/junit/LocalClientRunner.java (added) +++ openejb/trunk/openejb3/container/openejb-junit/src/main/java/org/apache/openejb/junit/LocalClientRunner.java Thu Mar 18 18:09:27 2010 @@ -0,0 +1,258 @@ +/** + * 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.junit; + +import org.apache.openejb.BeanType; +import org.apache.openejb.SystemException; +import org.apache.openejb.loader.SystemInstance; +import org.apache.openejb.core.CoreDeploymentInfo; +import org.apache.openejb.core.DeploymentContext; +import org.apache.openejb.core.ThreadContext; +import org.apache.openejb.core.transaction.JtaTransactionPolicyFactory; +import org.apache.openejb.core.transaction.TransactionType; +import org.apache.openejb.core.transaction.TransactionPolicy; +import org.apache.openejb.core.ivm.naming.IvmContext; +import org.apache.xbean.finder.ClassFinder; +import org.junit.internal.runners.model.ReflectiveCallable; +import org.junit.internal.runners.statements.Fail; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.Statement; + +import javax.transaction.TransactionManager; +import javax.interceptor.Interceptors; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; + +/** + * @version $Rev$ $Date$ + */ +public class LocalClientRunner extends BlockJUnit4ClassRunner { + + private final CoreDeploymentInfo deployment; + private final Class<?> clazz; + + public LocalClientRunner(Class<?> clazz) throws InitializationError { + super(clazz); + deployment = createDeployment(clazz); + this.clazz = clazz; + } + + @Override + protected Statement methodBlock(FrameworkMethod method) { + Object instance = newTestInstance(); + + Test test = new Test(clazz, method.getMethod(), instance, deployment); + + Statement statement = methodInvoker(method, instance); + + statement = wrap(test, statement, RunAs.class, javax.annotation.security.RunAs.class); + statement = wrap(test, statement, RunTestAs.class, org.apache.openejb.junit.RunTestAs.class); + statement = wrap(test, statement, Transaction.class, org.apache.openejb.junit.Transaction.class); + statement = wrap(test, statement, TransactionAttribute.class, javax.ejb.TransactionAttribute.class); + + statement = possiblyExpectingExceptions(method, instance, statement); + statement = withPotentialTimeout(method, instance, statement); + statement = withBefores(method, instance, statement); + statement = withAfters(method, instance, statement); + return statement; + } + + private Statement wrap(Test test, Statement statement, Class<? extends AnnotationStatement> clazz, Class<? extends Annotation> annotation) { + + if (test.has(annotation)) { + try { + Class[] types = {annotation, Statement.class, Test.class}; + Object[] args = {test.get(annotation), statement, test}; + return clazz.getConstructor(types).newInstance(args); + } catch (Exception e) { + throw new IllegalStateException("Cannot construct "+ clazz, e); + } + } + return statement; + } + + /** + * Creates a new test instance + * + * @return new instance + */ + private Object newTestInstance() { + try { + return new ReflectiveCallable() { + @Override + protected Object runReflectiveCall() throws Throwable { + return createTest(); + } + }.run(); + } catch (Throwable e) { + return new Fail(e); + } + } + + private CoreDeploymentInfo createDeployment(Class<?> testClass) { + try { + DeploymentContext deployment = new DeploymentContext(null, testClass.getClassLoader(), new IvmContext()); + return new CoreDeploymentInfo(deployment, testClass, null, null, null, null, null, null, null, null, BeanType.MANAGED); + } catch (SystemException e) { + throw new IllegalStateException(e); + } + } + + public static abstract class AnnotationStatement<A extends Annotation> extends Statement { + + protected final A annotation; + protected final Statement next; + protected final Test test; + protected final CoreDeploymentInfo info; + + protected AnnotationStatement(A annotation, Statement next, Test test) { + this.annotation = annotation; + this.next = next; + this.test = test; + this.info = test.info; + } + } + + private static class Test { + + public final Class clazz; + public final Method method; + public final Object instance; + public final CoreDeploymentInfo info; + + private Test(Class clazz, Method method, Object instance, CoreDeploymentInfo info) { + this.clazz = clazz; + this.method = method; + this.instance = instance; + this.info = info; + } + + private <A extends Annotation> boolean has(Class<A> a) { + return method.isAnnotationPresent(a) || clazz.isAnnotationPresent(a); + } + + private <A extends Annotation> A get(Class<A> annotationClass) { + A annotation = method.getAnnotation(annotationClass); + + if (annotation == null) { + annotation = (A) clazz.getAnnotation(annotationClass); + ; + } + + return annotation; + } + } + + public static class RunAs extends AnnotationStatement<javax.annotation.security.RunAs> { + + public RunAs(javax.annotation.security.RunAs annotation, Statement next, Test test) { + super(annotation, next, test); + } + + public void evaluate() throws Throwable { + info.setRunAs(annotation.value()); + final ThreadContext context = new ThreadContext(info, null); + final ThreadContext old = ThreadContext.enter(context); + try { + next.evaluate(); + } finally { + ThreadContext.exit(old); + } + } + } + + public static class RunTestAs extends AnnotationStatement<org.apache.openejb.junit.RunTestAs> { + + public RunTestAs(org.apache.openejb.junit.RunTestAs annotation, Statement next, Test test) { + super(annotation, next, test); + } + + public void evaluate() throws Throwable { + info.setRunAs(annotation.value()); + final ThreadContext context = new ThreadContext(info, null); + final ThreadContext old = ThreadContext.enter(context); + try { + next.evaluate(); + } finally { + ThreadContext.exit(old); + } + } + } + + public static class TransactionAttribute extends AnnotationStatement<javax.ejb.TransactionAttribute> { + + public TransactionAttribute(javax.ejb.TransactionAttribute annotation, Statement next, Test test) { + super(annotation, next, test); + } + + public void evaluate() throws Throwable { + TransactionManager transactionManager = SystemInstance.get().getComponent(TransactionManager.class); + JtaTransactionPolicyFactory factory = new JtaTransactionPolicyFactory(transactionManager); + TransactionType transactionType = TransactionType.get(annotation.value()); + // This creates *and* begins the transaction + TransactionPolicy policy = factory.createTransactionPolicy(transactionType); + try { + next.evaluate(); + } catch (Throwable t) { + if (!isApplicationException(t)) policy.setRollbackOnly(); + } finally { + policy.commit(); + } + } + + private boolean isApplicationException(Throwable t) { + if (t.getClass().isAnnotationPresent(javax.ejb.ApplicationException.class)) return true; + if (t instanceof Error) return false; + if (t instanceof RuntimeException) return false; + return true; + } + } + + public static class Transaction extends AnnotationStatement<org.apache.openejb.junit.Transaction> { + + public Transaction(org.apache.openejb.junit.Transaction annotation, Statement next, Test test) { + super(annotation, next, test); + } + + public void evaluate() throws Throwable { + TransactionManager transactionManager = SystemInstance.get().getComponent(TransactionManager.class); + JtaTransactionPolicyFactory factory = new JtaTransactionPolicyFactory(transactionManager); + + // This creates *and* begins the transaction + TransactionPolicy policy = factory.createTransactionPolicy(TransactionType.RequiresNew); + try { + next.evaluate(); + } finally { + + if (annotation.rollback()) policy.setRollbackOnly(); + + policy.commit(); + } + } + } + + public static class Interceptorss extends AnnotationStatement<Interceptors> { + public Interceptorss(Interceptors annotation, Statement next, Test test) { + super(annotation, next, test); + } + + public void evaluate() throws Throwable { + } + } +} Propchange: openejb/trunk/openejb3/container/openejb-junit/src/main/java/org/apache/openejb/junit/LocalClientRunner.java ------------------------------------------------------------------------------ svn:eol-style = native Added: openejb/trunk/openejb3/container/openejb-junit/src/main/java/org/apache/openejb/junit/Transaction.java URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-junit/src/main/java/org/apache/openejb/junit/Transaction.java?rev=924925&view=auto ============================================================================== --- openejb/trunk/openejb3/container/openejb-junit/src/main/java/org/apache/openejb/junit/Transaction.java (added) +++ openejb/trunk/openejb3/container/openejb-junit/src/main/java/org/apache/openejb/junit/Transaction.java Thu Mar 18 18:09:27 2010 @@ -0,0 +1,15 @@ +package org.apache.openejb.junit; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author quintin + */ +...@target({ElementType.METHOD, ElementType.TYPE}) +...@retention(RetentionPolicy.RUNTIME) +public @interface Transaction { + boolean rollback() default false; +} Propchange: openejb/trunk/openejb3/container/openejb-junit/src/main/java/org/apache/openejb/junit/Transaction.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: openejb/trunk/openejb3/container/openejb-osgi/src/main/resources/META-INF/org.apache.openejb/service-jar.xml URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-osgi/src/main/resources/META-INF/org.apache.openejb/service-jar.xml?rev=924925&r1=924924&r2=924925&view=diff ============================================================================== --- openejb/trunk/openejb3/container/openejb-osgi/src/main/resources/META-INF/org.apache.openejb/service-jar.xml (original) +++ openejb/trunk/openejb3/container/openejb-osgi/src/main/resources/META-INF/org.apache.openejb/service-jar.xml Thu Mar 18 18:09:27 2010 @@ -82,17 +82,20 @@ id="Default Stateless Container" service="Container" types="STATELESS" - constructor="id, securityService, TimeOut, PoolMin, PoolSize, StrictPooling" - class-name="org.apache.openejb.core.stateless.StatelessContainer"> + factory-name="create" + class-name="org.apache.openejb.core.stateless.StatelessContainerFactory"> # Specifies the time an invokation should wait for an instance - # of the pool to become available. This measured by default in - # milliseconds, but other time units can be specified, such as - # nanoseconds, microsecons, seconds or minutes. After the timeout - # is reached, if an instance in the pool cannot be obtained, the - # method invocation will fail. + # of the pool to become available. + # + # After the timeout is reached, if an instance in the pool cannot + # be obtained, the method invocation will fail. + # + # Usable time units: nanoseconds, microsecons, milliseconds, + # seconds, minutes, hours, days. Or any combination such as + # "1 hour and 27 minutes and 10 seconds" - TimeOut = 0 milliseconds + AccessTimeout = 0 milliseconds # Specifies the minimum number of bean instances that should be # in the pool for each bean. Pools are prefilled to the minimum @@ -102,28 +105,72 @@ PoolMin 0 - # Specifies the size of the bean pools for this - # stateless SessionBean container. If StrictPooling is not - # used, instances will still be created beyond this number if - # there is demand, but they will not be returned to the pool - # and instead will be immediately destroyed. + # Specifies the size of the bean pools for this stateless + # SessionBean container. If StrictPooling is not used, instances + # will still be created beyond this number if there is demand, but + # they will not be returned to the pool and instead will be + # immediately destroyed. PoolSize 10 # StrictPooling tells the container what to do when the pool - # reaches it's maximum size and there are incoming requests - # that need instances. + # reaches it's maximum size and there are incoming requests that + # need instances. # - # With strict pooling, requests will have to wait for instances - # to become available. The pool size will never grow beyond the - # the set PoolSize value. + # With strict pooling, requests will have to wait for instances to + # become available. The pool size will never grow beyond the the + # set PoolSize value. The maximum amount of time a request should + # wait is specified via the AccessTimeout setting. # # Without strict pooling, the container will create temporary # instances to meet demand. The instances will last for just one # method invocation and then are removed. + # + # Setting StrictPooling to false and PoolSize to 0 will result in + # no pooling. Instead instances will be created on demand and live + # for exactly one method call before being removed. StrictPooling true + # Specifies the maximum time that an instance should live before + # it should be retired and removed from use. This will happen + # gracefully. Useful for situations where bean instances are + # designed to hold potentially expensive resources such as memory + # or file handles and need to be periodically cleared out. + # + # Usable time units: nanoseconds, microsecons, milliseconds, + # seconds, minutes, hours, days. Or any combination such as + # "1 hour and 27 minutes and 10 seconds" + + MaxAge = 0 hours + + # Specifies the maximum time that an instance should be allowed to + # sit idly in the pool without use before it should be retired and + # removed. + # + # Note that all instances in the pool, excluding the minimum, are + # eligible for garbage collection by the virtual machine as per + # the rules of java.lang.ref.WeakReference, so the use of an + # IdleTimeout is not required to conserve JVM-managed memory or + # shrink the pool. + # + # Usable time units: nanoseconds, microsecons, milliseconds, + # seconds, minutes, hours, days. Or any combination such as + # "1 hour and 27 minutes and 10 seconds" + + IdleTimeout = 0 minutes + + # The frequency in which the container will sweep the pool and + # evict expired instances. Eviction is how the IdleTimeout, + # MaxAge, and pool "flush" functionality is enforced. Higher + # intervals are better. Expired instances in use while the pool + # is swept will still be evicted upon return to the pool. + # + # Usable time units: nanoseconds, microsecons, milliseconds, + # seconds, minutes, hours, days. Or any combination such as + # "1 hour and 27 minutes and 10 seconds" + + PollInterval = 5 minutes </ServiceProvider>
