You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2006/08/04 01:43:10 UTC

svn commit: r428569 - in /tapestry/tapestry5/tapestry-core/trunk/src: main/java/org/apache/tapestry/internal/ioc/services/ main/java/org/apache/tapestry/ioc/services/ test/java/org/apache/tapestry/internal/ioc/services/

Author: hlship
Date: Thu Aug  3 16:43:09 2006
New Revision: 428569

URL: http://svn.apache.org/viewvc?rev=428569&view=rev
Log:
Add service to create default implementations of a service.
Change PipelineBuilder to allow the terminator to be omitted (and create a default implementation).
Add method for creating noop methods to ClassFab.

Added:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/DefaultImplementationBuilderImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/DefaultImplementationBuilder.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/DefaultImplementationBuilderImplTest.java
Modified:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ClassFabImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PipelineBuilderImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassFab.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PipelineBuilder.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ClassFabImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/PipelineBuilderImplTest.java

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ClassFabImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ClassFabImpl.java?rev=428569&r1=428568&r2=428569&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ClassFabImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ClassFabImpl.java Thu Aug  3 16:43:09 2006
@@ -16,6 +16,7 @@
 
 import java.lang.reflect.Modifier;
 import java.util.Formatter;
+import java.util.Map;
 import java.util.Set;
 
 import javassist.CannotCompileException;
@@ -35,6 +36,7 @@
 import org.apache.tapestry.util.Defense;
 
 import static java.lang.String.format;
+import static org.apache.tapestry.util.CollectionFactory.newMap;
 import static org.apache.tapestry.util.CollectionFactory.newSet;
 
 /**
@@ -45,6 +47,16 @@
  */
 public class ClassFabImpl extends AbstractFab implements ClassFab
 {
+    private static final Map<Class, String> DEFAULT_RETURN = newMap();
+
+    static
+    {
+        DEFAULT_RETURN.put(boolean.class, "false");
+        DEFAULT_RETURN.put(long.class, "0L");
+        DEFAULT_RETURN.put(float.class, "0.0f");
+        DEFAULT_RETURN.put(double.class, "0.0d");
+    }
+
     /**
      * Add fields, methods, and constructors are added, their psuedo-code is appended to this
      * description, which is used by toString().
@@ -55,7 +67,6 @@
 
     private final Set<MethodSignature> _addedSignatures = newSet();
 
-   
     public ClassFabImpl(CtClassSource source, CtClass ctClass, Log log)
     {
         super(source, ctClass, log);
@@ -216,6 +227,27 @@
         addMethodDetailsToDescription(ms.getParameterTypes(), ms.getExceptionTypes(), body);
 
         _description.append("\n\n");
+    }
+
+    public void addNoOpMethod(MethodSignature signature)
+    {
+        Class returnType = signature.getReturnType();
+
+        if (returnType.equals(void.class))
+        {
+            addMethod(Modifier.PUBLIC, signature, "return;");
+            return;
+        }
+
+        String value = "null";
+        if (returnType.isPrimitive())
+        {
+            value = DEFAULT_RETURN.get(returnType);
+            if (value == null)
+                value = "0";
+        }
+
+        addMethod(Modifier.PUBLIC, signature, "return " + value + ";");
     }
 
     @SuppressNullCheck

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/DefaultImplementationBuilderImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/DefaultImplementationBuilderImpl.java?rev=428569&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/DefaultImplementationBuilderImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/DefaultImplementationBuilderImpl.java Thu Aug  3 16:43:09 2006
@@ -0,0 +1,96 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// 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.tapestry.internal.ioc.services;
+
+import java.util.Map;
+
+import org.apache.tapestry.internal.annotations.Concurrent;
+import org.apache.tapestry.ioc.services.ClassFab;
+import org.apache.tapestry.ioc.services.ClassFactory;
+import org.apache.tapestry.ioc.services.DefaultImplementationBuilder;
+import org.apache.tapestry.ioc.services.MethodIterator;
+import org.apache.tapestry.ioc.services.MethodSignature;
+
+import static java.lang.String.format;
+import static org.apache.tapestry.util.CollectionFactory.newMap;
+
+/**
+ * @author Howard M. Lewis Ship
+ */
+@Concurrent
+public class DefaultImplementationBuilderImpl implements DefaultImplementationBuilder
+{
+    private final Map<Class, Object> _cache = newMap();
+
+    private final ClassFactory _classFactory;
+
+    public DefaultImplementationBuilderImpl(ClassFactory classFactory)
+    {
+        _classFactory = classFactory;
+    }
+
+    @Concurrent.Read
+    public <S> S createDefaultImplementation(Class<S> serviceInterface)
+    {
+        S instance = serviceInterface.cast(_cache.get(serviceInterface));
+
+        if (instance == null)
+            instance = fillCache(serviceInterface);
+
+        return instance;
+    }
+
+    /**
+     * Creates a class and an instance of that class. Updates the cache and returns the instance.
+     */
+    @Concurrent.Write
+    private <S> S fillCache(Class<S> serviceInterface)
+    {
+        Class<S> noopClass = createClass(serviceInterface);
+
+        try
+        {
+            S instance = noopClass.newInstance();
+
+            _cache.put(serviceInterface, instance);
+
+            return instance;
+        }
+        catch (Exception ex)
+        {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private <S> Class<S> createClass(Class<S> serviceInterface)
+    {
+        ClassFab cf = _classFactory.newClass(serviceInterface);
+
+        MethodIterator mi = new MethodIterator(serviceInterface);
+
+        while (mi.hasNext())
+        {
+            MethodSignature sig = mi.next();
+
+            cf.addNoOpMethod(sig);
+        }
+
+        if (!mi.getToString())
+            cf.addToString(format("<NoOp %s>", serviceInterface.getName()));
+
+        return cf.createClass();
+    }
+}
\ No newline at end of file

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PipelineBuilderImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PipelineBuilderImpl.java?rev=428569&r1=428568&r2=428569&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PipelineBuilderImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PipelineBuilderImpl.java Thu Aug  3 16:43:09 2006
@@ -18,6 +18,7 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.tapestry.ioc.services.ClassFactory;
+import org.apache.tapestry.ioc.services.DefaultImplementationBuilder;
 import org.apache.tapestry.ioc.services.PipelineBuilder;
 
 /**
@@ -27,9 +28,21 @@
 {
     private final ClassFactory _classFactory;
 
-    public PipelineBuilderImpl(ClassFactory classFactory)
+    private final DefaultImplementationBuilder _defaultImplementationBuilder;
+
+    public PipelineBuilderImpl(ClassFactory classFactory,
+            DefaultImplementationBuilder defaultImplementationBuilder)
     {
         _classFactory = classFactory;
+        _defaultImplementationBuilder = defaultImplementationBuilder;
+    }
+
+    public <S, F> S buildPipeline(Log log, Class<S> serviceInterface, Class<F> filterInterface,
+            List<F> filters)
+    {
+        S terminator = _defaultImplementationBuilder.createDefaultImplementation(serviceInterface);
+
+        return buildPipeline(log, serviceInterface, filterInterface, filters, terminator);
     }
 
     public <S, F> S buildPipeline(Log log, Class<S> serviceInterface, Class<F> filterInterface,

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassFab.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassFab.java?rev=428569&r1=428568&r2=428569&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassFab.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassFab.java Thu Aug  3 16:43:09 2006
@@ -113,4 +113,11 @@
      * implemented in the (concrete) class.
      */
     Class createClass();
+
+    /**
+     * Adds a public no-op method. The method will return null, false, or zero as per the return
+     * type (if not void).
+     */
+
+    void addNoOpMethod(MethodSignature signature);
 }

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/DefaultImplementationBuilder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/DefaultImplementationBuilder.java?rev=428569&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/DefaultImplementationBuilder.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/DefaultImplementationBuilder.java Thu Aug  3 16:43:09 2006
@@ -0,0 +1,35 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// 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.tapestry.ioc.services;
+
+/**
+ * Creates default implementatons of a class.
+ * 
+ * @author Howard M. Lewis Ship
+ * @see org.apache.tapestry.ioc.services.ClassFab#addNoOpMethod(MethodSignature)
+ */
+public interface DefaultImplementationBuilder
+{
+    /**
+     * Creates a new implementation of the provided interface. Each method in the interface will be
+     * implemented as a noop method. The method will ignore any parameters and return null, or 0, or
+     * false (or return nothing if the method is void).
+     * 
+     * @param <S>
+     * @param serviceInterface
+     * @return
+     */
+    <S> S createDefaultImplementation(Class<S> serviceInterface);
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PipelineBuilder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PipelineBuilder.java?rev=428569&r1=428568&r2=428569&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PipelineBuilder.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PipelineBuilder.java Thu Aug  3 16:43:09 2006
@@ -42,6 +42,8 @@
 public interface PipelineBuilder
 {
     /**
+     * Creates a pipeline from the filters and a terminator.
+     * 
      * @param <S>
      *            service type
      * @param <F>
@@ -58,5 +60,20 @@
      */
     <S, F> S buildPipeline(Log log, Class<S> serviceInterface, Class<F> filterInterface,
             List<F> filters, S terminator);
+
+    /**
+     * Creates a pipeline from just the filters. A
+     * {@link DefaultImplementationBuilder default implementation} is created as the terminator.
+     * 
+     * @param <S>
+     * @param <F>
+     * @param log
+     * @param serviceInterface
+     * @param filterInterface
+     * @param filters
+     * @return
+     */
+    <S, F> S buildPipeline(Log log, Class<S> serviceInterface, Class<F> filterInterface,
+            List<F> filters);
 
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java?rev=428569&r1=428568&r2=428569&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java Thu Aug  3 16:43:09 2006
@@ -19,6 +19,7 @@
 import org.apache.commons.logging.Log;
 import org.apache.tapestry.internal.ioc.services.ChainBuilderImpl;
 import org.apache.tapestry.internal.ioc.services.ClassFactoryImpl;
+import org.apache.tapestry.internal.ioc.services.DefaultImplementationBuilderImpl;
 import org.apache.tapestry.internal.ioc.services.LoggingDecoratorImpl;
 import org.apache.tapestry.internal.ioc.services.PerThreadServiceLifecycle;
 import org.apache.tapestry.internal.ioc.services.PipelineBuilderImpl;
@@ -129,8 +130,16 @@
      * Builder that creates a filter pipeline around a simple service interface.
      */
     public PipelineBuilder buildPipelineBuilder(@InjectService("ClassFactory")
-    ClassFactory classFactory)
+    ClassFactory classFactory, @InjectService("DefaultImplementationBuilder")
+    DefaultImplementationBuilder builder)
+    {
+        return new PipelineBuilderImpl(classFactory, builder);
+    }
+
+    public DefaultImplementationBuilder buildDefaultImplementationBuilder(
+            @InjectService("ClassFactory")
+            ClassFactory classFactory)
     {
-        return new PipelineBuilderImpl(classFactory);
+        return new DefaultImplementationBuilderImpl(classFactory);
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ClassFabImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ClassFabImplTest.java?rev=428569&r1=428568&r2=428569&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ClassFabImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ClassFabImplTest.java Thu Aug  3 16:43:09 2006
@@ -25,10 +25,10 @@
 
 import org.apache.commons.logging.LogFactory;
 import org.apache.hivemind.impl.BaseLocatable;
-import org.apache.hivemind.util.PropertyUtils;
 import org.apache.tapestry.internal.ioc.services.LoggingDecoratorImplTest.ToStringService;
 import org.apache.tapestry.ioc.services.ClassFab;
 import org.apache.tapestry.ioc.services.MethodSignature;
+import org.apache.tapestry.ioc.services.PropertyAccess;
 import org.apache.tapestry.test.BaseTestCase;
 import org.testng.annotations.Test;
 
@@ -39,6 +39,8 @@
 {
     private final CtClassSource _source;
 
+    private final PropertyAccess _access = new PropertyAccessImpl();
+
     public interface SampleService
     {
         int primitiveMethod(int primitiveValue);
@@ -93,11 +95,11 @@
 
         Object targetBean = targetClass.newInstance();
 
-        PropertyUtils.write(targetBean, "stringValue", "Fred");
+        _access.set(targetBean, "stringValue", "Fred");
 
         // May keep a test-time dependency on HiveMind, just for PropertyUtils.
 
-        String actual = (String) PropertyUtils.read(targetBean, "stringValue");
+        String actual = (String) _access.get(targetBean, "stringValue");
 
         assertEquals(actual, "Fred");
     }
@@ -210,7 +212,7 @@
         Object targetBean = c.newInstance(new Object[]
         { "Buffy" });
 
-        String actual = (String) PropertyUtils.read(targetBean, "stringValue");
+        String actual = (String) _access.get(targetBean, "stringValue");
 
         assertEquals("Buffy", actual);
     }
@@ -435,6 +437,26 @@
                         + "  throws java.lang.InstantiationException, java.lang.IllegalAccessException\n"
                         + "{ return _map; }");
 
+    }
+
+    @Test
+    public void add_noop_method() throws Exception
+    {
+        ClassFab cf = newClassFab("NoOp", Object.class);
+        cf.addInterface(Runnable.class);
+
+        cf.addNoOpMethod(new MethodSignature(void.class, "run", null, null));
+        cf.addNoOpMethod(new MethodSignature(int.class, "getInt", null, null));
+        cf.addNoOpMethod(new MethodSignature(double.class, "getDouble", null, null));
+
+        Class clazz = cf.createClass();
+
+        Runnable instance = (Runnable) clazz.newInstance();
+
+        instance.run();
+
+        assertEquals(_access.get(instance, "int"), 0);
+        assertEquals(_access.get(instance, "double"), 0.0d);
     }
 
     private void assertContains(String actual, String expectedSubstring)

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/DefaultImplementationBuilderImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/DefaultImplementationBuilderImplTest.java?rev=428569&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/DefaultImplementationBuilderImplTest.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/DefaultImplementationBuilderImplTest.java Thu Aug  3 16:43:09 2006
@@ -0,0 +1,71 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// 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.tapestry.internal.ioc.services;
+
+import org.apache.tapestry.internal.test.InternalBaseTestCase;
+import org.apache.tapestry.ioc.Registry;
+import org.apache.tapestry.ioc.services.DefaultImplementationBuilder;
+import org.testng.annotations.Configuration;
+import org.testng.annotations.Test;
+
+/**
+ * @author Howard M. Lewis Ship
+ */
+public class DefaultImplementationBuilderImplTest extends InternalBaseTestCase
+{
+    private DefaultImplementationBuilder _builder;
+
+    @Configuration(beforeTestClass = true)
+    public void setup_builder()
+    {
+        Registry registry = buildRegistry();
+
+        _builder = registry.getService(
+                "tapestry.ioc.DefaultImplementationBuilder",
+                DefaultImplementationBuilder.class);
+    }
+
+    @Test
+    public void simple_interface()
+    {
+        Runnable r = _builder.createDefaultImplementation(Runnable.class);
+
+        r.run();
+
+        assertEquals(r.toString(), "<NoOp java.lang.Runnable>");
+    }
+
+    public interface ToString
+    {
+        String toString();
+    }
+
+    @Test
+    public void interface_has_toString()
+    {
+        ToString ts = _builder.createDefaultImplementation(ToString.class);
+
+        assertNull(ts.toString());
+    }
+
+    @Test
+    public void instances_are_cached()
+    {
+        Runnable r1 = _builder.createDefaultImplementation(Runnable.class);
+        Runnable r2 = _builder.createDefaultImplementation(Runnable.class);
+
+        assertSame(r2, r1);
+    }
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/PipelineBuilderImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/PipelineBuilderImplTest.java?rev=428569&r1=428568&r2=428569&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/PipelineBuilderImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/PipelineBuilderImplTest.java Thu Aug  3 16:43:09 2006
@@ -21,6 +21,7 @@
 import org.apache.tapestry.internal.test.InternalBaseTestCase;
 import org.apache.tapestry.ioc.Registry;
 import org.apache.tapestry.ioc.services.PipelineBuilder;
+import org.testng.annotations.Configuration;
 import org.testng.annotations.Test;
 
 import static org.apache.tapestry.util.CollectionFactory.newList;
@@ -33,6 +34,17 @@
 public class PipelineBuilderImplTest extends InternalBaseTestCase
 {
 
+    private PipelineBuilder _builder;
+
+    @Configuration(beforeTestClass = true)
+    public void setup_builder()
+    {
+        Registry registry = buildRegistry();
+
+        _builder = registry.getService("tapestry.ioc.PipelineBuilder", PipelineBuilder.class);
+
+    }
+
     @Test
     public void pipeline_with_filters()
     {
@@ -72,13 +84,7 @@
             }
         };
 
-        Registry registry = buildRegistry();
-
-        PipelineBuilder builder = registry.getService(
-                "tapestry.ioc.PipelineBuilder",
-                PipelineBuilder.class);
-
-        StandardService pipeline = builder.buildPipeline(
+        StandardService pipeline = _builder.buildPipeline(
                 log,
                 StandardService.class,
                 StandardFilter.class,
@@ -93,22 +99,16 @@
     }
 
     @Test
-    public void pipline_without_filters_is_terminator()
+    public void pipeline_without_filters_is_terminator()
     {
         Log log = newLog();
         StandardService terminator = newMock(StandardService.class);
 
         replay();
 
-        Registry registry = buildRegistry();
-
-        PipelineBuilder builder = registry.getService(
-                "tapestry.ioc.PipelineBuilder",
-                PipelineBuilder.class);
-
         List<StandardFilter> filters = newList();
 
-        StandardService pipeline = builder.buildPipeline(
+        StandardService pipeline = _builder.buildPipeline(
                 log,
                 StandardService.class,
                 StandardFilter.class,
@@ -116,6 +116,26 @@
                 terminator);
 
         assertSame(pipeline, terminator);
+
+        verify();
+    }
+
+    @Test
+    public void pipeline_with_default_terminator()
+    {
+        Log log = newLog();
+
+        replay();
+
+        List<StandardFilter> filters = newList();
+
+        StandardService pipeline = _builder.buildPipeline(
+                log,
+                StandardService.class,
+                StandardFilter.class,
+                filters);
+
+        assertEquals(pipeline.run(99), 0);
 
         verify();
     }