You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by fr...@apache.org on 2006/12/17 09:00:35 UTC

svn commit: r487946 - in /tapestry/tapestry5/tapestry-ioc/trunk/src: main/java/org/apache/tapestry/ioc/RegistryBuilder.java main/java/org/apache/tapestry/ioc/internal/RegistryImpl.java test/java/org/apache/tapestry/ioc/RegistryBuilderOverrideTest.java

Author: freemant
Date: Sun Dec 17 00:00:34 2006
New Revision: 487946

URL: http://svn.apache.org/viewvc?view=rev&rev=487946
Log:
Allows overriding service implementations. The primary purpose 
is to support unit testing pages/components by overriding
the business services with mock objects. The overriding
implementations are treated pretty much like the built-in
services.

Added:
    tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/RegistryBuilderOverrideTest.java
Modified:
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/RegistryBuilder.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/RegistryImpl.java

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/RegistryBuilder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/RegistryBuilder.java?view=diff&rev=487946&r1=487945&r2=487946
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/RegistryBuilder.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/RegistryBuilder.java Sun Dec 17 00:00:34 2006
@@ -12,134 +12,146 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.ioc;
-
-import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newList;
-import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newMap;
-
-import java.lang.reflect.AnnotatedElement;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.commons.logging.Log;
-import org.apache.tapestry.ioc.annotations.SubModule;
-import org.apache.tapestry.ioc.def.ModuleDef;
+package org.apache.tapestry.ioc;
+
+import java.lang.reflect.AnnotatedElement;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.tapestry.ioc.annotations.SubModule;
+import org.apache.tapestry.ioc.def.ModuleDef;
 import org.apache.tapestry.ioc.internal.DefaultModuleDefImpl;
 import org.apache.tapestry.ioc.internal.IOCMessages;
 import org.apache.tapestry.ioc.internal.LogSourceImpl;
 import org.apache.tapestry.ioc.internal.RegistryImpl;
 import org.apache.tapestry.ioc.internal.util.OneShotLock;
-import org.apache.tapestry.ioc.services.TapestryIOCModule;
-
-/**
- * Used to construct the IoC {@link org.apache.tapestry.ioc.Registry}. This class is <em>not</em>
- * threadsafe. The Registry, once created, <em>is</em> threadsafe.
- */
-public final class RegistryBuilder
-{
-    private final OneShotLock _lock = new OneShotLock();
-
-    /** Module defs, keyed on module id. */
-    final Map<String, ModuleDef> _modules = newMap();
-
-    private final ClassLoader _classLoader;
-
-    private final Log _log;
-
-    private final LogSource _logSource;
-
-    public RegistryBuilder()
-    {
-        this(Thread.currentThread().getContextClassLoader());
-    }
-
-    public RegistryBuilder(ClassLoader classLoader)
-    {
-        this(classLoader, new LogSourceImpl());
-    }
-
-    public RegistryBuilder(ClassLoader classLoader, LogSource logSource)
-    {
-        _classLoader = classLoader;
-        _logSource = logSource;
-        _log = logSource.getLog(RegistryBuilder.class);
-
-        add(TapestryIOCModule.class);
-    }
-
-    public void add(ModuleDef moduleDef)
-    {
-        _lock.check();
-
-        String id = moduleDef.getModuleId();
-
-        if (_modules.containsKey(id))
-        {
-            _log.warn(IOCMessages.moduleIdConflict(id), null);
-            return;
-        }
-
-        _modules.put(id, moduleDef);
-    }
-
-    public void add(Class... moduleBuilderClasses)
-    {
-        _lock.check();
-
-        List<Class> queue = newList(Arrays.asList(moduleBuilderClasses));
-
-        while (!queue.isEmpty())
-        {
-            Class c = queue.remove(0);
-
-            ModuleDef def = new DefaultModuleDefImpl(c, _log);
-            add(def);
-
-            SubModule annotation = ((AnnotatedElement) c).getAnnotation(SubModule.class);
-
-            if (annotation == null)
-                continue;
-
-            for (Class sub : annotation.value())
-                queue.add(sub);
-        }
-    }
-
-    public void add(String classname)
-    {
-        _lock.check();
-
-        try
-        {
-            Class builderClass = Class.forName(classname, true, _classLoader);
-
-            add(builderClass);
-        }
-        catch (ClassNotFoundException ex)
-        {
-            throw new IllegalArgumentException(ex);
-        }
-    }
-
-    public Registry build()
-    {
-        _lock.lock();
-
-        return new RegistryImpl(_modules.values(), _classLoader, _logSource);
-    }
-
-    public ClassLoader getClassLoader()
-    {
-        _lock.check();
-
-        return _classLoader;
-    }
-
-    public Log getLog()
-    {
-        _lock.check();
-
-        return _log;
-    }
-}
+import org.apache.tapestry.ioc.services.TapestryIOCModule;
+
+import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newList;
+import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newMap;
+
+/**
+ * Used to construct the IoC {@link org.apache.tapestry.ioc.Registry}. This class is <em>not</em>
+ * threadsafe. The Registry, once created, <em>is</em> threadsafe.
+ */
+public final class RegistryBuilder
+{
+    private final OneShotLock _lock = new OneShotLock();
+
+    /** Module defs, keyed on module id. */
+    final Map<String, ModuleDef> _modules = newMap();
+
+    /**
+     * Service implementation overrides, keyed on service id. Service implementations are most
+     * useful when perfroming integration tests on services. As one service can bring in another, we
+     * have to stop at a certain "bounary" services by provide stub/ mock objects as their
+     * implementations.
+     */
+    private final Map<String, Object> _serviceOverrides = newMap();
+
+    private final ClassLoader _classLoader;
+
+    private final Log _log;
+
+    private final LogSource _logSource;
+
+    public RegistryBuilder()
+    {
+        this(Thread.currentThread().getContextClassLoader());
+    }
+
+    public RegistryBuilder(ClassLoader classLoader)
+    {
+        this(classLoader, new LogSourceImpl());
+    }
+
+    public RegistryBuilder(ClassLoader classLoader, LogSource logSource)
+    {
+        _classLoader = classLoader;
+        _logSource = logSource;
+        _log = logSource.getLog(RegistryBuilder.class);
+
+        add(TapestryIOCModule.class);
+    }
+
+    public void add(ModuleDef moduleDef)
+    {
+        _lock.check();
+
+        String id = moduleDef.getModuleId();
+
+        if (_modules.containsKey(id))
+        {
+            _log.warn(IOCMessages.moduleIdConflict(id), null);
+            return;
+        }
+
+        _modules.put(id, moduleDef);
+    }
+
+    public void add(Class... moduleBuilderClasses)
+    {
+        _lock.check();
+
+        List<Class> queue = newList(Arrays.asList(moduleBuilderClasses));
+
+        while (!queue.isEmpty())
+        {
+            Class c = queue.remove(0);
+
+            ModuleDef def = new DefaultModuleDefImpl(c, _log);
+            add(def);
+
+            SubModule annotation = ((AnnotatedElement) c).getAnnotation(SubModule.class);
+
+            if (annotation == null) continue;
+
+            for (Class sub : annotation.value())
+                queue.add(sub);
+        }
+    }
+
+    public void add(String classname)
+    {
+        _lock.check();
+
+        try
+        {
+            Class builderClass = Class.forName(classname, true, _classLoader);
+
+            add(builderClass);
+        }
+        catch (ClassNotFoundException ex)
+        {
+            throw new IllegalArgumentException(ex);
+        }
+    }
+
+    public Registry build()
+    {
+        _lock.lock();
+
+        return new RegistryImpl(_modules.values(), _classLoader, _logSource, _serviceOverrides);
+    }
+
+    public ClassLoader getClassLoader()
+    {
+        _lock.check();
+
+        return _classLoader;
+    }
+
+    public Log getLog()
+    {
+        _lock.check();
+
+        return _log;
+    }
+
+    public void addServiceOverride(String serviceId, Object overridingImpl)
+    {
+        _serviceOverrides.put(serviceId, overridingImpl);
+    }
+}

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/RegistryImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/RegistryImpl.java?view=diff&rev=487946&r1=487945&r2=487946
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/RegistryImpl.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/RegistryImpl.java Sun Dec 17 00:00:34 2006
@@ -87,6 +87,14 @@
 
     private final Map<String, ServiceLifecycle> _lifecycles = newMap();
 
+    /**
+     * Service implementation overrides, keyed on service id. Service implementations are most
+     * useful when perfroming integration tests on services. As one service can bring in another, we
+     * have to stop at a certain "bounary" services by provide stub/ mock objects as their
+     * implementations.
+     */
+    private Map<String, Object> _serviceOverrides;
+
     private final ThreadCleanupHubImpl _cleanupHub;
 
     private final ClassFactory _classFactory;
@@ -110,9 +118,11 @@
     }
 
     public RegistryImpl(Collection<ModuleDef> moduleDefs, ClassLoader contextClassLoader,
-            LogSource logSource)
+            LogSource logSource, Map<String, Object> serviceOverrides)
     {
         _logSource = logSource;
+        
+        _serviceOverrides = serviceOverrides;
 
         for (ModuleDef def : moduleDefs)
         {
@@ -175,6 +185,9 @@
         T result = checkForBuiltinService(serviceId, serviceInterface);
         if (result != null) return result;
 
+        result = checkForServiceOverrides(serviceId, serviceInterface);
+        if (result != null) return result;
+
         // Checking serviceId and serviceInterface is overkill; they have been checked and rechecked
         // all the way to here.
 
@@ -197,6 +210,23 @@
         {
             throw new RuntimeException(IOCMessages.serviceWrongInterface(serviceId, _builtinTypes
                     .get(serviceId), serviceInterface));
+        }
+    }
+
+    private <T> T checkForServiceOverrides(String serviceId, Class<T> serviceInterface)
+    {
+        Object service = _serviceOverrides.get(serviceId);
+
+        if (service == null) return null;
+
+        try
+        {
+            return serviceInterface.cast(service);
+        }
+        catch (ClassCastException ex)
+        {
+            throw new RuntimeException(IOCMessages.serviceWrongInterface(serviceId, 
+                    service.getClass(), serviceInterface));
         }
     }
 

Added: tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/RegistryBuilderOverrideTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/RegistryBuilderOverrideTest.java?view=auto&rev=487946
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/RegistryBuilderOverrideTest.java (added)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/RegistryBuilderOverrideTest.java Sun Dec 17 00:00:34 2006
@@ -0,0 +1,130 @@
+// 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;
+
+import org.apache.tapestry.ioc.annotations.Id;
+import org.apache.tapestry.ioc.annotations.InjectService;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+@Test
+public class RegistryBuilderOverrideTest extends Assert
+{
+    private RegistryBuilder builder;
+
+    private Registry registry;
+
+    public interface Transformer<T>
+    {
+        T transform(T input);
+    }
+
+    public static class IdentifyTransformer<T> implements Transformer<T>
+    {
+        public T transform(T input)
+        {
+            return input;
+        }
+    }
+
+    public static class UppercaseTransformer implements Transformer<String>
+    {
+        public String transform(String input)
+        {
+            return input.toUpperCase();
+        }
+    }
+
+    @Id("test")
+    public static class TestModule
+    {
+        public static Transformer buildService1()
+        {
+            return new IdentifyTransformer<String>();
+        }
+
+        public static Transformer buildService2()
+        {
+            return new IdentifyTransformer<String>();
+        }
+
+        // Just a proxy for Service2.
+        public static Transformer buildService3(@InjectService("Service2")
+        Transformer s2)
+        {
+            return s2;
+        }
+    }
+
+    @BeforeMethod
+    public void before()
+    {
+        builder = new RegistryBuilder();
+        builder.add(TestModule.class);
+    }
+
+    @AfterMethod
+    public void after()
+    {
+        if (registry != null)
+        {
+            registry.shutdown();
+        }
+    }
+
+    @Test
+    public void testServiceOverride()
+    {
+        builder.addServiceOverride("test.Service2", new UppercaseTransformer());
+        registry = builder.build();
+        @SuppressWarnings("unchecked")
+        Transformer<String> s1 = registry.getService("test.Service1", Transformer.class);
+        assertEquals(s1.transform("a"), "a");
+        @SuppressWarnings("unchecked")
+        Transformer<String> s2 = registry.getService("test.Service2", Transformer.class);
+        assertEquals(s2.transform("a"), "A");
+    }
+
+    @Test
+    public void testOverrideInjectedIntoOtherServices()
+    {
+        builder.addServiceOverride("test.Service2", new UppercaseTransformer());
+        registry = builder.build();
+        @SuppressWarnings("unchecked")
+        Transformer<String> s3 = registry.getService("test.Service3", Transformer.class);
+        assertEquals(s3.transform("a"), "A");
+    }
+
+    @Test
+    public void testInterfaceIncorrect()
+    {
+        builder = new RegistryBuilder();
+        builder.add(TestModule.class);
+        builder.addServiceOverride("test.Service2", "bad impl");
+        registry = builder.build();
+        try
+        {
+            registry.getService("test.Service2", Transformer.class);
+        }
+        catch (RuntimeException e)
+        {
+            String errorMsg = e.getMessage();
+            assertTrue(errorMsg.contains("String"));
+            assertTrue(errorMsg.contains("Transformer"));
+        }
+    }
+}