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 2007/11/17 17:36:52 UTC

svn commit: r595970 [1/2] - in /tapestry/tapestry5/trunk/tapestry-ioc/src: main/java/org/apache/tapestry/ioc/internal/ main/java/org/apache/tapestry/ioc/test/ main/resources/org/apache/tapestry/ioc/internal/ main/resources/org/apache/tapestry/ioc/inter...

Author: hlship
Date: Sat Nov 17 08:36:50 2007
New Revision: 595970

URL: http://svn.apache.org/viewvc?rev=595970&view=rev
Log:
TAPESTRY-1891:  Tapestry IoC Service Proxies should be serializable

Added:
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/SerializationSupport.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/ServiceProxyProvider.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/ServiceProxyToken.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/serialization.apt
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/ServiceProxySerializationTest.java
Modified:
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/IOCInternalTestCase.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/IOCMessages.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/ModuleImpl.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/RegistryImpl.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/test/IOCTestCase.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/resources/org/apache/tapestry/ioc/internal/IOCStrings.properties
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/resources/org/apache/tapestry/ioc/internal/services/ServiceStrings.properties
    tapestry/tapestry5/trunk/tapestry-ioc/src/site/site.xml
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/RegistryBuilderTest.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/DefaultModuleDefImplTest.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/ModuleImplTest.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/services/PropertyAccessImplTest.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/services/RegistryStartupTest.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/services/PropertyShadowBuilderImplTest.java

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/IOCInternalTestCase.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/IOCInternalTestCase.java?rev=595970&r1=595969&r2=595970&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/IOCInternalTestCase.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/IOCInternalTestCase.java Sat Nov 17 08:36:50 2007
@@ -91,6 +91,7 @@
         throw new UnsupportedOperationException("No registry shutdown until @AfterSuite.");
     }
 
+
     @AfterSuite
     public final void shutdown_registry()
     {
@@ -133,14 +134,13 @@
         expect(source.getDescription()).andReturn(description).atLeastOnce();
     }
 
-    protected final void train_getLifecycle(InternalRegistry registry, String scope,
-                                            ServiceLifecycle lifecycle)
+    protected final void train_getLifecycle(InternalRegistry registry, String scope, ServiceLifecycle lifecycle)
     {
         expect(registry.getServiceLifecycle(scope)).andReturn(lifecycle);
     }
 
-    protected final <T> void train_getService(InternalRegistry registry, String serviceId,
-                                              Class<T> serviceInterface, T service)
+    protected final <T> void train_getService(InternalRegistry registry, String serviceId, Class<T> serviceInterface,
+                                              T service)
     {
         expect(registry.getService(serviceId, serviceInterface)).andReturn(service);
     }

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/IOCMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/IOCMessages.java?rev=595970&r1=595969&r2=595970&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/IOCMessages.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/IOCMessages.java Sat Nov 17 08:36:50 2007
@@ -56,14 +56,10 @@
         return MESSAGES.get("builder-locked");
     }
 
-    static String serviceWrongInterface(String serviceId, Class actualInterface,
-                                        Class requestedInterface)
+    static String serviceWrongInterface(String serviceId, Class actualInterface, Class requestedInterface)
     {
-        return MESSAGES.format(
-                "service-wrong-interface",
-                serviceId,
-                actualInterface.getName(),
-                requestedInterface.getName());
+        return MESSAGES.format("service-wrong-interface", serviceId, actualInterface.getName(),
+                               requestedInterface.getName());
     }
 
     static String instantiateBuilderError(Class builderClass, Throwable cause)
@@ -107,11 +103,7 @@
             buffer.append(ids.get(i));
         }
 
-        return MESSAGES.format(
-                "many-service-matches",
-                serviceInterface.getName(),
-                ids.size(),
-                buffer.toString());
+        return MESSAGES.format("many-service-matches", serviceInterface.getName(), ids.size(), buffer.toString());
     }
 
     static String unknownScope(String name)
@@ -124,15 +116,10 @@
         return MESSAGES.format("decorator-method-needs-delegate-parameter", asString(method));
     }
 
-    static String decoratorReturnedWrongType(Method method, String serviceId, Object returned,
-                                             Class serviceInterface)
+    static String decoratorReturnedWrongType(Method method, String serviceId, Object returned, Class serviceInterface)
     {
-        return MESSAGES.format(
-                "decorator-returned-wrong-type",
-                asString(method),
-                serviceId,
-                returned,
-                serviceInterface.getName());
+        return MESSAGES.format("decorator-returned-wrong-type", asString(method), serviceId, returned,
+                               serviceInterface.getName());
     }
 
     static String creatingService(String serviceId)
@@ -163,10 +150,8 @@
 
     static String contributionWrongReturnType(Method method)
     {
-        return MESSAGES.format(
-                "contribution-wrong-return-type",
-                asString(method),
-                toJavaClassName(method.getReturnType()));
+        return MESSAGES.format("contribution-wrong-return-type", asString(method),
+                               toJavaClassName(method.getReturnType()));
     }
 
     static String tooManyContributionParameters(Method method)
@@ -194,22 +179,18 @@
         return MESSAGES.format("contribution-key-was-null", serviceId, def);
     }
 
-    static String contributionWrongValueType(String serviceId, ContributionDef def,
-                                             Class actualClass, Class expectedClass)
+    static String contributionWrongValueType(String serviceId, ContributionDef def, Class actualClass,
+                                             Class expectedClass)
     {
         return MESSAGES.format("contribution-wrong-value-type", serviceId, def, actualClass
                 .getName(), expectedClass.getName());
     }
 
-    static String contributionWrongKeyType(String serviceId, ContributionDef def,
-                                           Class actualClass, Class expectedClass)
+    static String contributionWrongKeyType(String serviceId, ContributionDef def, Class actualClass,
+                                           Class expectedClass)
     {
-        return MESSAGES.format(
-                "contribution-wrong-key-type",
-                serviceId,
-                def,
-                actualClass.getName(),
-                expectedClass.getName());
+        return MESSAGES.format("contribution-wrong-key-type", serviceId, def, actualClass.getName(),
+                               expectedClass.getName());
     }
 
     static String tooManyConfigurationParameters(String methodId)
@@ -225,11 +206,7 @@
     static String contributionDuplicateKey(String serviceId, ContributionDef contributionDef,
                                            ContributionDef existingDef)
     {
-        return MESSAGES.format(
-                "contribution-duplicate-key",
-                serviceId,
-                contributionDef,
-                existingDef);
+        return MESSAGES.format("contribution-duplicate-key", serviceId, contributionDef, existingDef);
     }
 
     static String errorBuildingService(String serviceId, ServiceDef serviceDef, Throwable cause)
@@ -244,10 +221,7 @@
 
     static String tooManyPublicConstructors(Class moduleBuilderClass, Constructor constructor)
     {
-        return MESSAGES.format(
-                "too-many-public-constructors",
-                moduleBuilderClass.getName(),
-                constructor);
+        return MESSAGES.format("too-many-public-constructors", moduleBuilderClass.getName(), constructor);
     }
 
     static String recursiveModuleConstructor(Class builderClass, Constructor constructor)
@@ -311,11 +285,25 @@
                 .toJavaClassName(objectType), ClassFabUtils.toJavaClassName(marker));
     }
 
-    static String manyServicesMatchMarker(Class objectType, Class marker,
-                                          Collection<ServiceDef> matchingServices)
+    static String manyServicesMatchMarker(Class objectType, Class marker, Collection<ServiceDef> matchingServices)
     {
         return MESSAGES.format("many-services-match-marker", ClassFabUtils
                 .toJavaClassName(objectType), ClassFabUtils.toJavaClassName(marker), InternalUtils
                 .joinSorted(matchingServices));
+    }
+
+    static String overlappingServiceProxyProviders()
+    {
+        return MESSAGES.get("overlapping-service-proxy-providers");
+    }
+
+    static String unexpectedServiceProxyProvider()
+    {
+        return MESSAGES.get("unexpected-service-proxy-provider");
+    }
+
+    static String noProxyProvider(String serviceId)
+    {
+        return MESSAGES.format("no-proxy-provider", serviceId);
     }
 }

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/ModuleImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/ModuleImpl.java?rev=595970&r1=595969&r2=595970&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/ModuleImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/ModuleImpl.java Sat Nov 17 08:36:50 2007
@@ -27,9 +27,12 @@
 import org.apache.tapestry.ioc.services.*;
 import org.slf4j.Logger;
 
+import java.io.ObjectStreamException;
+import java.io.Serializable;
 import static java.lang.String.format;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
 import java.util.*;
 
 public class ModuleImpl implements Module
@@ -64,8 +67,8 @@
      */
     private final Map<String, Object> _services = newCaseInsensitiveMap();
 
-    public ModuleImpl(InternalRegistry registry, ServiceActivityTracker tracker,
-                      ModuleDef moduleDef, ClassFactory classFactory, Logger logger)
+    public ModuleImpl(InternalRegistry registry, ServiceActivityTracker tracker, ModuleDef moduleDef,
+                      ClassFactory classFactory, Logger logger)
     {
         _registry = registry;
         _tracker = tracker;
@@ -199,8 +202,7 @@
 
         try
         {
-            ServiceBuilderResources resources = new ServiceResourcesImpl(_registry, this, def,
-                                                                         _classFactory, logger);
+            ServiceBuilderResources resources = new ServiceResourcesImpl(_registry, this, def, _classFactory, logger);
 
             // Build up a stack of operations that will be needed to realize the service
             // (by the proxy, at a later date).
@@ -215,8 +217,7 @@
 
             if (!serviceInterface.isInterface()) return creator.createObject();
 
-            creator = new LifecycleWrappedServiceCreator(_registry, def.getServiceScope(),
-                                                         resources, creator);
+            creator = new LifecycleWrappedServiceCreator(_registry, def.getServiceScope(), resources, creator);
 
             // Don't allow the core IOC services services to be decorated.
 
@@ -227,8 +228,7 @@
 
             creator = new RecursiveServiceCreationCheckWrapper(def, creator, logger);
 
-            JustInTimeObjectCreator delegate = new JustInTimeObjectCreator(_tracker, creator,
-                                                                           serviceId);
+            JustInTimeObjectCreator delegate = new JustInTimeObjectCreator(_tracker, creator, serviceId);
 
             Object proxy = createProxy(resources, delegate);
 
@@ -271,8 +271,7 @@
 
         Constructor[] constructors = builderClass.getConstructors();
 
-        if (constructors.length == 0)
-            throw new RuntimeException(IOCMessages.noPublicConstructors(builderClass));
+        if (constructors.length == 0) throw new RuntimeException(IOCMessages.noPublicConstructors(builderClass));
 
         if (constructors.length > 1)
         {
@@ -296,9 +295,7 @@
         Constructor constructor = constructors[0];
 
         if (_insideConstructor)
-            throw new RuntimeException(IOCMessages.recursiveModuleConstructor(
-                    builderClass,
-                    constructor));
+            throw new RuntimeException(IOCMessages.recursiveModuleConstructor(builderClass, constructor));
 
         ObjectLocator locator = new ObjectLocatorImpl(_registry, this);
         Map<Class, Object> parameterDefaults = newMap();
@@ -312,11 +309,9 @@
         {
             _insideConstructor = true;
 
-            Object[] parameterValues = InternalUtils.calculateParameters(
-                    locator,
-                    parameterDefaults,
-                    constructor.getParameterTypes(),
-                    constructor.getParameterAnnotations());
+            Object[] parameterValues = InternalUtils.calculateParameters(locator, parameterDefaults,
+                                                                         constructor.getParameterTypes(),
+                                                                         constructor.getParameterAnnotations());
 
             return constructor.newInstance(parameterValues);
         }
@@ -346,12 +341,52 @@
         return createProxyInstance(creator, serviceId, serviceInterface, toString);
     }
 
-    private Object createProxyInstance(ObjectCreator creator, String serviceId,
-                                       Class serviceInterface, String description)
+    private Object createProxyInstance(ObjectCreator creator, String serviceId, Class serviceInterface,
+                                       String description)
     {
-        ClassFab cf = _registry.newClass(serviceInterface);
+        ServiceProxyToken token = SerializationSupport.createToken(serviceId);
 
-        return ClassFabUtils.createObjectCreatorProxy(cf, serviceInterface, creator, description);
+        ClassFab classFab = _registry.newClass(serviceInterface);
+
+        classFab.addField("_creator", Modifier.PRIVATE | Modifier.FINAL, ObjectCreator.class);
+        classFab.addField("_token", Modifier.PRIVATE | Modifier.FINAL, ServiceProxyToken.class);
+
+        classFab.addConstructor(new Class[]{ObjectCreator.class, ServiceProxyToken.class}, null,
+                                "{ _creator = $1; _token = $2; }");
+
+        // Make proxies serializable by writing the token to the stream.
+
+        classFab.addInterface(Serializable.class);
+
+        // This is the "magic" signature that allows an object to substitute some other
+        // object for itself.
+        MethodSignature writeReplaceSig = new MethodSignature(Object.class, "writeReplace", null,
+                                                              new Class[]{ObjectStreamException.class});
+
+        classFab.addMethod(Modifier.PRIVATE, writeReplaceSig, "return _token;");
+
+        // Now delegate all the methods.
+
+        String body = format("return (%s) _creator.createObject();", serviceInterface.getName());
+
+        MethodSignature sig = new MethodSignature(serviceInterface, "_delegate", null, null);
+
+        classFab.addMethod(Modifier.PRIVATE, sig, body);
+
+        classFab.proxyMethodsToDelegate(serviceInterface, "_delegate()", description);
+
+        Class proxyClass = classFab.createClass();
+
+        try
+        {
+            return proxyClass.getConstructors()[0].newInstance(creator, token);
+        }
+        catch (Exception ex)
+        {
+            // Exceptions should not happen.
+
+            throw new RuntimeException(ex.getMessage(), ex);
+        }
     }
 
     public Set<ContributionDef> getContributorDefsForService(String serviceId)

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/RegistryImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/RegistryImpl.java?rev=595970&r1=595969&r2=595970&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/RegistryImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/RegistryImpl.java Sat Nov 17 08:36:50 2007
@@ -35,7 +35,7 @@
 import java.lang.reflect.InvocationTargetException;
 import java.util.*;
 
-public class RegistryImpl implements Registry, InternalRegistry
+public class RegistryImpl implements Registry, InternalRegistry, ServiceProxyProvider
 {
     private static final String SYMBOL_SOURCE_SERVICE_ID = "SymbolSource";
 
@@ -97,8 +97,8 @@
      */
     private final Map<Class, List<ServiceDef>> _markerToServiceDef = newMap();
 
-    public static final class OrderedConfigurationToOrdererAdaptor<T> implements
-                                                                      OrderedConfiguration<T>
+
+    public static final class OrderedConfigurationToOrdererAdaptor<T> implements OrderedConfiguration<T>
     {
         private final Orderer<T> _orderer;
 
@@ -120,8 +120,7 @@
      * @param classFactory TODO
      * @param loggerSource used to obtain Logger instances
      */
-    public RegistryImpl(Collection<ModuleDef> moduleDefs, ClassFactory classFactory,
-                        LoggerSource loggerSource)
+    public RegistryImpl(Collection<ModuleDef> moduleDefs, ClassFactory classFactory, LoggerSource loggerSource)
     {
         _loggerSource = loggerSource;
 
@@ -129,10 +128,7 @@
 
         _tracker = scoreboardAndTracker;
 
-        addBuiltin(
-                SERVICE_ACTIVITY_SCOREBOARD_SERVICE_ID,
-                ServiceActivityScoreboard.class,
-                scoreboardAndTracker);
+        addBuiltin(SERVICE_ACTIVITY_SCOREBOARD_SERVICE_ID, ServiceActivityScoreboard.class, scoreboardAndTracker);
 
         addBuiltin(LOG_SOURCE_SERVICE_ID, LoggerSource.class, _loggerSource);
 
@@ -150,10 +146,7 @@
 
         _registryShutdownHub = new RegistryShutdownHubImpl(logger);
 
-        addBuiltin(
-                REGISTRY_SHUTDOWN_HUB_SERVICE_ID,
-                RegistryShutdownHub.class,
-                _registryShutdownHub);
+        addBuiltin(REGISTRY_SHUTDOWN_HUB_SERVICE_ID, RegistryShutdownHub.class, _registryShutdownHub);
 
         _lifecycles.put("singleton", new SingletonServiceLifecycle());
 
@@ -179,9 +172,8 @@
 
                 Module existing = _serviceIdToModule.get(serviceId);
 
-                if (existing != null)
-                    throw new RuntimeException(IOCMessages.serviceIdConflict(serviceId, existing
-                            .getServiceDef(serviceId), serviceDef));
+                if (existing != null) throw new RuntimeException(IOCMessages.serviceIdConflict(serviceId, existing
+                        .getServiceDef(serviceId), serviceDef));
 
                 _serviceIdToModule.put(serviceId, module);
 
@@ -195,6 +187,8 @@
         }
 
         scoreboardAndTracker.startup();
+
+        SerializationSupport.setProvider(this);
     }
 
     /**
@@ -281,11 +275,10 @@
         _lock.lock();
 
         _registryShutdownHub.fireRegistryDidShutdown();
+
+        SerializationSupport.clearProvider(this);
     }
 
-    /**
-     * Internal access, usually from another module.
-     */
     public <T> T getService(String serviceId, Class<T> serviceInterface)
     {
         _lock.check();
@@ -329,9 +322,8 @@
     {
         Module module = _serviceIdToModule.get(serviceId);
 
-        if (module == null)
-            throw new RuntimeException(IOCMessages.noSuchService(serviceId, _serviceIdToModule
-                    .keySet()));
+        if (module == null) throw new RuntimeException(IOCMessages.noSuchService(serviceId, _serviceIdToModule
+                .keySet()));
 
         return module;
     }
@@ -382,8 +374,7 @@
         {
             ObjectProvider contribution = new ObjectProvider()
             {
-                public <T> T provide(Class<T> objectType, AnnotationProvider annotationProvider,
-                                     ObjectLocator locator)
+                public <T> T provide(Class<T> objectType, AnnotationProvider annotationProvider, ObjectLocator locator)
                 {
                     return findServiceByMarkerAndType(objectType, annotationProvider);
                 }
@@ -395,8 +386,7 @@
         return orderer.getOrdered();
     }
 
-    public <K, V> Map<K, V> getMappedConfiguration(ServiceDef serviceDef, Class<K> keyType,
-                                                   Class<V> objectType)
+    public <K, V> Map<K, V> getMappedConfiguration(ServiceDef serviceDef, Class<K> keyType, Class<V> objectType)
     {
         _lock.check();
 
@@ -416,13 +406,7 @@
         Collection<Module> modules = _modules;
 
         for (Module m : modules)
-            addToMappedConfiguration(
-                    configuration,
-                    keyToContribution,
-                    keyType,
-                    objectType,
-                    serviceDef,
-                    m);
+            addToMappedConfiguration(configuration, keyToContribution, keyType, objectType, serviceDef, m);
 
         return result;
     }
@@ -442,8 +426,7 @@
 
     private <K, V> void addToMappedConfiguration(MappedConfiguration<K, V> configuration,
                                                  Map<K, ContributionDef> keyToContribution, Class<K> keyClass,
-                                                 Class<V> valueType,
-                                                 ServiceDef serviceDef, Module module)
+                                                 Class<V> valueType, ServiceDef serviceDef, Module module)
     {
         String serviceId = serviceDef.getServiceId();
         Set<ContributionDef> contributions = module.getContributorDefsForService(serviceId);
@@ -454,13 +437,15 @@
 
         boolean debug = logger.isDebugEnabled();
 
-        ObjectLocator locator = new ServiceResourcesImpl(this, module, serviceDef, _classFactory,
-                                                         logger);
+        ObjectLocator locator = new ServiceResourcesImpl(this, module, serviceDef, _classFactory, logger);
 
         for (ContributionDef def : contributions)
         {
-            MappedConfiguration<K, V> validating = new ValidatingMappedConfigurationWrapper<K, V>(
-                    serviceId, def, logger, keyClass, valueType, keyToContribution, configuration);
+            MappedConfiguration<K, V> validating = new ValidatingMappedConfigurationWrapper<K, V>(serviceId, def,
+                                                                                                  logger, keyClass,
+                                                                                                  valueType,
+                                                                                                  keyToContribution,
+                                                                                                  configuration);
 
             if (debug) logger.debug(IOCMessages.invokingMethod(def));
 
@@ -469,8 +454,8 @@
 
     }
 
-    private <T> void addToUnorderedConfiguration(Configuration<T> configuration,
-                                                 Class<T> valueType, ServiceDef serviceDef, Module module)
+    private <T> void addToUnorderedConfiguration(Configuration<T> configuration, Class<T> valueType,
+                                                 ServiceDef serviceDef, Module module)
     {
         String serviceId = serviceDef.getServiceId();
         Set<ContributionDef> contributions = module.getContributorDefsForService(serviceId);
@@ -481,13 +466,12 @@
 
         boolean debug = logger.isDebugEnabled();
 
-        ObjectLocator locator = new ServiceResourcesImpl(this, module, serviceDef, _classFactory,
-                                                         logger);
+        ObjectLocator locator = new ServiceResourcesImpl(this, module, serviceDef, _classFactory, logger);
 
         for (ContributionDef def : contributions)
         {
-            Configuration<T> validating = new ValidatingConfigurationWrapper<T>(serviceId, logger,
-                                                                                valueType, def, configuration);
+            Configuration<T> validating = new ValidatingConfigurationWrapper<T>(serviceId, logger, valueType, def,
+                                                                                configuration);
 
             if (debug) logger.debug(IOCMessages.invokingMethod(def));
 
@@ -495,8 +479,8 @@
         }
     }
 
-    private <T> void addToOrderedConfiguration(OrderedConfiguration<T> configuration,
-                                               Class<T> valueType, ServiceDef serviceDef, Module module)
+    private <T> void addToOrderedConfiguration(OrderedConfiguration<T> configuration, Class<T> valueType,
+                                               ServiceDef serviceDef, Module module)
     {
         String serviceId = serviceDef.getServiceId();
         Set<ContributionDef> contributions = module.getContributorDefsForService(serviceId);
@@ -506,13 +490,12 @@
         Logger logger = getServiceLogger(serviceId);
         boolean debug = logger.isDebugEnabled();
 
-        ObjectLocator locator = new ServiceResourcesImpl(this, module, serviceDef, _classFactory,
-                                                         logger);
+        ObjectLocator locator = new ServiceResourcesImpl(this, module, serviceDef, _classFactory, logger);
 
         for (ContributionDef def : contributions)
         {
-            OrderedConfiguration<T> validating = new ValidatingOrderedConfigurationWrapper<T>(
-                    serviceId, def, logger, valueType, configuration);
+            OrderedConfiguration<T> validating = new ValidatingOrderedConfigurationWrapper<T>(serviceId, def, logger,
+                                                                                              valueType, configuration);
 
             if (debug) logger.debug(IOCMessages.invokingMethod(def));
 
@@ -544,9 +527,7 @@
 
                 Collections.sort(serviceIds);
 
-                throw new RuntimeException(IOCMessages.manyServiceMatches(
-                        serviceInterface,
-                        serviceIds));
+                throw new RuntimeException(IOCMessages.manyServiceMatches(serviceInterface, serviceIds));
         }
     }
 
@@ -575,9 +556,7 @@
 
         if (result == null)
         {
-            ServiceLifecycleSource source = getService(
-                    "ServiceLifecycleSource",
-                    ServiceLifecycleSource.class);
+            ServiceLifecycleSource source = getService("ServiceLifecycleSource", ServiceLifecycleSource.class);
             result = source.get(scope);
         }
 
@@ -602,8 +581,7 @@
 
             if (decorators.isEmpty()) continue;
 
-            ServiceResources resources = new ServiceResourcesImpl(this, module, serviceDef,
-                                                                  _classFactory, logger);
+            ServiceResources resources = new ServiceResourcesImpl(this, module, serviceDef, _classFactory, logger);
 
             for (DecoratorDef dd : decorators)
             {
@@ -623,13 +601,11 @@
         return _classFactory.newClass(serviceInterface);
     }
 
-    private <T> T getObject(Class<T> objectType, AnnotationProvider annotationProvider,
-                            ObjectLocator locator)
+    private <T> T getObject(Class<T> objectType, AnnotationProvider annotationProvider, ObjectLocator locator)
     {
         _lock.check();
 
-        AnnotationProvider effectiveProvider = annotationProvider != null ? annotationProvider
-                                               : new NullAnnotationProvider();
+        AnnotationProvider effectiveProvider = annotationProvider != null ? annotationProvider : new NullAnnotationProvider();
 
         // We do a check here for known marker/type combinations, so that you can use a marker
         // annotation
@@ -640,9 +616,8 @@
 
         if (result != null) return result;
 
-        MasterObjectProvider masterProvider = getService(
-                IOCConstants.MASTER_OBJECT_PROVIDER_SERVICE_ID,
-                MasterObjectProvider.class);
+        MasterObjectProvider masterProvider = getService(IOCConstants.MASTER_OBJECT_PROVIDER_SERVICE_ID,
+                                                         MasterObjectProvider.class);
 
         return masterProvider.provide(objectType, effectiveProvider, locator, true);
     }
@@ -686,10 +661,7 @@
                             .noServicesMatchMarker(objectType, marker));
 
                 default:
-                    throw new RuntimeException(IOCMessages.manyServicesMatchMarker(
-                            objectType,
-                            marker,
-                            matches));
+                    throw new RuntimeException(IOCMessages.manyServicesMatchMarker(objectType, marker, matches));
             }
 
         }
@@ -727,8 +699,7 @@
      */
     private synchronized SymbolSource getSymbolSource()
     {
-        if (_symbolSource == null)
-            _symbolSource = getService(SYMBOL_SOURCE_SERVICE_ID, SymbolSource.class);
+        if (_symbolSource == null) _symbolSource = getService(SYMBOL_SOURCE_SERVICE_ID, SymbolSource.class);
 
         return _symbolSource;
     }
@@ -739,8 +710,7 @@
 
         Constructor constructor = InternalUtils.findAutobuildConstructor(clazz);
 
-        if (constructor == null)
-            throw new RuntimeException(IOCMessages.noAutobuildConstructor(clazz));
+        if (constructor == null) throw new RuntimeException(IOCMessages.noAutobuildConstructor(clazz));
 
         Throwable failure = null;
         // An empty map, because when performing autobuilding outside the context of building a
@@ -750,10 +720,7 @@
 
         try
         {
-            Object[] parameters = InternalUtils.calculateParametersForConstructor(
-                    constructor,
-                    this,
-                    empty);
+            Object[] parameters = InternalUtils.calculateParametersForConstructor(constructor, this, empty);
 
             return clazz.cast(constructor.newInstance(parameters));
         }
@@ -768,8 +735,7 @@
 
         String description = _classFactory.getConstructorLocation(constructor).toString();
 
-        throw new RuntimeException(IOCMessages.autobuildConstructorError(description, failure),
-                                   failure);
+        throw new RuntimeException(IOCMessages.autobuildConstructorError(description, failure), failure);
     }
 
     public <T> T proxy(Class<T> interfaceClass, final Class<? extends T> implementationClass)
@@ -806,5 +772,10 @@
                 .getName(), interfaceClass.getName());
 
         return ClassFabUtils.createObjectCreatorProxy(cf, interfaceClass, justInTime, description);
+    }
+
+    public Object provideServiceProxy(String serviceId)
+    {
+        return getService(serviceId, Object.class);
     }
 }

Added: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/SerializationSupport.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/SerializationSupport.java?rev=595970&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/SerializationSupport.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/SerializationSupport.java Sat Nov 17 08:36:50 2007
@@ -0,0 +1,67 @@
+package org.apache.tapestry.ioc.internal;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Serialization support for service proxies.
+ */
+class SerializationSupport
+{
+    private static final Logger LOGGER = LoggerFactory.getLogger(SerializationSupport.class);
+
+    // We use a weak reference so that the underlying Registry can be reclaimed by the garbage collector
+    // even if it is not explicitly shut down.
+
+    private static WeakReference<ServiceProxyProvider> _providerRef;
+
+    static synchronized void setProvider(ServiceProxyProvider proxyProvider)
+    {
+        ServiceProxyProvider existing = currentProvider();
+
+        if (existing != null) LOGGER.error(IOCMessages.overlappingServiceProxyProviders());
+
+        _providerRef = new WeakReference<ServiceProxyProvider>(proxyProvider);
+    }
+
+    private static ServiceProxyProvider currentProvider()
+    {
+        return _providerRef == null ? null : _providerRef.get();
+    }
+
+    static synchronized void clearProvider(ServiceProxyProvider proxyProvider)
+    {
+        ServiceProxyProvider existing = currentProvider();
+
+        // The registry does a setProvider() at startup, and we want to make sure that we're the only Registry, that
+        // there hasn't been another setProvider() by another Registry.
+
+        if (existing != proxyProvider)
+        {
+            LOGGER.error(IOCMessages.unexpectedServiceProxyProvider());
+            return;
+        }
+
+        // Good. It's all the expected simple case, without duelling registries. Kill the reference
+        // to the registry.
+
+        _providerRef = null;
+    }
+
+    static synchronized Object readResolve(String serviceId)
+    {
+        ServiceProxyProvider provider = currentProvider();
+
+        if (provider == null) throw new RuntimeException(IOCMessages.noProxyProvider(serviceId));
+
+        return provider.provideServiceProxy(serviceId);
+    }
+
+    static ServiceProxyToken createToken(String serviceId)
+    {
+        return new ServiceProxyToken(serviceId);
+    }
+
+}
\ No newline at end of file

Added: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/ServiceProxyProvider.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/ServiceProxyProvider.java?rev=595970&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/ServiceProxyProvider.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/ServiceProxyProvider.java Sat Nov 17 08:36:50 2007
@@ -0,0 +1,17 @@
+package org.apache.tapestry.ioc.internal;
+
+/**
+ * Used in concert with {@link org.apache.tapestry.ioc.internal.SerializationSupport} to
+ * convert service tokens back into service proxies.
+ */
+public interface ServiceProxyProvider
+{
+    /**
+     * Look up the service and return it's proxy.
+     *
+     * @param serviceId the id of the service to obtain
+     * @return the service proxy
+     * @throws RuntimeException if the service does not exist or does not have a proxy
+     */
+    Object provideServiceProxy(String serviceId);
+}

Added: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/ServiceProxyToken.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/ServiceProxyToken.java?rev=595970&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/ServiceProxyToken.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/ServiceProxyToken.java Sat Nov 17 08:36:50 2007
@@ -0,0 +1,34 @@
+package org.apache.tapestry.ioc.internal;
+
+import java.io.InvalidObjectException;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+
+/**
+ * Token that replaces a service proxy when the proxy is serialized.
+ */
+class ServiceProxyToken implements Serializable
+{
+    private final String _serviceId;
+
+    ServiceProxyToken(String serviceId)
+    {
+        _serviceId = serviceId;
+    }
+
+    Object readResolve() throws ObjectStreamException
+    {
+        try
+        {
+            return SerializationSupport.readResolve(_serviceId);
+        }
+        catch (Exception ex)
+        {
+            ObjectStreamException ose = new InvalidObjectException(ex.getMessage());
+            ose.initCause(ex);
+
+            throw ose;
+        }
+    }
+
+}

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/test/IOCTestCase.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/test/IOCTestCase.java?rev=595970&r1=595969&r2=595970&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/test/IOCTestCase.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/test/IOCTestCase.java Sat Nov 17 08:36:50 2007
@@ -36,6 +36,10 @@
  */
 public class IOCTestCase extends TestBase
 {
+
+    /**
+     * Builds a Registry for the provided modules; caller should shutdown the Registry when done.
+     */
     protected final Registry buildRegistry(Class... moduleClasses)
     {
         RegistryBuilder builder = new RegistryBuilder();
@@ -52,10 +56,8 @@
             if (method.getName().equals(methodName)) return method;
         }
 
-        throw new IllegalArgumentException(String.format(
-                "Class %s does not provide a method named '%s'.",
-                clazz.getName(),
-                methodName));
+        throw new IllegalArgumentException(
+                String.format("Class %s does not provide a method named '%s'.", clazz.getName(), methodName));
     }
 
     protected final Method findMethod(Object subject, String methodName)
@@ -210,8 +212,7 @@
         expect(messages.contains(isA(String.class))).andStubReturn(contained);
     }
 
-    protected <S, T> void train_coerce(TypeCoercer coercer, S input, Class<T> expectedType,
-                                       T coercedValue)
+    protected <S, T> void train_coerce(TypeCoercer coercer, S input, Class<T> expectedType, T coercedValue)
     {
         expect(coercer.coerce(input, expectedType)).andReturn(coercedValue);
     }
@@ -221,8 +222,7 @@
         expect(messages.contains(key)).andReturn(result).atLeastOnce();
     }
 
-    protected final void train_createInterceptor(ServiceDecorator decorator, Object coreObject,
-                                                 Object interceptor)
+    protected final void train_createInterceptor(ServiceDecorator decorator, Object coreObject, Object interceptor)
     {
         expect(decorator.createInterceptor(coreObject)).andReturn(interceptor);
     }
@@ -276,8 +276,7 @@
         expect(source.getLogger(serviceId)).andReturn(logger).atLeastOnce();
     }
 
-    protected final void train_getMessageFormatter(Messages messages, String key,
-                                                   MessageFormatter formatter)
+    protected final void train_getMessageFormatter(Messages messages, String key, MessageFormatter formatter)
     {
         expect(messages.getFormatter(key)).andReturn(formatter).atLeastOnce();
     }
@@ -287,14 +286,13 @@
         expect(r.getPath()).andReturn(path).atLeastOnce();
     }
 
-    protected final <T> void train_getService(ObjectLocator locator, Class<T> serviceInterface,
-                                              T service)
+    protected final <T> void train_getService(ObjectLocator locator, Class<T> serviceInterface, T service)
     {
         expect(locator.getService(serviceInterface)).andReturn(service);
     }
 
-    protected final <T> void train_getService(ObjectLocator locator, String serviceId,
-                                              Class<T> serviceInterface, T service)
+    protected final <T> void train_getService(ObjectLocator locator, String serviceId, Class<T> serviceInterface,
+                                              T service)
     {
         expect(locator.getService(serviceId, serviceInterface)).andReturn(service);
     }
@@ -315,8 +313,7 @@
         expect(def.getServiceInterface()).andReturn(serviceInterface).atLeastOnce();
     }
 
-    protected final void train_getServiceInterface(ServiceResources resources,
-                                                   Class serviceInterface)
+    protected final void train_getServiceInterface(ServiceResources resources, Class serviceInterface)
     {
         expect(resources.getServiceInterface()).andReturn(serviceInterface).atLeastOnce();
     }
@@ -337,8 +334,7 @@
         expect(log.isTraceEnabled()).andReturn(traceEnabled);
     }
 
-    protected final void train_matches(DecoratorDef decoratorDef, ServiceDef serviceDef,
-                                       boolean matches)
+    protected final void train_matches(DecoratorDef decoratorDef, ServiceDef serviceDef, boolean matches)
     {
         expect(decoratorDef.matches(serviceDef)).andReturn(matches);
     }
@@ -354,8 +350,8 @@
         expect(resource.toURL()).andReturn(url).atLeastOnce();
     }
 
-    protected final <T extends Annotation> void train_getAnnotation(
-            AnnotationProvider annotationProvider, Class<T> annotationClass, T annotation)
+    protected final <T extends Annotation> void train_getAnnotation(AnnotationProvider annotationProvider,
+                                                                    Class<T> annotationClass, T annotation)
     {
         expect(annotationProvider.getAnnotation(annotationClass)).andReturn(annotation);
     }

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/resources/org/apache/tapestry/ioc/internal/IOCStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/resources/org/apache/tapestry/ioc/internal/IOCStrings.properties?rev=595970&r1=595969&r2=595970&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/resources/org/apache/tapestry/ioc/internal/IOCStrings.properties (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/resources/org/apache/tapestry/ioc/internal/IOCStrings.properties Sat Nov 17 08:36:50 2007
@@ -12,68 +12,68 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-build-method-conflict=Service building method %s conflicts with (has the same name as, but different parameters than) \
-  previously seen method %s and has been ignored.
-build-method-wrong-return-type=Method %s is named like a service builder method, \
- but the return type (%s) is not acceptable (try an interface). \
- The method has been ignored.
-decorator-method-wrong-return-type=Method %s is named like a service decorator method, \
-  but the return type (%s) is not acceptable (try Object). The method has been ignored.
-builder-locked=The Registry Builder has created the Registry, further operations are not allowed.
-service-wrong-interface=Service '%s' implements interface %s, which is not compatible with the requested type %s.
-instantiate-builder-error=Unable to instantiate class %s as a module builder: %s
+build-method-conflict=Service building method %s conflicts with (has the same name as, but different parameters than) \
+  previously seen method %s and has been ignored.
+build-method-wrong-return-type=Method %s is named like a service builder method, \
+ but the return type (%s) is not acceptable (try an interface). \
+ The method has been ignored.
+decorator-method-wrong-return-type=Method %s is named like a service decorator method, \
+  but the return type (%s) is not acceptable (try Object). The method has been ignored.
+builder-locked=The Registry Builder has created the Registry, further operations are not allowed.
+service-wrong-interface=Service '%s' implements interface %s, which is not compatible with the requested type %s.
+instantiate-builder-error=Unable to instantiate class %s as a module builder: %s
 builder-method-error=Error invoking service builder method %s (for service '%s'): %s
-constructor-error=Error invoking constructor %s (for service '%s'): %s
-decorator-method-error=Error invoking service decorator method %s (for service '%s'): %s
-builder-method-returned-null=Builder method %s (for service '%s') returned null.
-no-service-matches-type=No service implements the interface %s.
-many-service-matches=Service interface %s is matched by %d services: %s.  \
+constructor-error=Error invoking constructor %s (for service '%s'): %s
+decorator-method-error=Error invoking service decorator method %s (for service '%s'): %s
+builder-method-returned-null=Builder method %s (for service '%s') returned null.
+no-service-matches-type=No service implements the interface %s.
+many-service-matches=Service interface %s is matched by %d services: %s.  \
   Automatic dependency resolution requires that exactly one service implement the interface.
 no-services-match-marker=Unable to locate any service assignable to type %s with marker annotation %s.
 many-services-match-marker=Unable to locate a single service assignable to type %s with marker annotation %s.  \
- All of the following services match: %s.
-unknown-scope=Unknown service scope '%s'.
-decorator-method-needs-delegate-parameter=Decorator methods must a parameter for the service delegate \
- (i.e., the object the created interceptor will delegate to). \
- Method %s does not include such a parameter, and has been ignored.
-decorator-returned-wrong-type=Decorator method %s (invoked for service '%s') returned %s, \
-  which is not assignable to the %s service interface.
-creating-service=Creating service '%s'.
+ All of the following services match: %s.
+unknown-scope=Unknown service scope '%s'.
+decorator-method-needs-delegate-parameter=Decorator methods must a parameter for the service delegate \
+ (i.e., the object the created interceptor will delegate to). \
+ Method %s does not include such a parameter, and has been ignored.
+decorator-returned-wrong-type=Decorator method %s (invoked for service '%s') returned %s, \
+  which is not assignable to the %s service interface.
+creating-service=Creating service '%s'.
 invoking-method=Invoking method %s.
-invoking-constructor=Invoking constructor %s.
-recursive-service-build=Construction of service '%s' has failed due to recursion: \
-  the service depends on itself in some way. \
-  Please check %s for references to another service that is itself dependent on service '%1$s'.
-contribution-wrong-return-type=Method %s is named like a service contributor method, \
-  but the return type (%s) is not appropriate (try void). The return value will be ignored.
-too-many-contribution-parameters=Service contribution method %s contains more than one parameter of type Configuration, \
-  OrderedConfiguration, or MappedConfiguration. Exactly one such parameter is required for a service contribution method. \
-  The method has been ignored.
-too-many-configuration-parameters=Service builder method %s contains more than one parameter of type \
-  Collection, List, or Map. Parameters of this type are the way in which service configuration values, \
-  collected from service contributor methods, are provided to the service builder. \
-  Services are only allowed a single configuration. Unexpected results or failures may occur.
-no-contribution-parameter=Service contribution method %s does not contain a parameter of type \
-  Configuration, OrderedConfiguration or MappedConfiguration. This parameter is how the method \
-  make contributions into the service's configuration. The method has been ignored.
-contribution-method-error=Error invoking service contribution method %s: %s
-contribution-was-null=Service contribution (to service '%s', by %s) was null and has been ignored.
-contribution-key-was-null=Key for service contribution (to service '%s', by %s) was null and has been ignored.
-contribution-wrong-value-type=Service contribution (to service '%s', by %s) was an instance of %s, \
-  but %s was expected. The contribution has been ignored.
-contribution-wrong-key-type=Key for service contribution (to service '%s', by %s) was an instance of %s, \
-  but %s was expected. The contribution has been ignored.
-contribution-duplicate-key=Service contribution (to service '%s', by %s) conflicts with \
-  existing contribution (by %s) and has been ignored.
-generic-type-not-supported=Generic type '%s' is not supported. Only simple parameterized lists are \
-  supported.
-error-building-service=Error building service proxy for service '%s' (at %s): %s
-no-public-constructors=Module builder class %s does not contain any public constructors.
-too-many-public-constructors=Module bulider class %s contains more than one public constructor. \
-  The first constructor, %s, is being used. \
-  You should change the class to have only a single public constructor.
-recursive-module-constructor=The constructor for module class %s is recursive: it depends on itself in some way. \
-  The constructor, %s, is in some way is triggering a service builder, decorator or contribution method within the class.
+invoking-constructor=Invoking constructor %s.
+recursive-service-build=Construction of service '%s' has failed due to recursion: \
+  the service depends on itself in some way. \
+  Please check %s for references to another service that is itself dependent on service '%1$s'.
+contribution-wrong-return-type=Method %s is named like a service contributor method, \
+  but the return type (%s) is not appropriate (try void). The return value will be ignored.
+too-many-contribution-parameters=Service contribution method %s contains more than one parameter of type Configuration, \
+  OrderedConfiguration, or MappedConfiguration. Exactly one such parameter is required for a service contribution method. \
+  The method has been ignored.
+too-many-configuration-parameters=Service builder method %s contains more than one parameter of type \
+  Collection, List, or Map. Parameters of this type are the way in which service configuration values, \
+  collected from service contributor methods, are provided to the service builder. \
+  Services are only allowed a single configuration. Unexpected results or failures may occur.
+no-contribution-parameter=Service contribution method %s does not contain a parameter of type \
+  Configuration, OrderedConfiguration or MappedConfiguration. This parameter is how the method \
+  make contributions into the service's configuration. The method has been ignored.
+contribution-method-error=Error invoking service contribution method %s: %s
+contribution-was-null=Service contribution (to service '%s', by %s) was null and has been ignored.
+contribution-key-was-null=Key for service contribution (to service '%s', by %s) was null and has been ignored.
+contribution-wrong-value-type=Service contribution (to service '%s', by %s) was an instance of %s, \
+  but %s was expected. The contribution has been ignored.
+contribution-wrong-key-type=Key for service contribution (to service '%s', by %s) was an instance of %s, \
+  but %s was expected. The contribution has been ignored.
+contribution-duplicate-key=Service contribution (to service '%s', by %s) conflicts with \
+  existing contribution (by %s) and has been ignored.
+generic-type-not-supported=Generic type '%s' is not supported. Only simple parameterized lists are \
+  supported.
+error-building-service=Error building service proxy for service '%s' (at %s): %s
+no-public-constructors=Module builder class %s does not contain any public constructors.
+too-many-public-constructors=Module bulider class %s contains more than one public constructor. \
+  The first constructor, %s, is being used. \
+  You should change the class to have only a single public constructor.
+recursive-module-constructor=The constructor for module class %s is recursive: it depends on itself in some way. \
+  The constructor, %s, is in some way is triggering a service builder, decorator or contribution method within the class.
 constructed-configuration=Constructed configuration: %s
 service-construction-failed=Construction of service %s failed: %s
 no-such-service=Service id '%s' is not defined by any module.  Defined services: %s.
@@ -84,4 +84,6 @@
  error-in-bind-method=Error invoking service binder method %s: %s
 no-autobuild-constructor=Class %s does not contain a public constructor needed to autobuild.
 autobuild-constructor-error=Error invoking constructor %s: %s
- 
\ No newline at end of file
+overlapping-service-proxy-providers=Setting a new service proxy provider when there's already an existing provider. This may indicate that you have multiple IoC Registries.
+unexpected-service-proxy-provider=Unexpected service proxy provider when clearing the provider. This may indicate that you have multiple IoC Registries.
+no-proxy-provider=Service token for service '%s' can not be converted back into a proxy because no proxy provider has been registered. This may indicate that an IoC Registry has not been started yet.
\ No newline at end of file

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/resources/org/apache/tapestry/ioc/internal/services/ServiceStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/resources/org/apache/tapestry/ioc/internal/services/ServiceStrings.properties?rev=595970&r1=595969&r2=595970&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/resources/org/apache/tapestry/ioc/internal/services/ServiceStrings.properties (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/resources/org/apache/tapestry/ioc/internal/services/ServiceStrings.properties Sat Nov 17 08:36:50 2007
@@ -12,31 +12,31 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-unable-to-add-method=Unable to add method %s to class %s: %s
-unable-to-add-field=Unable to add field %s to class %s: %s
-unable-to-add-constructor=Unable to add constructor to class %s: %s
-unable-to-create-class=Unable to create class %s as subclass of %s: %s
-unable-to-lookup-class=Unable to lookup class %s: %s
-unable-to-write-class=Unable to create class %s: %s
-duplicate-method-in-class=Attempt to redefine method %s of class %s.
-logging-interceptor=<Logging interceptor for %s(%s)>
-thread-cleanup-error=Error invoking listener %s: %s
-no-such-property=Class %s does not contain a property named '%s'.
-read-not-supported=Class %s does not provide an accessor ('getter') method for property '%s'.
-write-not-supported=Class %s does not provide an mutator ('setter') method for property '%s'.
-read-failure=Error reading property '%s' of %s: %s
-write-failure=Error updating property '%s' of %s: %s
-property-type-mismatch=Property '%s' of class %s is of type %s, which is not assignable to type %s.
-extra-filter-method=Method %s of filter interface %s does not have a matching method in %s.
-unmatched-service-method=Method %s has no match in filter interface %s.
-unknown-object-provider=Object provider '%s' does not exist (in object reference '%s').
-shutdown-listener-error=Error notifying %s of registry shutdown: %s
+unable-to-add-method=Unable to add method %s to class %s: %s
+unable-to-add-field=Unable to add field %s to class %s: %s
+unable-to-add-constructor=Unable to add constructor to class %s: %s
+unable-to-create-class=Unable to create class %s as subclass of %s: %s
+unable-to-lookup-class=Unable to lookup class %s: %s
+unable-to-write-class=Unable to create class %s: %s
+duplicate-method-in-class=Attempt to redefine method %s of class %s.
+logging-interceptor=<Logging interceptor for %s(%s)>
+thread-cleanup-error=Error invoking listener %s: %s
+no-such-property=Class %s does not contain a property named '%s'.
+read-not-supported=Class %s does not provide an accessor ('getter') method for property '%s'.
+write-not-supported=Class %s does not provide an mutator ('setter') method for property '%s'.
+read-failure=Error reading property '%s' of %s: %s
+write-failure=Error updating property '%s' of %s: %s
+property-type-mismatch=Property '%s' of class %s is of type %s, which is not assignable to type %s.
+extra-filter-method=Method %s of filter interface %s does not have a matching method in %s.
+unmatched-service-method=Method %s has no match in filter interface %s.
+unknown-object-proxyProvider=Object proxyProvider '%s' does not exist (in object reference '%s').
+shutdown-listener-error=Error notifying %s of registry shutdown: %s
 no-coercion-found=Could not find a coercion from type %s to type %s.  Available coercions: %s.
 recursive-symbol=Symbol '%s' is defined in terms of itself (%s).
 symbol-undefined=Symbol '%s' is not defined.
 symbol-undefined-in-path=Symbol '%s' is not defined (in %s). 
 missing-symbol-close-brace=Input string '%s' is missing a symbol closing brace.
 missing-symbol-close-brace-in-path=Input string '%s' is missing a symbol closing brace (in %s).
-failed-coercion=Coercion of %s to type %s (via %s) failed: %s
+failed-coercion=Coercion of %s to type %s (via %s) failed: %s
 registry-shutdown=Proxy for service %s is no longer active because the IOC Registry has been shut down.
 service-build-failure=Exception constructing service '%s': %s

Added: tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/serialization.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/serialization.apt?rev=595970&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/serialization.apt (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/serialization.apt Sat Nov 17 08:36:50 2007
@@ -0,0 +1,51 @@
+ ----
+ Service Serialization
+ ----
+
+Service Serialization
+
+  Every once in a while you may need to serialize a service.  For example, you may store an object into
+  the HttpSession that holds a reference to a service.  In a clustered environment, that object will
+  be serialized and broadcast to other servers in the cluster.
+
+  Services in Tapestry are serializable.  Specifically, service <proxies> are serializable.
+
+  Your service implementations <do not> have to be serializable.
+
+  Serialization works as follows:
+
+  * When a proxy is serialized, it instead serializes a <token> object.
+
+  * The token object is what's stored in the output stream.
+
+  * When the token is de-serialized, it locates the service proxy in the current Registry and returns that.
+
+  []
+
+  The end result is very efficient: just the tiny tokens are serialized, not the services with their
+  proxies, configurations, implementations, dependencies, internal state and so forth.
+
+  Again, note that the actual service implementation is not serialized.  Due to Tapestry's lazy  creation policy,
+  the service implementation may not even exist.  Since outside code only sees the proxy, there's no
+  difference.
+
+Registry Resolution
+
+  The one trick here is locating the service proxy.  Tapestry uses a <weak reference> to the Registry to do this.
+  When a Registry starts up, it is stored in the reference, so that de-serialization can work.
+
+  The reference is cleared when you shut down the Registry.  If you stop using the Registry, but fail to
+  shut it down, the weak reference ensures that it will be released to the garbage collector anyway.  Stil, you
+  should shutdown a Registry when done with it.
+
+  This all makes one BIG assumption: that there's just one Registry.  That's normal for a web application, especially
+  when the tapestry-ioc JAR is included as part of the web application's WAR.
+
+  If you are running multiple Registries you will likely see errors in your console:
+
++---+
+[ERROR] SerializationSupport Setting a new service proxy provider when there's already an existing provider. This may indicate that you have multiple IoC Registries.
++---+
+
+
+

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/site/site.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/site/site.xml?rev=595970&r1=595969&r2=595970&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/site/site.xml (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/site/site.xml Sat Nov 17 08:36:50 2007
@@ -53,6 +53,7 @@
             <item name="Decorators" href="decorator.html"/>
             <item name="Configuration" href="configuration.html"/>
             <item name="Type Coercion" href="coerce.html"/>
+            <item name="Serialization" href="serialization.html"/>
             <item name="Case Insensitivity" href="case.html"/>
             <item name="Symbols" href="symbols.html"/>
             <item name="Starting the Registry" href="run.html"/>

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java?rev=595970&r1=595969&r2=595970&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java Sat Nov 17 08:36:50 2007
@@ -66,6 +66,8 @@
 
         assertFalse(StaticModule.isInstantiated());
         assertTrue(StaticModule.getFredRan());
+
+        r.shutdown();
     }
 
     @Test
@@ -82,6 +84,8 @@
 
         assertFalse(StaticModule.isInstantiated());
         assertTrue(StaticModule.getDecoratorRan());
+
+        r.shutdown();
     }
 
     @Test
@@ -98,6 +102,8 @@
         assertEquals(names, Arrays.asList("Fred"));
 
         assertFalse(StaticModule.isInstantiated());
+
+        r.shutdown();
     }
 
     @Test
@@ -118,9 +124,8 @@
         }
         catch (IllegalStateException ex)
         {
-            assertEquals(
-                    ex.getMessage(),
-                    "Proxy for service Fred is no longer active because the IOC Registry has been shut down.");
+            assertEquals(ex.getMessage(),
+                         "Proxy for service Fred is no longer active because the IOC Registry has been shut down.");
         }
 
         // Show that toString() still works, even for a shutdown proxy.
@@ -208,6 +213,8 @@
         // Random objects are size 1
 
         assertEquals(sizer.size(this), 1);
+
+        r.shutdown();
     }
 
     @Test
@@ -225,11 +232,10 @@
         }
         catch (Exception ex)
         {
-            assertMessageContains(
-                    ex,
-                    "Exception constructing service 'UnknownScope'",
-                    "Unknown service scope 'magic'");
+            assertMessageContains(ex, "Exception constructing service 'UnknownScope'", "Unknown service scope 'magic'");
         }
+
+        r.shutdown();
     }
 
     @Test
@@ -269,6 +275,8 @@
         assertEquals(holder.getValue(), "fred");
 
         r.cleanupThread();
+
+        r.shutdown();
     }
 
     /**
@@ -315,6 +323,8 @@
         {
             assertTrue(ex.getMessage().contains("has failed due to recursion"));
         }
+
+        r.shutdown();
     }
 
     @Test
@@ -322,13 +332,13 @@
     {
         Registry r = buildRegistry(EagerLoadModule.class);
 
-        assertFalse(
-                EagerLoadModule._eagerLoadDidHappen,
-                "EagerLoadModule is not in correct initial state.");
+        assertFalse(EagerLoadModule._eagerLoadDidHappen, "EagerLoadModule is not in correct initial state.");
 
         r.performRegistryStartup();
 
         assertTrue(EagerLoadModule._eagerLoadDidHappen);
+
+        r.shutdown();
     }
 
     @Test
@@ -339,6 +349,8 @@
         Runnable fred = r.getService("Fred", Runnable.class);
 
         assertSame(r.getService("FRED", Runnable.class), fred);
+
+        r.shutdown();
     }
 
     @Test
@@ -351,6 +363,8 @@
         sh.setValue("Foo");
 
         assertEquals(sh.getValue(), "Foo");
+
+        r.shutdown();
     }
 
     @Test
@@ -367,13 +381,12 @@
         }
         catch (RuntimeException ex)
         {
-            assertMessageContains(
-                    ex,
-                    "Error invoking constructor",
-                    "ExceptionInConstructorServiceImpl() (at ExceptionInConstructorServiceImpl.java",
-                    "for service 'Pingable'",
-                    "Yes, we have no tomatoes.");
+            assertMessageContains(ex, "Error invoking constructor",
+                                  "ExceptionInConstructorServiceImpl() (at ExceptionInConstructorServiceImpl.java",
+                                  "for service 'Pingable'", "Yes, we have no tomatoes.");
         }
+
+        r.shutdown();
     }
 
     @Test
@@ -387,6 +400,8 @@
         StringHolder holder = r.getService(StringHolder.class);
 
         assertTrue(holder instanceof StringHolderImpl);
+
+        r.shutdown();
     }
 
     @Test
@@ -401,6 +416,8 @@
         holder.setValue("Foo");
 
         assertEquals(holder.getValue(), "Foo");
+
+        r.shutdown();
     }
 
     @Test
@@ -417,6 +434,8 @@
         holder.setValue("Foo");
 
         assertEquals(holder.getValue(), "Foo");
+
+        r.shutdown();
     }
 
     @Test
@@ -438,15 +457,15 @@
         }
         catch (RuntimeException ex)
         {
-            assertMessageContains(
-                    ex,
-                    "Class org.apache.tapestry.ioc.UnbuildablePingable does not contain a public constructor needed to autobuild.");
+            assertMessageContains(ex,
+                                  "Class org.apache.tapestry.ioc.UnbuildablePingable does not contain a public constructor needed to autobuild.");
 
             // Like to check that the message includes the source location
 
-            assertTrue(ex.getMessage().matches(
-                    ".*\\(at ServiceBuilderAutobuilderModule.java:\\d+\\).*"));
+            assertTrue(ex.getMessage().matches(".*\\(at ServiceBuilderAutobuilderModule.java:\\d+\\).*"));
         }
+
+        r.shutdown();
     }
 
     @Test
@@ -462,10 +481,11 @@
         }
         catch (RuntimeException ex)
         {
-            assertMessageContains(
-                    ex,
-                    "Class org.apache.tapestry.ioc.UnbuildablePingable does not contain a public constructor needed to autobuild.");
+            assertMessageContains(ex,
+                                  "Class org.apache.tapestry.ioc.UnbuildablePingable does not contain a public constructor needed to autobuild.");
         }
+
+        r.shutdown();
     }
 
     @Test
@@ -481,15 +501,15 @@
         }
         catch (RuntimeException ex)
         {
-            assertMessageContains(
-                    ex,
-                    "Error invoking constructor org.apache.tapestry.ioc.FailInConstructorRunnable()",
-                    "Failure in Runnable constructor.");
+            assertMessageContains(ex, "Error invoking constructor org.apache.tapestry.ioc.FailInConstructorRunnable()",
+                                  "Failure in Runnable constructor.");
 
             // Like to check that the message includes the source location
 
             assertTrue(ex.getMessage().matches(".*\\(at FailInConstructorRunnable.java:\\d+\\).*"));
         }
+
+        r.shutdown();
     }
 
     @Test
@@ -506,6 +526,8 @@
         {
             assertMessageContains(ex, "Service id \'PeekABoo\' is not defined by any module.");
         }
+
+        r.shutdown();
     }
 
     @Test
@@ -521,10 +543,10 @@
         }
         catch (RuntimeException ex)
         {
-            assertEquals(
-                    ex.getMessage(),
-                    "No service implements the interface java.sql.PreparedStatement.");
+            assertEquals(ex.getMessage(), "No service implements the interface java.sql.PreparedStatement.");
         }
+
+        r.shutdown();
     }
 
     @Test
@@ -539,10 +561,11 @@
         }
         catch (RuntimeException ex)
         {
-            assertEquals(
-                    ex.getMessage(),
-                    "Service interface org.apache.tapestry.ioc.Pingable is matched by 2 services: Barney, Fred.  Automatic dependency resolution requires that exactly one service implement the interface.");
+            assertEquals(ex.getMessage(),
+                         "Service interface org.apache.tapestry.ioc.Pingable is matched by 2 services: Barney, Fred.  Automatic dependency resolution requires that exactly one service implement the interface.");
         }
+
+        r.shutdown();
     }
 
     @Test
@@ -559,6 +582,8 @@
         // But the implementation is cached
 
         assertSame(r.getService(StringHolder.class), holder);
+
+        r.shutdown();
     }
 
     @Test
@@ -570,6 +595,8 @@
 
         assertEquals(g.getGreeting(), "Hello");
         assertEquals(g.toString(), "<Proxy for Greeter(org.apache.tapestry.ioc.Greeter)>");
+
+        r.shutdown();
     }
 
     @Test
@@ -581,6 +608,8 @@
 
         assertEquals(g.getGreeting(), "Hello");
         assertEquals(g.toString(), "<Proxy for HelloGreeter(org.apache.tapestry.ioc.Greeter)>");
+
+        r.shutdown();
     }
 
     @Test
@@ -591,6 +620,8 @@
         Greeter g = r.getService("InjectedBlueGreeter", Greeter.class);
 
         assertEquals(g.getGreeting(), "Blue");
+
+        r.shutdown();
     }
 
     @Test
@@ -607,13 +638,13 @@
         }
         catch (RuntimeException ex)
         {
-            assertMessageContains(
-                    ex,
-                    "Error invoking service builder method",
-                    "Unable to locate a single service assignable to type org.apache.tapestry.ioc.Greeter with marker annotation org.apache.tapestry.ioc.RedMarker",
-                    "org.apache.tapestry.ioc.GreeterModule.buildRedGreeter1()",
-                    "org.apache.tapestry.ioc.GreeterModule.buildRedGreeter2()");
+            assertMessageContains(ex, "Error invoking service builder method",
+                                  "Unable to locate a single service assignable to type org.apache.tapestry.ioc.Greeter with marker annotation org.apache.tapestry.ioc.RedMarker",
+                                  "org.apache.tapestry.ioc.GreeterModule.buildRedGreeter1()",
+                                  "org.apache.tapestry.ioc.GreeterModule.buildRedGreeter2()");
         }
+
+        r.shutdown();
     }
 
     @Test
@@ -630,12 +661,11 @@
         }
         catch (RuntimeException ex)
         {
-            assertMessageContains(
-                    ex,
-                    "Error invoking service builder method",
-                    " Unable to locate any service assignable to type org.apache.tapestry.ioc.Greeter with marker annotation org.apache.tapestry.ioc.YellowMarker.");
+            assertMessageContains(ex, "Error invoking service builder method",
+                                  " Unable to locate any service assignable to type org.apache.tapestry.ioc.Greeter with marker annotation org.apache.tapestry.ioc.YellowMarker.");
         }
 
+        r.shutdown();
     }
 
     @SuppressWarnings("unchecked")
@@ -665,6 +695,8 @@
         assertSame(tc1, tc2);
 
         verify();
+
+        r.shutdown();
     }
 
     /**
@@ -704,6 +736,8 @@
 
             if (serviceId.equals("BlueGreeter")) assertEquals(a.getStatus(), Status.VIRTUAL);
         }
+
+        r.shutdown();
     }
 
     @Test
@@ -717,9 +751,8 @@
 
         assertEquals(CountingGreeterImpl._instantiationCount, 0);
 
-        assertEquals(
-                g.toString(),
-                "<Autobuild proxy org.apache.tapestry.ioc.CountingGreeterImpl(org.apache.tapestry.ioc.Greeter)>");
+        assertEquals(g.toString(),
+                     "<Autobuild proxy org.apache.tapestry.ioc.CountingGreeterImpl(org.apache.tapestry.ioc.Greeter)>");
 
         assertEquals(CountingGreeterImpl._instantiationCount, 0);
 
@@ -731,5 +764,9 @@
             assertEquals(g.getGreeting(), "Hello");
             assertEquals(CountingGreeterImpl._instantiationCount, 1);
         }
+
+        r.shutdown();
     }
+
+
 }

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/RegistryBuilderTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/RegistryBuilderTest.java?rev=595970&r1=595969&r2=595970&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/RegistryBuilderTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/RegistryBuilderTest.java Sat Nov 17 08:36:50 2007
@@ -39,6 +39,8 @@
         List<String> names = service.getNames();
 
         assertEquals(names, Arrays.asList("Beta", "Gamma", "UnorderedNames"));
+
+        r.shutdown();
     }
 
     @Test
@@ -61,5 +63,7 @@
         // ClassFactory service was accessed and used.
 
         assertEquals(service.toString(), "<Proxy for Square(org.apache.tapestry.ioc.Square)>");
+
+        registry.shutdown();
     }
 }

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/DefaultModuleDefImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/DefaultModuleDefImplTest.java?rev=595970&r1=595969&r2=595970&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/DefaultModuleDefImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/DefaultModuleDefImplTest.java Sat Nov 17 08:36:50 2007
@@ -139,8 +139,7 @@
 
         // BigDecimal is arbitrary, any class would do.
 
-        ModuleDef md = new DefaultModuleDefImpl(ServiceIdConflictMethodModule.class, logger,
-                                                _classFactory);
+        ModuleDef md = new DefaultModuleDefImpl(ServiceIdConflictMethodModule.class, logger, _classFactory);
 
         Set<String> ids = md.getServiceIds();
 
@@ -204,8 +203,7 @@
         invalidDecoratorMethod(VoidDecoratorMethodModule.class, "decorateVoid");
     }
 
-    private void invalidDecoratorMethod(Class moduleClass, String methodName)
-            throws NoSuchMethodException
+    private void invalidDecoratorMethod(Class moduleClass, String methodName) throws NoSuchMethodException
     {
         Method m = moduleClass.getMethod(methodName, Object.class);
 
@@ -262,23 +260,17 @@
     @Test
     public void ordered_contribution_method()
     {
-        attemptConfigurationMethod(
-                OrderedConfigurationModule.class,
-                "Ordered",
-                "contributeOrdered(OrderedConfiguration)");
+        attemptConfigurationMethod(OrderedConfigurationModule.class, "Ordered",
+                                   "contributeOrdered(OrderedConfiguration)");
     }
 
     @Test
     public void mapped_contribution_method()
     {
-        attemptConfigurationMethod(
-                MappedConfigurationModule.class,
-                "Mapped",
-                "contributeMapped(MappedConfiguration)");
+        attemptConfigurationMethod(MappedConfigurationModule.class, "Mapped", "contributeMapped(MappedConfiguration)");
     }
 
-    private void attemptConfigurationMethod(Class moduleClass, String expectedServiceId,
-                                            String expectedMethodSignature)
+    private void attemptConfigurationMethod(Class moduleClass, String expectedServiceId, String expectedMethodSignature)
     {
         Logger logger = mockLogger();
 
@@ -387,15 +379,13 @@
 
         try
         {
-            new DefaultModuleDefImpl(UninstantiableAutobuildServiceModule.class, logger,
-                                     _classFactory);
+            new DefaultModuleDefImpl(UninstantiableAutobuildServiceModule.class, logger, _classFactory);
             unreachable();
         }
         catch (RuntimeException ex)
         {
-            assertEquals(
-                    ex.getMessage(),
-                    "Class org.apache.tapestry.ioc.internal.RunnableServiceImpl (implementation of service \'Runnable\') does not contain any public constructors.");
+            assertEquals(ex.getMessage(),
+                         "Class org.apache.tapestry.ioc.internal.RunnableServiceImpl (implementation of service \'Runnable\') does not contain any public constructors.");
         }
 
         verify();
@@ -406,14 +396,11 @@
     {
         Logger logger = mockLogger();
 
-        logger.error(and(
-                contains(NonStaticBindMethodModule.class.getName()),
-                contains("but is an instance method")));
+        logger.error(and(contains(NonStaticBindMethodModule.class.getName()), contains("but is an instance method")));
 
         replay();
 
-        ModuleDef md = new DefaultModuleDefImpl(NonStaticBindMethodModule.class, logger,
-                                                _classFactory);
+        ModuleDef md = new DefaultModuleDefImpl(NonStaticBindMethodModule.class, logger, _classFactory);
 
         // Prove that the bind method was not invoke
 
@@ -439,16 +426,12 @@
         train_getServiceId(resources, "StringHolder");
         train_getLogger(resources, logger);
         train_getServiceInterface(resources, StringHolder.class);
-        train_getService(
-                resources,
-                "ToUpperCaseStringHolder",
-                StringHolder.class,
-                new ToUpperCaseStringHolder());
+        train_getService(resources, "ToUpperCaseStringHolder", StringHolder.class, new ToUpperCaseStringHolder());
 
         replay();
 
-        ModuleDef def = new DefaultModuleDefImpl(MutlipleAutobuildServiceConstructorsModule.class,
-                                                 logger, _classFactory);
+        ModuleDef def = new DefaultModuleDefImpl(MutlipleAutobuildServiceConstructorsModule.class, logger,
+                                                 _classFactory);
 
         ServiceDef sd = def.getServiceDef("StringHolder");
 
@@ -481,8 +464,7 @@
             assertTrue(ex
                     .getMessage()
                     .matches(
-                    "Error invoking service binder method org.apache.tapestry.ioc.internal.ExceptionInBindMethod.bind\\(ServiceBinder\\) "
-                            + "\\(at ExceptionInBindMethod.java:\\d+\\): Really, how often is this going to happen\\?"));
+                    "Error invoking service binder method org.apache.tapestry.ioc.internal.ExceptionInBindMethod.bind\\(ServiceBinder\\) " + "\\(at ExceptionInBindMethod.java:\\d+\\): Really, how often is this going to happen\\?"));
         }
 
         verify();
@@ -495,8 +477,7 @@
 
         replay();
 
-        ModuleDef md = new DefaultModuleDefImpl(EagerLoadViaAnnotationModule.class, logger,
-                                                _classFactory);
+        ModuleDef md = new DefaultModuleDefImpl(EagerLoadViaAnnotationModule.class, logger, _classFactory);
 
         ServiceDef sd = md.getServiceDef("Runnable");
 
@@ -566,7 +547,12 @@
         ServiceDef sd = md.getServiceDef("SurprisinglyBlueGreeter");
 
         // BlueMarker from ServiceBindingOptions, RedMarker from @Marker on class
-        assertEquals(sd.getMarkers(), CollectionFactory.newSet(RedMarker.class, BlueMarker.class));
+
+        Set<Class> markers = sd.getMarkers();
+
+        assertTrue(markers.contains(RedMarker.class));
+        assertTrue(markers.contains(BlueMarker.class));
+        assertEquals(markers.size(), 2);
 
         verify();
     }

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/ModuleImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/ModuleImplTest.java?rev=595970&r1=595969&r2=595970&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/ModuleImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/ModuleImplTest.java Sat Nov 17 08:36:50 2007
@@ -42,8 +42,7 @@
         ClassFactory factory = new ClassFactoryImpl();
         ServiceActivityTracker tracker = mockServiceActivityTracker();
 
-        ModuleDef moduleDef = new DefaultModuleDefImpl(ModuleImplTestModule.class, logger,
-                                                       getClassFactory());
+        ModuleDef moduleDef = new DefaultModuleDefImpl(ModuleImplTestModule.class, logger, getClassFactory());
 
         Module module = new ModuleImpl(registry, tracker, moduleDef, null, logger);
 
@@ -79,8 +78,7 @@
         verify();
     }
 
-    protected final void train_newClass(InternalRegistry registry, ClassFactory factory,
-                                        Class serviceInterface)
+    protected final void train_newClass(InternalRegistry registry, ClassFactory factory, Class serviceInterface)
     {
         expect(registry.newClass(serviceInterface)).andReturn(factory.newClass(serviceInterface));
     }
@@ -156,10 +154,8 @@
         }
         catch (RuntimeException ex)
         {
-            assertEquals(
-                    ex.getMessage(),
-                    "Module builder class org.apache.tapestry.ioc.internal.PrivateConstructorModule "
-                            + "does not contain any public constructors.");
+            assertEquals(ex.getMessage(),
+                         "Module builder class org.apache.tapestry.ioc.internal.PrivateConstructorModule " + "does not contain any public constructors.");
         }
 
         verify();
@@ -213,13 +209,13 @@
         UpcaseService us = registry.getService(UpcaseService.class);
 
         assertEquals(us.upcase("hello"), "HELLO");
-        assertEquals(
-                us.toString(),
-                "<Proxy for Upcase(org.apache.tapestry.ioc.internal.UpcaseService)>");
+        assertEquals(us.toString(), "<Proxy for Upcase(org.apache.tapestry.ioc.internal.UpcaseService)>");
 
         ToStringService ts = registry.getService(ToStringService.class);
 
         assertEquals(ts.toString(), "<ToStringService: ToString>");
+
+        registry.shutdown();
     }
 
     @Test
@@ -238,6 +234,8 @@
         {
             // The details are checked elsewhere.
         }
+
+        registry.shutdown();
     }
 
 }

Added: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/ServiceProxySerializationTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/ServiceProxySerializationTest.java?rev=595970&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/ServiceProxySerializationTest.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/ServiceProxySerializationTest.java Sat Nov 17 08:36:50 2007
@@ -0,0 +1,83 @@
+package org.apache.tapestry.ioc.internal;
+
+import org.apache.tapestry.ioc.Registry;
+import org.apache.tapestry.ioc.services.TypeCoercer;
+import org.apache.tapestry.ioc.test.IOCTestCase;
+import org.testng.annotations.Test;
+
+import java.io.*;
+
+public class ServiceProxySerializationTest extends IOCTestCase
+{
+    @Test
+    public void serialization_deserialization() throws Exception
+    {
+        Registry r = buildRegistry();
+
+        TypeCoercer proxy = r.getService(TypeCoercer.class);
+
+        byte[] serialized = serialize(proxy);
+
+        TypeCoercer proxy2 = deserialize(TypeCoercer.class, serialized);
+
+        assertSame(proxy2, proxy, "De-serialized proxy is same object if Registry unchanged.");
+
+        r.shutdown();
+
+        r = buildRegistry();
+
+        TypeCoercer proxy3 = deserialize(TypeCoercer.class, serialized);
+
+        assertNotNull(proxy3);
+        assertNotSame(proxy3, proxy, "New proxy should be different, as it is from a different Registry.");
+
+        r.shutdown();
+    }
+
+    @Test
+    public void deserialize_with_no_registry() throws Exception
+    {
+        Registry r = buildRegistry();
+
+        TypeCoercer proxy = r.getService(TypeCoercer.class);
+
+        byte[] serialized = serialize(proxy);
+
+        r.shutdown();
+
+        try
+        {
+            deserialize(TypeCoercer.class, serialized);
+            unreachable();
+        }
+        catch (Exception ex)
+        {
+            assertMessageContains(ex,
+                                  "Service token for service 'TypeCoercer' can not be converted back into a proxy because no proxy provider has been registered");
+        }
+    }
+
+    private byte[] serialize(Object object) throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream oos = new ObjectOutputStream(baos);
+
+        oos.writeObject(object);
+
+        oos.close();
+
+        byte[] serialized = baos.toByteArray();
+        return serialized;
+    }
+
+    private <T> T deserialize(Class<T> type, byte[] serialized) throws IOException, ClassNotFoundException
+    {
+        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(serialized));
+
+        Object raw = ois.readObject();
+
+        ois.close();
+
+        return type.cast(raw);
+    }
+}