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"));
+ }
+ }
+}