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/11 10:28:04 UTC

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

Author: hlship
Date: Sun Nov 11 01:28:03 2007
New Revision: 593862

URL: http://svn.apache.org/viewvc?rev=593862&view=rev
Log:
TAPESTRY-1900: Allow multiple markers annotations per service

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/Core.java
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AssetObjectProvider.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/TestableMarkupWriterFactoryImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/ServiceBindingOptions.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/annotations/Marker.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/def/ServiceDef.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/DefaultModuleDefImpl.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/internal/ServiceBinderImpl.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/ServiceDefImpl.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/test/TestBase.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/cookbook/patterns.apt
    tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/overview.apt
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/DefaultModuleDefImplTest.java
    tapestry/tapestry5/trunk/tapestry-spring/src/main/java/org/apache/tapestry/internal/spring/SpringModuleDef.java

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AssetObjectProvider.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AssetObjectProvider.java?rev=593862&r1=593861&r2=593862&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AssetObjectProvider.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AssetObjectProvider.java Sun Nov 11 01:28:03 2007
@@ -24,6 +24,7 @@
 import org.apache.tapestry.ioc.services.SymbolSource;
 import org.apache.tapestry.ioc.services.TypeCoercer;
 import org.apache.tapestry.services.AssetSource;
+import org.apache.tapestry.services.Core;
 
 /**
  * Exposes assets (in the current locale). The Inject annotation must be supplemented by a
@@ -37,7 +38,7 @@
 
     private final SymbolSource _symbolSource;
 
-    public AssetObjectProvider(@Builtin
+    public AssetObjectProvider(@Core
     AssetSource source,
 
     @Builtin

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/TestableMarkupWriterFactoryImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/TestableMarkupWriterFactoryImpl.java?rev=593862&r1=593861&r2=593862&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/TestableMarkupWriterFactoryImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/TestableMarkupWriterFactoryImpl.java Sun Nov 11 01:28:03 2007
@@ -18,7 +18,7 @@
 
 import org.apache.tapestry.MarkupWriter;
 import org.apache.tapestry.ioc.annotations.Scope;
-import org.apache.tapestry.ioc.services.Builtin;
+import org.apache.tapestry.services.Core;
 import org.apache.tapestry.services.MarkupWriterFactory;
 
 @Scope(PERTHREAD_SCOPE)
@@ -32,7 +32,7 @@
      * Using Builtin to reference to framework-provided version, which this implementation wraps
      * around.
      */
-    public TestableMarkupWriterFactoryImpl(@Builtin
+    public TestableMarkupWriterFactoryImpl(@Core
     MarkupWriterFactory delegate)
     {
         _delegate = delegate;

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/Core.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/Core.java?rev=593862&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/Core.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/Core.java Sun Nov 11 01:28:03 2007
@@ -0,0 +1,35 @@
+// Copyright 2007 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.services;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Marker for services that are provided by the Tapestry core module.
+ */
+@Target(
+{ PARAMETER, FIELD })
+@Retention(RUNTIME)
+@Documented
+public @interface Core
+{
+
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java?rev=593862&r1=593861&r2=593862&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java Sun Nov 11 01:28:03 2007
@@ -132,7 +132,7 @@
 /**
  * The root module for Tapestry.
  */
-@Marker(Builtin.class)
+@Marker(Core.class)
 public final class TapestryModule
 {
     public static void bind(ServiceBinder binder)
@@ -1580,9 +1580,9 @@
 
     public static void contributeTemplateParser(MappedConfiguration<String, URL> config)
     {
-        // Any class inside the internal module would do.  Or we could move all these
+        // Any class inside the internal module would do. Or we could move all these
         // files to o.a.t.services.
-        
+
         Class c = UpdateListenerHub.class;
         config.add("-//W3C//DTD XHTML 1.0 Strict//EN", c.getResource("xhtml1-strict.dtd"));
         config.add("-//W3C//DTD XHTML 1.0 Transitional//EN", c

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/ServiceBindingOptions.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/ServiceBindingOptions.java?rev=593862&r1=593861&r2=593862&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/ServiceBindingOptions.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/ServiceBindingOptions.java Sun Nov 11 01:28:03 2007
@@ -56,15 +56,16 @@
     ServiceBindingOptions eagerLoad();
 
     /**
-     * Defines the marker interface for the service, used to connect injections by type at the point
-     * of injection with a particular service implementation, based on the intersection of type and
-     * marker interface. The containing module will sometimes provide a default marker interface for
-     * all services within the module, this method allows that default to be overridden (typically a
-     * different marker annotation, but sometimes to null).
+     * Defines the marker interface(s) for the service, used to connect injections by type at the
+     * point of injection with a particular service implementation, based on the intersection of
+     * type and marker interface. The containing module will sometimes provide a set of default
+     * marker annotations for all services within the module, this method allows that default to be
+     * extended.
      * 
      * @param <T>
      * @param marker
+     *            one or more markers to add
      * @return this binding options, for further configuration
      */
-    <T extends Annotation> ServiceBindingOptions withMarker(Class<T> marker);
+    <T extends Annotation> ServiceBindingOptions withMarker(Class<T>... marker);
 }

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/annotations/Marker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/annotations/Marker.java?rev=593862&r1=593861&r2=593862&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/annotations/Marker.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/annotations/Marker.java Sun Nov 11 01:28:03 2007
@@ -25,7 +25,7 @@
 import org.apache.tapestry.ioc.def.ServiceDef;
 
 /**
- * Used to define a {@linkplain ServiceDef#getMarker() marker annotation} for a service
+ * Used to define a {@linkplain ServiceDef#getMarkers() marker annotation} for a service
  * implementation. This allows for injection based on the combination of type and marker interface.
  * These marker interfaces should not have any values. The mere presence of the marker annotation is
  * all that is needed.

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/def/ServiceDef.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/def/ServiceDef.java?rev=593862&r1=593861&r2=593862&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/def/ServiceDef.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/def/ServiceDef.java Sun Nov 11 01:28:03 2007
@@ -14,6 +14,7 @@
 
 package org.apache.tapestry.ioc.def;
 
+import java.util.Set;
 
 import org.apache.tapestry.ioc.ObjectCreator;
 import org.apache.tapestry.ioc.ServiceBuilderResources;
@@ -48,7 +49,7 @@
      * 
      * @return the annotation, or null if the service has no annotation
      */
-    Class getMarker();
+    Set<Class> getMarkers();
 
     /**
      * Returns the service interface associated with this service. This is the interface exposed to

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/DefaultModuleDefImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/DefaultModuleDefImpl.java?rev=593862&r1=593861&r2=593862&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/DefaultModuleDefImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/DefaultModuleDefImpl.java Sun Nov 11 01:28:03 2007
@@ -28,6 +28,8 @@
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.Map;
 import java.util.Set;
@@ -48,6 +50,7 @@
 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.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry.ioc.internal.util.InternalUtils;
 import org.apache.tapestry.ioc.services.ClassFactory;
 import org.slf4j.Logger;
@@ -84,7 +87,7 @@
 
     private final static Map<Class, ConfigurationType> PARAMETER_TYPE_TO_CONFIGURATION_TYPE = newMap();
 
-    private final Class _defaultMarker;
+    private final Set<Class> _defaultMarkers = newSet();
 
     static
     {
@@ -108,7 +111,7 @@
 
         Marker annotation = builderClass.getAnnotation(Marker.class);
 
-        _defaultMarker = annotation != null ? annotation.value() : null;
+        if (annotation != null) _defaultMarkers.addAll(Arrays.asList(annotation.value()));
 
         grind();
         bind();
@@ -319,22 +322,22 @@
             }
         };
 
-        Class marker = extractMarker(method);
+        Set<Class> markers = newSet(_defaultMarkers);
+        markers.addAll(extractMarkers(method));
 
-        ServiceDefImpl serviceDef = new ServiceDefImpl(returnType, serviceId, marker, scope,
+        ServiceDefImpl serviceDef = new ServiceDefImpl(returnType, serviceId, markers, scope,
                 eagerLoad, source);
 
         addServiceDef(serviceDef);
     }
 
-    private Class extractMarker(Method method)
+    private Collection<Class> extractMarkers(Method method)
     {
         Marker annotation = method.getAnnotation(Marker.class);
 
-        // Use the annotation value if present, otherwise use the module's default
-        // (from the module class's annotation, or null if no annotation there).
+        if (annotation == null) return Collections.emptyList();
 
-        return annotation == null ? _defaultMarker : annotation.value();
+        return CollectionFactory.newList(annotation.value());
     }
 
     public void addServiceDef(ServiceDef serviceDef)
@@ -393,7 +396,7 @@
                 return;
             }
 
-            ServiceBinderImpl binder = new ServiceBinderImpl(this, _classFactory, _defaultMarker);
+            ServiceBinderImpl binder = new ServiceBinderImpl(this, _classFactory, _defaultMarkers);
 
             bindMethod.invoke(null, binder);
 

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=593862&r1=593861&r2=593862&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 Sun Nov 11 01:28:03 2007
@@ -47,6 +47,7 @@
 import org.apache.tapestry.ioc.def.ServiceDef;
 import org.apache.tapestry.ioc.internal.services.RegistryShutdownHubImpl;
 import org.apache.tapestry.ioc.internal.services.ThreadCleanupHubImpl;
+import org.apache.tapestry.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry.ioc.internal.util.InternalUtils;
 import org.apache.tapestry.ioc.internal.util.OneShotLock;
 import org.apache.tapestry.ioc.internal.util.Orderer;
@@ -75,6 +76,14 @@
 
     private static final String SERVICE_ACTIVITY_SCOREBOARD_SERVICE_ID = "ServiceActivityScoreboard";
 
+    /** The set of marker annotations for a builtin service. */
+    private final static Set<Class> BUILTIN = CollectionFactory.newSet();
+
+    static
+    {
+        BUILTIN.add(Builtin.class);
+    }
+
     /**
      * Used to obtain the {@link org.apache.tapestry.ioc.services.ClassFactory} service, which is
      * crucial when creating runtime classes for proxies and the like.
@@ -209,9 +218,7 @@
                 // The service is defined but will not have gone further than that.
                 _tracker.define(serviceDef, Status.DEFINED);
 
-                Class marker = serviceDef.getMarker();
-
-                if (marker != null)
+                for (Class marker : serviceDef.getMarkers())
                     InternalUtils.addToMapList(_markerToServiceDef, marker, serviceDef);
 
             }
@@ -267,9 +274,9 @@
                 return null;
             }
 
-            public Class getMarker()
+            public Set<Class> getMarkers()
             {
-                return Builtin.class;
+                return BUILTIN;
             }
 
             public String getServiceId()
@@ -293,7 +300,8 @@
             }
         };
 
-        InternalUtils.addToMapList(_markerToServiceDef, serviceDef.getMarker(), serviceDef);
+        for (Class marker : serviceDef.getMarkers())
+            InternalUtils.addToMapList(_markerToServiceDef, marker, serviceDef);
 
         _tracker.define(serviceDef, Status.BUILTIN);
     }

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/ServiceBinderImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/ServiceBinderImpl.java?rev=593862&r1=593861&r2=593862&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/ServiceBinderImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/ServiceBinderImpl.java Sun Nov 11 01:28:03 2007
@@ -19,6 +19,8 @@
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Constructor;
+import java.util.Arrays;
+import java.util.Set;
 
 import org.apache.tapestry.ioc.IOCConstants;
 import org.apache.tapestry.ioc.ObjectCreator;
@@ -29,6 +31,7 @@
 import org.apache.tapestry.ioc.annotations.Marker;
 import org.apache.tapestry.ioc.annotations.Scope;
 import org.apache.tapestry.ioc.def.ServiceDef;
+import org.apache.tapestry.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry.ioc.internal.util.InternalUtils;
 import org.apache.tapestry.ioc.internal.util.OneShotLock;
 import org.apache.tapestry.ioc.services.ClassFactory;
@@ -41,21 +44,21 @@
 
     private final ClassFactory _classFactory;
 
-    private final Class _defaultMarker;
+    private final Set<Class> _defaultMarkers;
 
     public ServiceBinderImpl(ServiceDefAccumulator accumulator, ClassFactory classFactory,
-            Class defaultMarker)
+            Set<Class> defaultMarkers)
     {
         _accumulator = accumulator;
         _classFactory = classFactory;
-        _defaultMarker = defaultMarker;
+        _defaultMarkers = defaultMarkers;
     }
 
     private String _serviceId;
 
     private Class _serviceInterface;
 
-    private Class _marker;
+    private final Set<Class> _markers = CollectionFactory.newSet();
 
     private Class _serviceImplementation;
 
@@ -89,14 +92,18 @@
             }
         };
 
-        ServiceDef serviceDef = new ServiceDefImpl(_serviceInterface, _serviceId, _marker, _scope,
+        // Combine service-specific markers with those inherited form the module.
+        Set<Class> markers = CollectionFactory.newSet(_defaultMarkers);
+        markers.addAll(_markers);
+
+        ServiceDef serviceDef = new ServiceDefImpl(_serviceInterface, _serviceId, markers, _scope,
                 _eagerLoad, source);
 
         _accumulator.addServiceDef(serviceDef);
 
         _serviceId = null;
         _serviceInterface = null;
-        _marker = _defaultMarker;
+        _markers.clear();
         _serviceImplementation = null;
         _eagerLoad = false;
         _scope = null;
@@ -142,7 +149,7 @@
 
         Marker marker = serviceImplementation.getAnnotation(Marker.class);
 
-        _marker = marker != null ? marker.value() : _defaultMarker;
+        if (marker != null) _markers.addAll(Arrays.asList(marker.value()));
 
         return this;
     }
@@ -178,11 +185,11 @@
         return this;
     }
 
-    public <T extends Annotation> ServiceBindingOptions withMarker(Class<T> marker)
+    public <T extends Annotation> ServiceBindingOptions withMarker(Class<T>... marker)
     {
         _lock.check();
 
-        _marker = marker;
+        _markers.addAll(Arrays.asList(marker));
 
         return this;
     }

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/ServiceDefImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/ServiceDefImpl.java?rev=593862&r1=593861&r2=593862&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/ServiceDefImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/ServiceDefImpl.java Sun Nov 11 01:28:03 2007
@@ -14,7 +14,9 @@
 
 package org.apache.tapestry.ioc.internal;
 
+import java.util.Set;
 
+import org.apache.tapestry.ioc.IOCConstants;
 import org.apache.tapestry.ioc.ObjectCreator;
 import org.apache.tapestry.ioc.ServiceBuilderResources;
 import org.apache.tapestry.ioc.def.ServiceDef;
@@ -31,17 +33,33 @@
 
     private final ObjectCreatorSource _source;
 
-    private Class _marker;
+    private final Set<Class> _markers;
 
-    ServiceDefImpl(Class serviceInterface, String serviceId, Class marker,
-            String scope, boolean eagerLoad, ObjectCreatorSource source)
+    /**
+     * @param serviceInterface
+     *            interface implemented by the service (or the service implementation class, for
+     *            non-proxied services)
+     * @param serviceId
+     *            unique id for the service
+     * @param markers
+     *            set of marker annotation classes (will be retained not copied)
+     * @param scope
+     *            scope of the service (i.e., {@link IOCConstants#DEFAULT_SCOPE}).
+     * @param eagerLoad
+     *            if true, the service is realized at startup, rather than on-demand
+     * @param source
+     *            used to create the service implementation when needed
+     */
+    ServiceDefImpl(Class serviceInterface, String serviceId, Set<Class> markers, String scope,
+            boolean eagerLoad, ObjectCreatorSource source)
     {
         _serviceInterface = serviceInterface;
         _serviceId = serviceId;
-        _marker = marker;
         _scope = scope;
         _eagerLoad = eagerLoad;
         _source = source;
+
+        _markers = markers;
     }
 
     @Override
@@ -75,9 +93,9 @@
         return _eagerLoad;
     }
 
-    public Class getMarker()
+    public Set<Class> getMarkers()
     {
-        return _marker;
+        return _markers;
     }
 
 }

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/test/TestBase.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/test/TestBase.java?rev=593862&r1=593861&r2=593862&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/test/TestBase.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/test/TestBase.java Sun Nov 11 01:28:03 2007
@@ -14,6 +14,9 @@
 
 package org.apache.tapestry.ioc.test;
 
+import java.util.Arrays;
+import java.util.List;
+
 import org.easymock.EasyMock;
 import org.easymock.IExpectationSetters;
 import org.easymock.IMocksControl;
@@ -154,5 +157,59 @@
 
         for (String substring : substrings)
             assertTrue(message.contains(substring));
+    }
+
+    /**
+     * Compares two lists for equality; first all the elements are individually compared for
+     * equality (if the lists are of unequal length, only elements up to the shorter length are
+     * compared). Then the length of the lists are compared. This generally gives
+     * 
+     * @param <T>
+     *            type of objects to compare
+     * @param actual
+     *            actual values to check
+     * @param expected
+     *            expected values
+     */
+    protected final <T> void assertListsEquals(List<T> actual, List<T> expected)
+    {
+        int count = Math.min(actual.size(), expected.size());
+
+        for (int i = 0; i < count; i++)
+        {
+            assertEquals(actual.get(i), expected.get(i), String.format("Element #%d.", i));
+        }
+
+        assertEquals(actual.size(), expected.size(), "List size.");
+    }
+
+    /**
+     * Convenience for {@link #assertListsEquals(List, List)}.
+     * 
+     * @param <T>
+     *            tyoe of objects to compare
+     * @param actual
+     *            actual values to check
+     * @param expected
+     *            expected values
+     */
+    protected final <T> void assertListsEquals(List<T> actual, T... expected)
+    {
+        assertListsEquals(actual, Arrays.asList(expected));
+    }
+
+    /**
+     * Convenience for {@link #assertListsEquals(List, List)}.
+     * 
+     * @param <T>
+     *            tyoe of objects to compare
+     * @param actual
+     *            actual values to check
+     * @param expected
+     *            expected values
+     */
+    protected final <T> void assertArraysEqual(T[] actual, T... expected)
+    {
+        assertListsEquals(Arrays.asList(actual), expected);
     }
 }

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/cookbook/patterns.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/cookbook/patterns.apt?rev=593862&r1=593861&r2=593862&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/cookbook/patterns.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/cookbook/patterns.apt Sun Nov 11 01:28:03 2007
@@ -17,6 +17,8 @@
   Let's look at another example, again from the Tapestry code base.  The
   {{{../../apidocs/org/apache/tapestry/services/InjectionProvider.html}InjectProvider}} interface
   is used to process the @Inject annotation on the fields of a Tapestry page or component.
+  Many different instances are combined together to form a
+  {{{../command.html}chain of command}}.
 
   The interface has only a single method (this is far from uncommon):
 

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/overview.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/overview.apt?rev=593862&r1=593861&r2=593862&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/overview.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/overview.apt Sun Nov 11 01:28:03 2007
@@ -21,7 +21,7 @@
   
   That's an <unmanaged> system.  Most desktop applications are unmanaged, so it's a very familiar pattern, and easy to get your head around.
 
-  By contrast, web application are a <managed> environment.  You don't write a main(), you don't control startup.  You <configure>
+  By contrast, web applications are a <managed> environment.  You don't write a main(), you don't control startup.  You <configure>
   the Servlet API to tell it about your servlet classes to be instantiated, and their lifecycle is totally controlled by
   the servlet container. 
 
@@ -34,7 +34,7 @@
   of <internal state>, values stored inside instance variables, since in a multi-threaded environment, that's no longer the safe
   place it is in traditional development.  Shared objects plus internal state plus multiple threads equals an broken, unpredictable application.
   
-  Frameworks such as Tapestry (both the IoC container, and the web framework itself) exists to help.
+  Frameworks such as Tapestry -- both the IoC container, and the web framework itself -- exist to help.
   
   When thinking in terms of IoC, <<small is beautiful>>.  What does that mean?  It means small classes and small methods
   are easier to code than large ones.  At one extreme, we have servlets circa 1997 (and Visual Basic before that) with methods a thousand lines long,

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=593862&r1=593861&r2=593862&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 Sun Nov 11 01:28:03 2007
@@ -34,6 +34,7 @@
 import org.apache.tapestry.ioc.def.ModuleDef;
 import org.apache.tapestry.ioc.def.ServiceDef;
 import org.apache.tapestry.ioc.internal.services.ClassFactoryImpl;
+import org.apache.tapestry.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry.ioc.internal.util.InternalUtils;
 import org.apache.tapestry.ioc.services.ClassFactory;
 import org.apache.tapestry.ioc.test.IOCTestCase;
@@ -89,7 +90,7 @@
         assertTrue(sd.toString().contains(className + ".buildFred()"));
         assertEquals(sd.getServiceScope(), IOCConstants.DEFAULT_SCOPE);
         assertEquals(sd.isEagerLoad(), false);
-        assertNull(sd.getMarker());
+        assertTrue(sd.getMarkers().isEmpty());
 
         sd = md.getServiceDef("Wilma");
         assertEquals(sd.isEagerLoad(), true);
@@ -520,7 +521,7 @@
 
         ServiceDef sd = md.getServiceDef("Greeter");
 
-        assertEquals(sd.getMarker(), BlueMarker.class);
+        assertListsEquals(CollectionFactory.newList(sd.getMarkers()), BlueMarker.class);
 
         verify();
     }
@@ -536,7 +537,7 @@
 
         ServiceDef sd = md.getServiceDef("RedGreeter");
 
-        assertEquals(sd.getMarker(), RedMarker.class);
+        assertListsEquals(CollectionFactory.newList(sd.getMarkers()), RedMarker.class);
 
         verify();
     }
@@ -552,11 +553,12 @@
 
         ServiceDef sd = md.getServiceDef("SecondRedGreeter");
 
-        assertEquals(sd.getMarker(), RedMarker.class);
+        assertListsEquals(CollectionFactory.newList(sd.getMarkers()), RedMarker.class);
 
         verify();
     }
 
+    @SuppressWarnings("unchecked")
     @Test
     public void explicit_marker_overrides_marker_annotation()
     {
@@ -568,8 +570,13 @@
 
         ServiceDef sd = md.getServiceDef("SurprisinglyBlueGreeter");
 
-        assertEquals(sd.getMarker(), BlueMarker.class);
+        // BlueMarker from ServiceBindingOptions, RedMarker from @Marker on class
+        assertEquals(sd.getMarkers(), CollectionFactory.newSet(RedMarker.class, BlueMarker.class));
 
         verify();
     }
+
+    // TODO: We're short on tests that ensure that marker annotations are additive (i.e., module
+    // marker annotations are
+    // merged into the set specific to the service).
 }

Modified: tapestry/tapestry5/trunk/tapestry-spring/src/main/java/org/apache/tapestry/internal/spring/SpringModuleDef.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-spring/src/main/java/org/apache/tapestry/internal/spring/SpringModuleDef.java?rev=593862&r1=593861&r2=593862&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-spring/src/main/java/org/apache/tapestry/internal/spring/SpringModuleDef.java (original)
+++ tapestry/tapestry5/trunk/tapestry-spring/src/main/java/org/apache/tapestry/internal/spring/SpringModuleDef.java Sun Nov 11 01:28:03 2007
@@ -93,10 +93,10 @@
                     return false;
                 }
 
-                /** Returns null, Spring has no concept of a marker annotation. */
-                public Class getMarker()
+                /** Returns an empty set, Spring has no concept of a marker annotation. */
+                public Set<Class> getMarkers()
                 {
-                    return null;
+                    return Collections.emptySet();
                 }
 
             };
@@ -141,9 +141,9 @@
             }
 
             /** Returns null. */
-            public Class getMarker()
+            public Set<Class> getMarkers()
             {
-                return null;
+                return Collections.emptySet();
             }
 
         };