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>
 


Reply via email to