You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2006/07/25 23:12:16 UTC

svn commit: r425525 - in /tapestry/tapestry5/tapestry-core/trunk/src: main/java/org/apache/tapestry/internal/ioc/ main/java/org/apache/tapestry/internal/test/ main/java/org/apache/tapestry/ioc/ main/java/org/apache/tapestry/ioc/def/ main/java/org/apach...

Author: hlship
Date: Tue Jul 25 14:12:14 2006
New Revision: 425525

URL: http://svn.apache.org/viewvc?rev=425525&view=rev
Log:
Implement unordered configurations and contributions.

Added:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/ServiceCreatorResources.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/BarneyModule.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/DecoratorList.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/FredModule.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/UnorderedNames.java
Modified:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ConfigurationType.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/DefaultModuleDefImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCMessages.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/InternalRegistry.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/Module.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ModuleImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/RegistryImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ServiceBuilderMethodInvoker.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ServiceDefImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ServiceResourcesImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ValidatingConfigurationWrapper.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/def/ServiceDef.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/BaseTestCase.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/ioc/IOCStrings.properties
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/ModuleImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/ServiceBuilderMethodInvokerTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/ValidatingConfigurationWrapperTest.java

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ConfigurationType.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ConfigurationType.java?rev=425525&r1=425524&r2=425525&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ConfigurationType.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ConfigurationType.java Tue Jul 25 14:12:14 2006
@@ -4,7 +4,7 @@
 // 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
+// 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,

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/DefaultModuleDefImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/DefaultModuleDefImpl.java?rev=425525&r1=425524&r2=425525&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/DefaultModuleDefImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/DefaultModuleDefImpl.java Tue Jul 25 14:12:14 2006
@@ -82,12 +82,13 @@
 
     private final String _moduleId;
 
-    private final Map<Class, ConfigurationType> _contributorParameterToConfigurationType = newMap();
+    private final static Map<Class, ConfigurationType> PARAMETER_TYPE_TO_CONFIGURATION_TYPE = newMap();
 
+    static
     {
-        _contributorParameterToConfigurationType.put(MappedConfiguration.class, MAPPED);
-        _contributorParameterToConfigurationType.put(OrderedConfiguration.class, ORDERED);
-        _contributorParameterToConfigurationType.put(Configuration.class, UNORDERED);
+        PARAMETER_TYPE_TO_CONFIGURATION_TYPE.put(Configuration.class, UNORDERED);
+        PARAMETER_TYPE_TO_CONFIGURATION_TYPE.put(OrderedConfiguration.class, ORDERED);
+        PARAMETER_TYPE_TO_CONFIGURATION_TYPE.put(MappedConfiguration.class, MAPPED);
     }
 
     /**
@@ -214,7 +215,7 @@
 
         for (Class parameterType : method.getParameterTypes())
         {
-            ConfigurationType thisParameter = _contributorParameterToConfigurationType
+            ConfigurationType thisParameter = PARAMETER_TYPE_TO_CONFIGURATION_TYPE
                     .get(parameterType);
 
             if (thisParameter != null)

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCMessages.java?rev=425525&r1=425524&r2=425525&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCMessages.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCMessages.java Tue Jul 25 14:12:14 2006
@@ -15,6 +15,7 @@
 package org.apache.tapestry.internal.ioc;
 
 import java.lang.reflect.Method;
+import java.lang.reflect.Type;
 import java.util.List;
 
 import org.apache.hivemind.Messages;
@@ -201,5 +202,15 @@
     {
         return MESSAGES.format("contribution-wrong-value-type", new Object[]
         { serviceId, def, actualClass.getName(), expectedClass.getName() });
+    }
+
+    static String tooManyConfigurationParameters(Method method)
+    {
+        return MESSAGES.format("too-many-configuration-parameters", asString(method));
+    }
+
+    static String genericTypeNotSupported(Type type)
+    {
+        return MESSAGES.format("generic-type-not-supported", type);
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/InternalRegistry.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/InternalRegistry.java?rev=425525&r1=425524&r2=425525&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/InternalRegistry.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/InternalRegistry.java Tue Jul 25 14:12:14 2006
@@ -14,6 +14,7 @@
 
 package org.apache.tapestry.internal.ioc;
 
+import java.util.Collection;
 import java.util.List;
 
 import org.apache.tapestry.ioc.LogSource;
@@ -82,4 +83,17 @@
      * {@link ServiceDecorator}s.
      */
     List<ServiceDecorator> findDecoratorsForService(ServiceDef serviceDef);
+
+    /**
+     * Builds up an unordered collection by invoking service contributor methods that target the
+     * service (from any module, unless the service is private).
+     * 
+     * @param <T>
+     * @param serviceDef
+     *            defines the service for which configuration data is being assembled
+     * @param objectType
+     *            identifies the type of object allowed into the collection
+     * @return the final collection
+     */
+    <T> Collection<T> getUnorderedCollection(ServiceDef serviceDef, Class objectType);
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/Module.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/Module.java?rev=425525&r1=425524&r2=425525&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/Module.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/Module.java Tue Jul 25 14:12:14 2006
@@ -19,6 +19,7 @@
 import java.util.Set;
 
 import org.apache.tapestry.ioc.ServiceDecorator;
+import org.apache.tapestry.ioc.def.ContributionDef;
 import org.apache.tapestry.ioc.def.DecoratorDef;
 import org.apache.tapestry.ioc.def.ServiceDef;
 
@@ -87,4 +88,14 @@
      * @return set of decorators, possibly empty (but not null)
      */
     Set<DecoratorDef> findMatchingDecoratorDefs(ServiceDef serviceDef);
+
+    /** Returns the id that uniquely identifies this module within the registry. */
+    String getModuleId();
+
+    /** Finds any contributions that are targetted at the indicated service. */
+    Set<ContributionDef> getContributorDefsForService(String serviceId);
+
+    /** Assembles an unordered contribution by invoking any appropriate service contribution methods. */
+    <T> Collection<T> getUnorderedConfiguration(String serviceId, Class<T> objectType);
+
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ModuleImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ModuleImpl.java?rev=425525&r1=425524&r2=425525&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ModuleImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ModuleImpl.java Tue Jul 25 14:12:14 2006
@@ -24,9 +24,11 @@
 import org.apache.tapestry.internal.annotations.SuppressNullCheck;
 import org.apache.tapestry.ioc.IOCConstants;
 import org.apache.tapestry.ioc.ServiceCreator;
+import org.apache.tapestry.ioc.ServiceCreatorResources;
 import org.apache.tapestry.ioc.ServiceDecorator;
 import org.apache.tapestry.ioc.ServiceLifecycle;
 import org.apache.tapestry.ioc.ServiceResources;
+import org.apache.tapestry.ioc.def.ContributionDef;
 import org.apache.tapestry.ioc.def.DecoratorDef;
 import org.apache.tapestry.ioc.def.ModuleDef;
 import org.apache.tapestry.ioc.def.ServiceDef;
@@ -202,7 +204,7 @@
 
         ServiceLifecycle lifecycle = _registry.getServiceLifecycle(def.getServiceLifeycle());
 
-        ServiceResources resources = new ServiceResourcesImpl(_registry, this, def, log);
+        ServiceCreatorResources resources = new ServiceResourcesImpl(_registry, this, def, log);
 
         // Build up a stack of operations that will be needed to instantiate the service
         // (by the proxy, at a later date).
@@ -344,6 +346,31 @@
         // Let's hope that they aren't lying when they say that synchronized is now super cheap!
 
         cf.addMethod(Modifier.PRIVATE | Modifier.SYNCHRONIZED, sig, builder.toString());
+    }
+
+    public String getModuleId()
+    {
+        return _moduleDef.getModuleId();
+    }
+
+    public Set<ContributionDef> getContributorDefsForService(String serviceId)
+    {
+        Set<ContributionDef> result = newSet();
+
+        for (ContributionDef def : _moduleDef.getContributionDefs())
+        {
+            if (def.getServiceId().equals(serviceId))
+                result.add(def);
+        }
+
+        return result;
+    }
+
+    public <T> Collection<T> getUnorderedConfiguration(String serviceId, Class<T> objectType)
+    {
+        ServiceDef def = _moduleDef.getServiceDef(serviceId);
+
+        return _registry.getUnorderedCollection(def, objectType);
     }
 
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/RegistryImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/RegistryImpl.java?rev=425525&r1=425524&r2=425525&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/RegistryImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/RegistryImpl.java Tue Jul 25 14:12:14 2006
@@ -14,9 +14,6 @@
 
 package org.apache.tapestry.internal.ioc;
 
-import static org.apache.tapestry.util.CollectionFactory.newList;
-import static org.apache.tapestry.util.CollectionFactory.newMap;
-
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -26,17 +23,23 @@
 import org.apache.commons.logging.Log;
 import org.apache.tapestry.internal.annotations.SuppressNullCheck;
 import org.apache.tapestry.internal.util.Orderer;
+import org.apache.tapestry.ioc.Configuration;
 import org.apache.tapestry.ioc.IOCUtilities;
 import org.apache.tapestry.ioc.LogSource;
 import org.apache.tapestry.ioc.Registry;
 import org.apache.tapestry.ioc.ServiceDecorator;
 import org.apache.tapestry.ioc.ServiceLifecycle;
+import org.apache.tapestry.ioc.ServiceLocator;
 import org.apache.tapestry.ioc.ServiceResources;
+import org.apache.tapestry.ioc.def.ContributionDef;
 import org.apache.tapestry.ioc.def.DecoratorDef;
 import org.apache.tapestry.ioc.def.ModuleDef;
 import org.apache.tapestry.ioc.def.ServiceDef;
 import org.apache.tapestry.util.CollectionFactory;
 
+import static org.apache.tapestry.util.CollectionFactory.newList;
+import static org.apache.tapestry.util.CollectionFactory.newMap;
+
 /**
  * @author Howard M. Lewis Ship
  */
@@ -97,6 +100,63 @@
         return module;
     }
 
+    public <T> Collection<T> getUnorderedCollection(ServiceDef serviceDef, Class objectType)
+    {
+        final Collection<T> result = newList();
+
+        Configuration<T> configuration = new Configuration<T>()
+        {
+            public void add(T object)
+            {
+                result.add(object);
+            }
+
+        };
+
+        if (serviceDef.isPrivate())
+        {
+            String serviceId = serviceDef.getServiceId();
+
+            addToUnorderedConfiguration(
+                    configuration,
+                    objectType,
+                    serviceDef,
+                    locateModuleForService(serviceId));
+        }
+        else
+        {
+            for (Module m : _modules.values())
+            {
+                addToUnorderedConfiguration(configuration, objectType, serviceDef, m);
+            }
+        }
+
+        return result;
+    }
+
+    private <T> void addToUnorderedConfiguration(Configuration<T> configuration, Class objectType,
+            ServiceDef serviceDef, Module module)
+    {
+        String serviceId = serviceDef.getServiceId();
+        Set<ContributionDef> contributions = module.getContributorDefsForService(serviceId);
+
+        if (contributions.isEmpty())
+            return;
+
+        Log log = getLog(serviceId);
+
+        Object moduleBuilder = module.getModuleBuilder();
+        ServiceLocator locator = new ServiceResourcesImpl(this, module, serviceDef, log);
+
+        for (ContributionDef def : contributions)
+        {
+            Configuration<T> validating = new ValidatingConfigurationWrapper(serviceId, log,
+                    objectType, def, configuration);
+
+            def.contribute(moduleBuilder, locator, validating);
+        }
+    }
+
     // Seems like something that could be cached.
     @SuppressNullCheck
     public <T> T getService(Class<T> serviceInterface, Module module)
@@ -146,7 +206,6 @@
         return result;
     }
 
-    // TODO: Real implementation
     public List<ServiceDecorator> findDecoratorsForService(ServiceDef serviceDef)
     {
         Log log = getLog(serviceDef.getServiceId());
@@ -179,12 +238,14 @@
             // the decorators module (important in terms of service visibility
             // and abbreviated service ids).
 
-            if (!moduleId.equals(decoratorModuleId))
+            if (!decoratorModuleId.equals(moduleId))
             {
+                moduleId = decoratorModuleId;
+
                 Module module = _modules.get(moduleId);
                 moduleBuilder = module.getModuleBuilder();
+
                 resources = new ServiceResourcesImpl(this, module, serviceDef, log);
-                moduleId = decoratorModuleId;
             }
 
             ServiceDecorator decorator = dd.createDecorator(moduleBuilder, resources);

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ServiceBuilderMethodInvoker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ServiceBuilderMethodInvoker.java?rev=425525&r1=425524&r2=425525&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ServiceBuilderMethodInvoker.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ServiceBuilderMethodInvoker.java Tue Jul 25 14:12:14 2006
@@ -14,17 +14,25 @@
 
 package org.apache.tapestry.internal.ioc;
 
-import static org.apache.tapestry.internal.ioc.IOCUtilities.calculateParametersForMethod;
-import static org.apache.tapestry.util.CollectionFactory.newMap;
-
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.commons.logging.Log;
 import org.apache.tapestry.ioc.ServiceCreator;
+import org.apache.tapestry.ioc.ServiceCreatorResources;
 import org.apache.tapestry.ioc.ServiceResources;
 
+import static org.apache.tapestry.internal.ioc.ConfigurationType.MAPPED;
+import static org.apache.tapestry.internal.ioc.ConfigurationType.ORDERED;
+import static org.apache.tapestry.internal.ioc.ConfigurationType.UNORDERED;
+import static org.apache.tapestry.internal.ioc.IOCUtilities.calculateParametersForMethod;
+import static org.apache.tapestry.util.CollectionFactory.newMap;
+
 /**
  * Basic implementation of {@link org.apache.tapestry.ioc.ServiceCreator} that handles invoking a
  * method on the module builder, and figures out the correct parameters to pass into the annotated
@@ -40,14 +48,23 @@
 
     private final Map<Class, Object> _parameterDefaults = newMap();
 
-    private final ServiceResources _resources;
+    private final ServiceCreatorResources _resources;
 
     private final Method _builderMethod;
 
     private final Log _log;
 
+    private final static Map<Class, ConfigurationType> PARAMETER_TYPE_TO_CONFIGURATION_TYPE = newMap();
+
+    static
+    {
+        PARAMETER_TYPE_TO_CONFIGURATION_TYPE.put(Collection.class, UNORDERED);
+        PARAMETER_TYPE_TO_CONFIGURATION_TYPE.put(List.class, ORDERED);
+        PARAMETER_TYPE_TO_CONFIGURATION_TYPE.put(Map.class, MAPPED);
+    }
+
     public ServiceBuilderMethodInvoker(Method method, Object moduleBuilder,
-            ServiceResources resources)
+            ServiceCreatorResources resources)
     {
         _serviceId = resources.getServiceId();
         _builderMethod = method;
@@ -62,6 +79,103 @@
     }
 
     /**
+     * Returns a map (based on _parameterDefaults) that includes (possibly) an additional mapping
+     * containing the collected configuration data. This involves scanning the builder method's
+     * parameters
+     */
+    private Map<Class, Object> getParameterDefaultsWithConfigurations()
+    {
+        Map<Class, Object> result = newMap(_parameterDefaults);
+        ConfigurationType type = null;
+
+        Class[] parameterTypes = _builderMethod.getParameterTypes();
+
+        for (int i = 0; i < parameterTypes.length; i++)
+        {
+            Class parameterType = parameterTypes[i];
+
+            ConfigurationType thisType = PARAMETER_TYPE_TO_CONFIGURATION_TYPE.get(parameterType);
+
+            if (thisType == null)
+                continue;
+
+            if (type != null)
+            {
+                _log.warn(IOCMessages.tooManyConfigurationParameters(_builderMethod));
+                break;
+            }
+
+            // Remember that we've seen a configuration parameter, in case there
+            // is another.
+
+            type = thisType;
+
+            switch (type)
+            {
+
+                case UNORDERED:
+
+                    Class valueType = parameterizedType(_builderMethod.getGenericParameterTypes()[i]);
+                    Collection configuration = _resources.getUnorderedConfiguration(valueType);
+
+                    result.put(Collection.class, configuration);
+
+                    break;
+
+                default:
+                    throw new IllegalArgumentException(type + " not yet supported.");
+            }
+
+        }
+
+        return result;
+    }
+
+    /**
+     * Extracts from a generic type the underlying parameterized type. I.e., for List<Runnable>,
+     * will return Runnable. This is limited to simple parameterized types, not the more complex
+     * cases involving wildcards and upper/lower boundaries.
+     * 
+     * @param type
+     *            the genetic type of the parameter, i.e., List<Runnable>
+     * @return the parameterize type (i.e. Runnable.class if type represents List<Runnable>).
+     */
+
+    // package private for testing
+    static Class parameterizedType(Type type)
+    {
+        Class result = findParameterizedTypeFromGenericType(type);
+
+        if (result == null)
+            throw new IllegalArgumentException(IOCMessages.genericTypeNotSupported(type));
+
+        return result;
+    }
+
+    private static Class findParameterizedTypeFromGenericType(Type type)
+    {
+        // For a raw Class type, it means the parameter is not parameterized (i.e. Collection, not
+        // Collection<Foo>), so we can return Object.class to allow no restriction.
+
+        if (type instanceof Class)
+            return Object.class;
+
+        if (!(type instanceof ParameterizedType))
+            return null;
+
+        ParameterizedType pt = (ParameterizedType) type;
+
+        Type[] types = pt.getActualTypeArguments();
+        if (types.length != 1)
+            return null;
+
+        Type actualType = types[0];
+
+        return actualType instanceof Class ? (Class) actualType : null;
+
+    }
+
+    /**
      * Invoked from the proxy to create the actual service implementation.
      */
     public Object createService()
@@ -78,7 +192,7 @@
             Object[] parameters = calculateParametersForMethod(
                     _builderMethod,
                     _resources,
-                    _parameterDefaults);
+                    getParameterDefaultsWithConfigurations());
 
             result = _builderMethod.invoke(_moduleBuilder, parameters);
         }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ServiceDefImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ServiceDefImpl.java?rev=425525&r1=425524&r2=425525&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ServiceDefImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ServiceDefImpl.java Tue Jul 25 14:12:14 2006
@@ -18,7 +18,7 @@
 
 import org.apache.tapestry.internal.util.InternalUtils;
 import org.apache.tapestry.ioc.ServiceCreator;
-import org.apache.tapestry.ioc.ServiceResources;
+import org.apache.tapestry.ioc.ServiceCreatorResources;
 import org.apache.tapestry.ioc.def.ServiceDef;
 
 /**
@@ -53,7 +53,7 @@
         return _builderMethod;
     }
 
-    public ServiceCreator createServiceCreator(Object moduleBuilder, ServiceResources resources)
+    public ServiceCreator createServiceCreator(Object moduleBuilder, ServiceCreatorResources resources)
     {
         return new ServiceBuilderMethodInvoker(_builderMethod, moduleBuilder, resources);
     }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ServiceResourcesImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ServiceResourcesImpl.java?rev=425525&r1=425524&r2=425525&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ServiceResourcesImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ServiceResourcesImpl.java Tue Jul 25 14:12:14 2006
@@ -14,16 +14,25 @@
 
 package org.apache.tapestry.internal.ioc;
 
+import java.util.Collection;
+
 import org.apache.commons.logging.Log;
-import org.apache.tapestry.ioc.ServiceResources;
+import org.apache.tapestry.ioc.ServiceCreatorResources;
 import org.apache.tapestry.ioc.def.ServiceDef;
 
+import static org.apache.tapestry.ioc.IOCUtilities.toQualifiedId;
+
 /**
- * Implementation of {@link org.apache.tapestry.ioc.ServiceResources}.
+ * Implementation of {@link org.apache.tapestry.ioc.ServiceCreatorResources}. We just have one
+ * implementation that fills the purposes of methods that need a
+ * {@link org.apache.tapestry.ioc.ServiceResources} (which includes service decorator methods) as
+ * well as methods that need a {@link org.apache.tapestry.ioc.ServiceCreatorResources} (which is
+ * just service builder methods). Since it is most commonly used for the former, we'll just leave
+ * the name as ServiceResourcesImpl.
  * 
  * @author Howard M. Lewis Ship
  */
-public class ServiceResourcesImpl implements ServiceResources
+public class ServiceResourcesImpl implements ServiceCreatorResources
 {
     private final InternalRegistry _registry;
 
@@ -33,7 +42,8 @@
 
     private Log _log;
 
-    public ServiceResourcesImpl(InternalRegistry registry, Module module, ServiceDef serviceDef, Log log)
+    public ServiceResourcesImpl(InternalRegistry registry, Module module, ServiceDef serviceDef,
+            Log log)
     {
         _registry = registry;
         _module = module;
@@ -53,7 +63,10 @@
 
     public <T> T getService(String serviceId, Class<T> serviceInterface)
     {
-        return _registry.getService(serviceId, serviceInterface, _module);
+        return _registry.getService(
+                toQualifiedId(_module.getModuleId(), serviceId),
+                serviceInterface,
+                _module);
     }
 
     public <T> T getService(Class<T> serviceInterface)
@@ -66,4 +79,8 @@
         return _log;
     }
 
+    public <T> Collection<T> getUnorderedConfiguration(Class<T> objectType)
+    {
+        return _module.getUnorderedConfiguration(_serviceDef.getServiceId(), objectType);
+    }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ValidatingConfigurationWrapper.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ValidatingConfigurationWrapper.java?rev=425525&r1=425524&r2=425525&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ValidatingConfigurationWrapper.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/ValidatingConfigurationWrapper.java Tue Jul 25 14:12:14 2006
@@ -15,6 +15,7 @@
 package org.apache.tapestry.internal.ioc;
 
 import org.apache.commons.logging.Log;
+import org.apache.tapestry.internal.annotations.SuppressNullCheck;
 import org.apache.tapestry.ioc.Configuration;
 import org.apache.tapestry.ioc.def.ContributionDef;
 
@@ -23,7 +24,8 @@
  * 
  * @author Howard M. Lewis Ship
  */
-public class ValidatingConfigurationWrapper implements Configuration
+@SuppressNullCheck
+public class ValidatingConfigurationWrapper<T> implements Configuration<T>
 {
     private final String _serviceId;
 
@@ -47,13 +49,15 @@
         _expectedClass = expectedClass;
     }
 
-    public void add(Object object)
+    public void add(T object)
     {
         if (object == null)
         {
             _log.warn(IOCMessages.contributionWasNull(_serviceId, _contributionDef));
             return;
         }
+
+        // Sure, we say it is type T ... but is it really?
 
         if (!_expectedClass.isInstance(object))
         {

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java?rev=425525&r1=425524&r2=425525&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java Tue Jul 25 14:12:14 2006
@@ -69,14 +69,14 @@
         setReturnValue(decorators);
     }
 
-    protected final Module newModule()
-    {
-        return newMock(Module.class);
-    }
-
     protected void trainFindDecoratorsForService(InternalRegistry registry)
     {
         registry.findDecoratorsForService(EasyMock.isA(ServiceDef.class));
         setReturnValue(CollectionFactory.newList());
+    }
+
+    protected final InternalRegistry newInternalRegistry()
+    {
+        return newMock(InternalRegistry.class);
     }
 }

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/ServiceCreatorResources.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/ServiceCreatorResources.java?rev=425525&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/ServiceCreatorResources.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/ServiceCreatorResources.java Tue Jul 25 14:12:14 2006
@@ -0,0 +1,30 @@
+// 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 java.util.Collection;
+
+/**
+ * Extends {@link org.apache.tapestry.ioc.ServiceResources} with additional methods needed only by
+ * the service builder method, related to accessing a service's configuration.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+public interface ServiceCreatorResources extends ServiceResources
+{
+    <T> Collection<T> getUnorderedConfiguration(Class<T> objectType);
+
+    // More to come!
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/def/ServiceDef.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/def/ServiceDef.java?rev=425525&r1=425524&r2=425525&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/def/ServiceDef.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/def/ServiceDef.java Tue Jul 25 14:12:14 2006
@@ -15,7 +15,7 @@
 package org.apache.tapestry.ioc.def;
 
 import org.apache.tapestry.ioc.ServiceCreator;
-import org.apache.tapestry.ioc.ServiceResources;
+import org.apache.tapestry.ioc.ServiceCreatorResources;
 
 /**
  * Service definition derived, by default, from a service builder method.
@@ -30,10 +30,10 @@
      * @param moduleBuilder
      *            the module builder instance for the module containing the service
      * @param resources
-     *            used to resolve dependencies of the service
+     *            used to resolve dependencies of the service, or access its configuration
      * @return an object that can (later) be used to instantiate the service itself
      */
-    ServiceCreator createServiceCreator(Object moduleBuilder, ServiceResources resources);
+    ServiceCreator createServiceCreator(Object moduleBuilder, ServiceCreatorResources resources);
 
     /** Returns the fully qualified service id, derived from the method name. */
     String getServiceId();

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/BaseTestCase.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/BaseTestCase.java?rev=425525&r1=425524&r2=425525&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/BaseTestCase.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/BaseTestCase.java Tue Jul 25 14:12:14 2006
@@ -31,15 +31,19 @@
 import org.apache.commons.logging.Log;
 import org.apache.hivemind.Resource;
 import org.apache.tapestry.internal.annotations.SuppressNullCheck;
+import org.apache.tapestry.internal.ioc.Module;
 import org.apache.tapestry.ioc.Configuration;
 import org.apache.tapestry.ioc.LogSource;
 import org.apache.tapestry.ioc.MappedConfiguration;
 import org.apache.tapestry.ioc.OrderedConfiguration;
 import org.apache.tapestry.ioc.ServiceCreator;
+import org.apache.tapestry.ioc.ServiceCreatorResources;
 import org.apache.tapestry.ioc.ServiceDecorator;
 import org.apache.tapestry.ioc.ServiceLocator;
 import org.apache.tapestry.ioc.ServiceResources;
 import org.apache.tapestry.ioc.def.ContributionDef;
+import org.apache.tapestry.ioc.def.DecoratorDef;
+import org.apache.tapestry.ioc.def.ModuleDef;
 import org.apache.tapestry.ioc.def.ServiceDef;
 import org.apache.tapestry.model.MutableComponentModel;
 import org.apache.tapestry.transform.ClassTransformation;
@@ -280,6 +284,38 @@
     protected final ContributionDef newContributionDef()
     {
         return newMock(ContributionDef.class);
+    }
+
+    protected final ServiceCreatorResources newServiceCreatorResources()
+    {
+        return newMock(ServiceCreatorResources.class);
+    }
+
+    protected final Module newModule()
+    {
+        return newMock(Module.class);
+    }
+
+    protected final void trainGetModuleId(ModuleDef def, String moduleId)
+    {
+        def.getModuleId();
+        setReturnValue(moduleId);
+    }
+
+    protected final ModuleDef newModuleDef()
+    {
+        return newMock(ModuleDef.class);
+    }
+
+    protected final void trainMatches(DecoratorDef decoratorDef, ServiceDef serviceDef, boolean matches)
+    {
+        decoratorDef.matches(serviceDef);
+        setReturnValue(matches);
+    }
+
+    protected final DecoratorDef newDecoratorDef()
+    {
+        return newMock(DecoratorDef.class);
     }
 
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/ioc/IOCStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/ioc/IOCStrings.properties?rev=425525&r1=425524&r2=425525&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/ioc/IOCStrings.properties (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/ioc/IOCStrings.properties Tue Jul 25 14:12:14 2006
@@ -49,10 +49,16 @@
 too-many-contribution-parameters=Service contribution method {0} 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 {0} 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 {0} 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 {0}: {1}
 contribution-was-null=Service contribution (to service ''{0}'', by {1}) was null and has been ignored.
 contribution-wrong-value-type=Service contribution (to service ''{0}'', by {1}) was an instance of {2}, \
-  but {3} was expected. The contribution has been ignored.
\ No newline at end of file
+  but {3} was expected. The contribution has been ignored.
+generic-type-not-supported=Generic type ''{0}'' is not supported. Only simple parameterized lists are \
+  supported.
\ No newline at end of file

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/ModuleImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/ModuleImplTest.java?rev=425525&r1=425524&r2=425525&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/ModuleImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/ModuleImplTest.java Tue Jul 25 14:12:14 2006
@@ -40,7 +40,7 @@
     @Test
     public void get_service_by_id_exists()
     {
-        InternalRegistry registry = newMock(InternalRegistry.class);
+        InternalRegistry registry = newInternalRegistry();
         Log log = newLog();
 
         ModuleDef moduleDef = new DefaultModuleDefImpl(ModuleImplTestModule.class, log);
@@ -77,7 +77,7 @@
     @Test
     public void get_service_by_id_private()
     {
-        InternalRegistry registry = newMock(InternalRegistry.class);
+        InternalRegistry registry = newInternalRegistry();
         Log log = newLog();
 
         ModuleDef moduleDef = new DefaultModuleDefImpl(ModuleImplTestModule.class, log);
@@ -116,7 +116,7 @@
     @Test
     public void get_service_by_id_private_wrong_module()
     {
-        InternalRegistry registry = newMock(InternalRegistry.class);
+        InternalRegistry registry = newInternalRegistry();
         Log log = newLog();
 
         ModuleDef moduleDef = new DefaultModuleDefImpl(ModuleImplTestModule.class, log);
@@ -143,7 +143,7 @@
     @Test
     public void find_service_ids_for_interface()
     {
-        InternalRegistry registry = newMock(InternalRegistry.class);
+        InternalRegistry registry = newInternalRegistry();
         Log log = newLog();
 
         ModuleDef moduleDef = new DefaultModuleDefImpl(ModuleImplTestModule.class, log);
@@ -169,7 +169,7 @@
     @Test
     public void find_decorator_defs_for_service()
     {
-        InternalRegistry registry = newMock(InternalRegistry.class);
+        InternalRegistry registry = newInternalRegistry();
         ServiceDef serviceDef = newServiceDef();
         DecoratorDef def1 = newDecoratorDef();
         DecoratorDef def2 = newDecoratorDef();
@@ -198,20 +198,21 @@
         verify();
     }
 
-    protected ModuleDef newModuleDef()
+    @Test
+    public void get_module_id()
     {
-        return newMock(ModuleDef.class);
-    }
+        InternalRegistry registry = newInternalRegistry();
+        ModuleDef def = newModuleDef();
 
-    protected void trainMatches(DecoratorDef decoratorDef, ServiceDef serviceDef, boolean matches)
-    {
-        decoratorDef.matches(serviceDef);
-        setReturnValue(matches);
-    }
+        trainGetModuleId(def, "foo.bar");
 
-    protected final DecoratorDef newDecoratorDef()
-    {
-        return newMock(DecoratorDef.class);
+        replay();
+
+        Module module = new ModuleImpl(registry, def);
+
+        assertEquals(module.getModuleId(), "foo.bar");
+
+        verify();
     }
 
     private Registry buildRegistry()

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/ServiceBuilderMethodInvokerTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/ServiceBuilderMethodInvokerTest.java?rev=425525&r1=425524&r2=425525&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/ServiceBuilderMethodInvokerTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/ServiceBuilderMethodInvokerTest.java Tue Jul 25 14:12:14 2006
@@ -15,15 +15,21 @@
 package org.apache.tapestry.internal.ioc;
 
 import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 
 import org.apache.commons.logging.Log;
 import org.apache.tapestry.internal.test.InternalBaseTestCase;
 import org.apache.tapestry.ioc.ServiceCreator;
+import org.apache.tapestry.ioc.ServiceCreatorResources;
 import org.apache.tapestry.ioc.ServiceResources;
 import org.apache.tapestry.ioc.annotations.InjectService;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import static org.apache.tapestry.internal.ioc.ServiceBuilderMethodInvoker.parameterizedType;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertSame;
 
@@ -46,10 +52,12 @@
 
     private FoeService _expectedFoe;
 
+    private Object _expectedConfiguration;
+
     @Test
     public void noargs_method()
     {
-        ServiceResources resources = newServiceResources();
+        ServiceCreatorResources resources = newServiceCreatorResources();
         Log log = newLog();
         _fie = newFieService();
 
@@ -69,7 +77,7 @@
         verify();
     }
 
-    private void trainForConstructor(ServiceResources resources, Log log)
+    private void trainForConstructor(ServiceCreatorResources resources, Log log)
     {
         trainGetServiceId(resources, SERVICE_ID);
 
@@ -82,7 +90,7 @@
     public void method_with_args()
     {
         Method method = findMethod("build_args");
-        ServiceResources resources = newServiceResources();
+        ServiceCreatorResources resources = newServiceCreatorResources();
 
         Log log = newLog();
 
@@ -113,7 +121,7 @@
     @Test
     public void injected_service_method()
     {
-        ServiceResources resources = newServiceResources();
+        ServiceCreatorResources resources = newServiceCreatorResources();
         Log log = newLog();
 
         _fie = newFieService();
@@ -137,6 +145,41 @@
         verify();
     }
 
+    @Test
+    public void injected_unordered_collection()
+    {
+        ServiceCreatorResources resources = newServiceCreatorResources();
+        Log log = newLog();
+
+        _fie = newFieService();
+        _expectedConfiguration = newMock(Collection.class);
+
+        trainForConstructor(resources, log);
+
+        trainIsDebugEnabled(log, false);
+
+        resources.getUnorderedConfiguration(Runnable.class);
+        setReturnValue(_expectedConfiguration);
+
+        replay();
+
+        ServiceCreator sc = new ServiceBuilderMethodInvoker(
+                findMethod("buildWithUnorderedConfiguration"), this, resources);
+
+        Object actual = sc.createService();
+
+        assertSame(actual, _fie);
+
+        verify();
+    }
+
+    public FieService buildWithUnorderedConfiguration(Collection<Runnable> configuration)
+    {
+        assertSame(configuration, _expectedConfiguration);
+
+        return _fie;
+    }
+
     private FoeService newFoe()
     {
         return newFoeService();
@@ -145,7 +188,7 @@
     @Test
     public void builder_method_returns_null()
     {
-        ServiceResources resources = newServiceResources();
+        ServiceCreatorResources resources = newServiceCreatorResources();
         Log log = newLog();
 
         _fie = null;
@@ -176,7 +219,7 @@
     @Test
     public void builder_method_failed()
     {
-        ServiceResources resources = newServiceResources();
+        ServiceCreatorResources resources = newServiceCreatorResources();
         Log log = newLog();
 
         _fie = null;
@@ -214,7 +257,7 @@
     {
         Method method = findMethod("build_auto");
 
-        ServiceResources resources = newServiceResources();
+        ServiceCreatorResources resources = newServiceCreatorResources();
         Log log = newLog();
 
         _fie = newFieService();
@@ -235,6 +278,63 @@
         verify();
 
         assertSame(actual, _fie);
+    }
+
+    @Test
+    public void parameterized_type_of_generic_parameter()
+    {
+        Method m = findMethod("methodWithParameterizedList");
+
+        assertEquals(m.getParameterTypes()[0], List.class);
+        Type type = m.getGenericParameterTypes()[0];
+
+        assertEquals(type.toString(), "java.util.List<java.lang.Runnable>");
+        assertEquals(parameterizedType(type), Runnable.class);
+    }
+
+    public void methodWithParameterizedList(List<Runnable> list)
+    {
+    }
+
+    @Test
+    public void parameterized_type_of_nongeneric_parameter()
+    {
+        Method m = findMethod("methodWithList");
+
+        assertEquals(m.getParameterTypes()[0], List.class);
+        Type type = m.getGenericParameterTypes()[0];
+
+        assertEquals(type.toString(), "interface java.util.List");
+        assertEquals(parameterizedType(type), Object.class);
+    }
+
+    public void methodWithList(List list)
+    {
+
+    }
+
+    @Test
+    public void parameterize_type_for_non_supported_type()
+    {
+        Method m = findMethod("methodWithWildcardList");
+
+        assertEquals(m.getParameterTypes()[0], List.class);
+        Type type = m.getGenericParameterTypes()[0];
+
+        try
+        {
+            parameterizedType(type);
+            unreachable();
+        }
+        catch (IllegalArgumentException ex)
+        {
+            assertEquals(ex.getMessage(), IOCMessages.genericTypeNotSupported(type));
+        }
+    }
+
+    public void methodWithWildcardList(List<? super ArrayList> list)
+    {
+
     }
 
     private FoeService newFoeService()

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/ValidatingConfigurationWrapperTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/ValidatingConfigurationWrapperTest.java?rev=425525&r1=425524&r2=425525&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/ValidatingConfigurationWrapperTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/ValidatingConfigurationWrapperTest.java Tue Jul 25 14:12:14 2006
@@ -45,6 +45,62 @@
         verify();
     }
 
+    @Test
+    public void null_contribution()
+    {
+        Log log = newLog();
+        Configuration configuration = newConfiguration();
+        ContributionDef def = new ContributionDefImpl("foo.Bar",
+                findMethod("contributeUnorderedNull"));
+
+        log.warn(IOCMessages.contributionWasNull("foo.Bar", def));
+
+        replay();
+
+        Configuration wrapper = new ValidatingConfigurationWrapper("foo.Bar", log, Runnable.class,
+                def, configuration);
+
+        wrapper.add(null);
+
+        verify();
+    }
+
+    @Test
+    public void wrong_type_of_contribution()
+    {
+        Log log = newLog();
+        Configuration configuration = newConfiguration();
+        ContributionDef def = new ContributionDefImpl("foo.Bar",
+                findMethod("contributeUnorderedNull"));
+
+        log.warn(IOCMessages.contributionWrongValueType(
+                "foo.Bar",
+                def,
+                String.class,
+                Runnable.class));
+
+        replay();
+
+        Configuration wrapper = new ValidatingConfigurationWrapper("foo.Bar", log, Runnable.class,
+                def, configuration);
+
+        wrapper.add("runnable");
+
+        verify();
+    }
+
+    // Just a placeholder to give the errors something to report about
+
+    public void contributeUnorderedNull()
+    {
+
+    }
+
+    public void contributeWrongType()
+    {
+
+    }
+
     private Runnable newRunnable()
     {
         return newMock(Runnable.class);

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/BarneyModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/BarneyModule.java?rev=425525&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/BarneyModule.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/BarneyModule.java Tue Jul 25 14:12:14 2006
@@ -0,0 +1,71 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.ioc;
+
+import java.util.List;
+
+import org.apache.tapestry.annotations.Match;
+import org.apache.tapestry.ioc.annotations.After;
+import org.apache.tapestry.ioc.annotations.Contribute;
+import org.apache.tapestry.ioc.annotations.Id;
+
+import static org.apache.tapestry.util.CollectionFactory.newList;
+
+/**
+ * Module used to demonstrate decorator ordering.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+
+@Id("barney")
+public class BarneyModule
+{
+    @Match("fred.*")
+    @After("fred.Beta")
+    public Object decorateGamma(Object delegate, DecoratorList list)
+    {
+        list.add("gamma");
+
+        return null;
+    }
+
+    /**
+     * Put DecoratorList in module barney, where so it won't accidentally be decorated (which
+     * recusively builds the service, and is caught as a failure).
+     */
+    public DecoratorList buildDecoratorList()
+    {
+        return new DecoratorList()
+        {
+            private List<String> _names = newList();
+
+            public void add(String name)
+            {
+                _names.add(name);
+            }
+
+            public List<String> getNames()
+            {
+                return _names;
+            }
+        };
+    }
+
+    @Contribute("fred.UnorderedNames")
+    public void contributeGamma(Configuration<String> configuration)
+    {
+        configuration.add("Gamma");
+    }
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/DecoratorList.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/DecoratorList.java?rev=425525&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/DecoratorList.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/DecoratorList.java Tue Jul 25 14:12:14 2006
@@ -0,0 +1,29 @@
+// 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 java.util.List;
+
+/**
+ * Used to track the order in which decorators are invoked.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+public interface DecoratorList
+{
+    void add(String name);
+
+    List<String> getNames();
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/FredModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/FredModule.java?rev=425525&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/FredModule.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/FredModule.java Tue Jul 25 14:12:14 2006
@@ -0,0 +1,125 @@
+// 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 java.util.Collection;
+
+import org.apache.tapestry.annotations.Match;
+import org.apache.tapestry.ioc.annotations.Before;
+import org.apache.tapestry.ioc.annotations.Contribute;
+import org.apache.tapestry.ioc.annotations.Id;
+import org.apache.tapestry.ioc.annotations.InjectService;
+import org.apache.tapestry.ioc.annotations.Private;
+
+/**
+ * Module used to demonstrate decorator ordering.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+@Id("fred")
+public class FredModule
+{
+
+    /**
+     * Doesn't matter what the service does, we just want to verify that the decorators are invoked
+     * in the order we expect.
+     */
+    public Runnable buildFred()
+    {
+        return new Runnable()
+        {
+            public void run()
+            {
+            }
+        };
+    }
+
+    @Private
+    public Runnable buildPrivateFred()
+    {
+        return buildFred();
+    }
+
+    /** A public alias of a private service. */
+    public Runnable buildPrivateFredAlias(@InjectService("PrivateFred")
+    Runnable privateFred)
+    {
+        return privateFred;
+    }
+
+    @Match("fred.*")
+    @Before("Beta")
+    public Object decorateAlpha(Object delegate, DecoratorList list)
+    {
+        list.add("alpha");
+
+        return null;
+    }
+
+    @Match("fred.*")
+    public Object decorateBeta(Object delegate, DecoratorList list)
+    {
+        list.add("beta");
+
+        return null;
+    }
+
+    public UnorderedNames buildUnorderedNames(final Collection<String> configuration)
+    {
+        return new UnorderedNames()
+        {
+
+            public Collection<String> getNames()
+            {
+                return configuration;
+            }
+
+        };
+    }
+
+    public void contributeUnorderedNames(Configuration<String> configuration)
+    {
+        configuration.add("UnorderedNames");
+    }
+
+    @Contribute("UnorderedNames")
+    public void contributeBeta(Configuration<String> configuration)
+    {
+        configuration.add("Beta");
+    }
+
+    @Private
+    public UnorderedNames buildPrivateUnorderedNames(final Collection<String> configuration)
+    {
+        return buildUnorderedNames(configuration);
+    }
+
+    public void contributePrivateUnorderedNames(Configuration<String> configuration)
+    {
+        configuration.add("PrivateUnorderedNames");
+    }
+
+    @Contribute("PrivateUnorderedNames")
+    public void contributeOmega(Configuration<String> configuration)
+    {
+        configuration.add("Omega");
+    }
+
+    public UnorderedNames buildPrivateUnorderedNamesAlias(@InjectService("PrivateUnorderedNames")
+    UnorderedNames privateService)
+    {
+        return privateService;
+    }
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java?rev=425525&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java Tue Jul 25 14:12:14 2006
@@ -0,0 +1,127 @@
+// 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 java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.tapestry.internal.ioc.TapestryIOCModule;
+import org.apache.tapestry.internal.test.InternalBaseTestCase;
+import org.apache.tapestry.util.CollectionFactory;
+import org.testng.annotations.Test;
+
+import static org.apache.tapestry.util.CollectionFactory.newList;
+import static org.testng.Assert.assertEquals;
+
+/**
+ * A few tests that are easiest (or even just possible) by building a Registry and trying out a few
+ * things.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+public class IntegrationTest extends InternalBaseTestCase
+{
+    private Registry buildRegistry()
+    {
+        RegistryBuilder builder = new RegistryBuilder();
+        builder.add(TapestryIOCModule.class, FredModule.class, BarneyModule.class);
+
+        return builder.build();
+    }
+
+    /**
+     * Along the way, we also test a few other things, such as decorator matching and automatic
+     * dependency resolution.
+     */
+    @Test
+    public void public_service_decorator_order()
+    {
+        Registry r = buildRegistry();
+
+        Runnable service = r.getService("fred.Fred", Runnable.class);
+
+        // Force creation
+
+        service.run();
+
+        List<String> names = r.getService(DecoratorList.class).getNames();
+
+        // Note that the order of invocation appears backwards, since we build back-to-front
+
+        assertEquals(names, Arrays.asList("gamma", "beta", "alpha"));
+    }
+
+    /**
+     * Along the way, we are demonstrating that decorators can target multiple services within a
+     * module, and can target services in other modules. The main point, though, is the difference
+     * between a private and a public service in terms of decoration.
+     */
+    @Test
+    public void private_service_decorator_order()
+    {
+        Registry r = buildRegistry();
+
+        Runnable service = r.getService("fred.PrivateFredAlias", Runnable.class);
+
+        // Force creation
+
+        service.run();
+
+        List<String> names = r.getService(DecoratorList.class).getNames();
+
+        // The trick here is that the public service (PrivateFredAlias) was decorated first with the
+        // full set (the same as the previous test), then the private service (PrivateFred) was
+        // decorated just with decorators from module fred.
+
+        assertEquals(names, Arrays.asList("gamma", "beta", "alpha", "beta", "alpha"));
+    }
+
+    @Test
+    public void public_service_unordered_configuration()
+    {
+        Registry r = buildRegistry();
+
+        UnorderedNames service = r.getService("fred.UnorderedNames", UnorderedNames.class);
+
+        List<String> names = sortedNames(service);
+
+        assertEquals(names, Arrays.asList("Beta", "Gamma", "UnorderedNames"));
+    }
+
+    @Test
+    public void private_service_unordered_configuration()
+    {
+        Registry r = buildRegistry();
+
+        UnorderedNames service = r.getService(
+                "fred.PrivateUnorderedNamesAlias",
+                UnorderedNames.class);
+
+        List<String> names = sortedNames(service);
+
+        assertEquals(names, Arrays.asList("Omega", "PrivateUnorderedNames"));
+
+    }
+
+    private List<String> sortedNames(UnorderedNames service)
+    {
+        List<String> names = newList(service.getNames());
+
+        Collections.sort(names);
+        return names;
+    }
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/UnorderedNames.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/UnorderedNames.java?rev=425525&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/UnorderedNames.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/UnorderedNames.java Tue Jul 25 14:12:14 2006
@@ -0,0 +1,27 @@
+// 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 java.util.Collection;
+
+/**
+ * Used for testing of unordered contributions. The names are contributed in from multiple modules.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+public interface UnorderedNames
+{
+    public Collection<String> getNames();
+}