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 2008/08/11 18:42:56 UTC

svn commit: r684803 - in /tapestry/tapestry5/trunk: quickstart/src/main/resources/archetype-resources/src/main/java/services/ src/site/apt/ tapestry-annotations/src/main/java/org/apache/tapestry5/ioc/annotations/ tapestry-core/src/main/java/org/apache/...

Author: hlship
Date: Mon Aug 11 09:42:47 2008
New Revision: 684803

URL: http://svn.apache.org/viewvc?rev=684803&view=rev
Log:
TAPESTRY-1867: Support a special marker interface @Local to select just services defined within the same module

Added:
    tapestry/tapestry5/trunk/tapestry-annotations/src/main/java/org/apache/tapestry5/ioc/annotations/Local.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/LocalModule.java
Removed:
    tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry5/tutorial/services/Local.java
Modified:
    tapestry/tapestry5/trunk/quickstart/src/main/resources/archetype-resources/src/main/java/services/AppModule.java
    tapestry/tapestry5/trunk/src/site/apt/index.apt
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableRequestImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/IOCMessages.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/InternalRegistry.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ObjectLocatorImpl.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/RegistryImpl.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/InternalUtils.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/resources/org/apache/tapestry5/ioc/internal/IOCStrings.properties
    tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/service.apt
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/IntegrationTest.java
    tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry5/tutorial/entities/Address.java
    tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry5/tutorial/services/AppModule.java
    tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/forms2.apt

Modified: tapestry/tapestry5/trunk/quickstart/src/main/resources/archetype-resources/src/main/java/services/AppModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/quickstart/src/main/resources/archetype-resources/src/main/java/services/AppModule.java?rev=684803&r1=684802&r2=684803&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/quickstart/src/main/resources/archetype-resources/src/main/java/services/AppModule.java (original)
+++ tapestry/tapestry5/trunk/quickstart/src/main/resources/archetype-resources/src/main/java/services/AppModule.java Mon Aug 11 09:42:47 2008
@@ -6,7 +6,7 @@
 import org.apache.tapestry5.ioc.MappedConfiguration;
 import org.apache.tapestry5.ioc.OrderedConfiguration;
 import org.apache.tapestry5.ioc.ServiceBinder;
-import org.apache.tapestry5.ioc.annotations.InjectService;
+import org.apache.tapestry5.ioc.annotations.Local;
 import org.apache.tapestry5.services.Request;
 import org.apache.tapestry5.services.RequestFilter;
 import org.apache.tapestry5.services.RequestHandler;
@@ -52,7 +52,7 @@
      * This is a service definition, the service will be named "TimingFilter". The interface,
      * RequestFilter, is used within the RequestHandler service pipeline, which is built from the
      * RequestHandler service configuration. Tapestry IoC is responsible for passing in an
-     * appropriate Log instance. Requests for static resources are handled at a higher level, so
+     * appropriate Logger instance. Requests for static resources are handled at a higher level, so
      * this filter will only be invoked for Tapestry related requests.
      * 
      * <p>
@@ -96,10 +96,12 @@
     /**
      * This is a contribution to the RequestHandler service configuration. This is how we extend
      * Tapestry using the timing filter. A common use for this kind of filter is transaction
-     * management or security.
+     * management or security. The @Local annotation selects the desired service by type, but only
+     * from the same module.  Without @Local, there would be an error due to the other service(s)
+     * that implement RequestFilter (defined in other modules).
      */
     public void contributeRequestHandler(OrderedConfiguration<RequestFilter> configuration,
-            @InjectService("TimingFilter")
+            @Local
             RequestFilter filter)
     {
         // Each contribution to an ordered configuration has a name, When necessary, you may

Modified: tapestry/tapestry5/trunk/src/site/apt/index.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/src/site/apt/index.apt?rev=684803&r1=684802&r2=684803&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/src/site/apt/index.apt (original)
+++ tapestry/tapestry5/trunk/src/site/apt/index.apt Mon Aug 11 09:42:47 2008
@@ -36,7 +36,7 @@
 
   Tapestry is, of course, an open-source project, with all the work coming from unpaid volunteers.  That being said, our rough timeline is as follows:
   
-  * 5.0 Release Candidate in Q1 2008
+  * 5.0 Release Candidate in Q1 2008    ((<Ooops!)>
 
   * 5.0 Final Release shortly thereafter
 
@@ -84,6 +84,9 @@
 
 New And Of Note
 
+  * The new {{{apidocs/org/apache/tapestry5/ioc/annotations/Local.html}@Local}}
+    annotation makes it easier to reference services within the same module when injecting.  
+
   * Most general documentation has been moved from the tapestry-core module up to the project level.
   
   * Work has started on a {{{cookbook}Tapestry Cookbook}}, showing how to tackle common scenarios. 

Added: tapestry/tapestry5/trunk/tapestry-annotations/src/main/java/org/apache/tapestry5/ioc/annotations/Local.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-annotations/src/main/java/org/apache/tapestry5/ioc/annotations/Local.java?rev=684803&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-annotations/src/main/java/org/apache/tapestry5/ioc/annotations/Local.java (added)
+++ tapestry/tapestry5/trunk/tapestry-annotations/src/main/java/org/apache/tapestry5/ioc/annotations/Local.java Mon Aug 11 09:42:47 2008
@@ -0,0 +1,29 @@
+//  Copyright 2008 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.tapestry5.ioc.annotations;
+
+import java.lang.annotation.*;
+
+
+/**
+ * A special marker annotation which limits the search for possible services to just the <em>same</em> module containing
+ * the service being injected.  Other marker annotations may also be applied.
+ */
+@Target({ElementType.PARAMETER, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Local
+{
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableRequestImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableRequestImpl.java?rev=684803&r1=684802&r2=684803&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableRequestImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableRequestImpl.java Mon Aug 11 09:42:47 2008
@@ -16,6 +16,7 @@
 
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.ioc.annotations.Inject;
 import org.apache.tapestry5.services.Session;
 
 import java.util.List;
@@ -32,6 +33,7 @@
 
     private Session session;
 
+    @Inject
     public TestableRequestImpl()
     {
         this("/foo");

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java?rev=684803&r1=684802&r2=684803&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java Mon Aug 11 09:42:47 2008
@@ -194,15 +194,15 @@
     // ========================================================================
 
 
-    public static Alias build(Logger logger,
+    public static Alias buildAlias(Logger logger,
 
-                              @Inject @Symbol(InternalConstants.TAPESTRY_ALIAS_MODE_SYMBOL)
-                              String mode,
+                                   @Inject @Symbol(InternalConstants.TAPESTRY_ALIAS_MODE_SYMBOL)
+                                   String mode,
 
-                              @InjectService("AliasOverrides")
-                              AliasManager overridesManager,
+                                   @InjectService("AliasOverrides")
+                                   AliasManager overridesManager,
 
-                              Collection<AliasContribution> configuration)
+                                   Collection<AliasContribution> configuration)
     {
         AliasManager manager = new AliasManagerImpl(logger, configuration);
 
@@ -515,7 +515,7 @@
      */
     public static void contributeMasterObjectProvider(OrderedConfiguration<ObjectProvider> configuration,
 
-                                                      @InjectService("Alias")
+                                                      @Local
                                                       final Alias alias,
 
                                                       @InjectService("AssetObjectProvider")
@@ -523,7 +523,10 @@
     {
         // There's a nasty web of dependencies related to Alias; this wrapper class lets us
         // defer instantiating the Alias service implementation just long enough to defuse those
-        // dependencies.
+        // dependencies. The @Local annotation prevents a recursive call through the
+        // MasterObjectProvider to resolve the Alias service itself; that is MasterObjectProvider
+        // gets built using this proxy, then the proxy will trigger the construction of AliasImpl
+        // (which itself needs MasterObjectProvider to resolve some dependencies).
 
         ObjectProvider wrapper = new ObjectProvider()
         {
@@ -533,7 +536,7 @@
             }
         };
 
-        configuration.add("Alias", wrapper, "after:Value");
+        configuration.add("Alias", wrapper, "after:Value,Symbol");
 
         configuration.add("Asset", assetObjectProvider, "before:Alias");
 

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/IOCMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/IOCMessages.java?rev=684803&r1=684802&r2=684803&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/IOCMessages.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/IOCMessages.java Mon Aug 11 09:42:47 2008
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007 The Apache Software Foundation
+// Copyright 2006, 2007, 2008 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.
@@ -17,6 +17,7 @@
 import org.apache.tapestry5.ioc.Messages;
 import org.apache.tapestry5.ioc.def.ContributionDef;
 import org.apache.tapestry5.ioc.def.ServiceDef;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import static org.apache.tapestry5.ioc.internal.util.InternalUtils.asString;
 import org.apache.tapestry5.ioc.internal.util.MessagesImpl;
@@ -279,17 +280,32 @@
         return MESSAGES.format("autobuild-constructor-error", constructorDescription, cause);
     }
 
-    static String noServicesMatchMarker(Class objectType, Class marker)
+    private static String toJavaClassNames(List<Class> classes)
     {
-        return MESSAGES.format("no-services-match-marker", ClassFabUtils
-                .toJavaClassName(objectType), ClassFabUtils.toJavaClassName(marker));
+        List<String> names = CollectionFactory.newList();
+
+        for (Class<?> clazz : classes)
+        {
+            names.add(ClassFabUtils.toJavaClassName(clazz));
+        }
+
+        return InternalUtils.joinSorted(names);
+    }
+
+    static String noServicesMatchMarker(Class objectType, List<Class> markers)
+    {
+        return MESSAGES.format("no-services-match-marker",
+                               ClassFabUtils.toJavaClassName(objectType),
+                               toJavaClassNames(markers));
     }
 
-    static String manyServicesMatchMarker(Class objectType, Class marker, Collection<ServiceDef> matchingServices)
+    static String manyServicesMatchMarker(Class objectType, List<Class> markers,
+                                          Collection<ServiceDef> matchingServices)
     {
-        return MESSAGES.format("many-services-match-marker", ClassFabUtils
-                .toJavaClassName(objectType), ClassFabUtils.toJavaClassName(marker), InternalUtils
-                .joinSorted(matchingServices));
+        return MESSAGES.format("many-services-match-marker",
+                               ClassFabUtils.toJavaClassName(objectType),
+                               toJavaClassNames(markers),
+                               InternalUtils.joinSorted(matchingServices));
     }
 
     static String overlappingServiceProxyProviders()

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/InternalRegistry.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/InternalRegistry.java?rev=684803&r1=684802&r2=684803&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/InternalRegistry.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/InternalRegistry.java Mon Aug 11 09:42:47 2008
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007 The Apache Software Foundation
+// Copyright 2006, 2007, 2008 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.
@@ -14,6 +14,7 @@
 
 package org.apache.tapestry5.ioc.internal;
 
+import org.apache.tapestry5.ioc.AnnotationProvider;
 import org.apache.tapestry5.ioc.Registry;
 import org.apache.tapestry5.ioc.ServiceDecorator;
 import org.apache.tapestry5.ioc.ServiceLifecycle;
@@ -32,6 +33,17 @@
 public interface InternalRegistry extends Registry, RegistryShutdownHub
 {
     /**
+     * As with {@link org.apache.tapestry5.ioc.Registry#getObject(Class, org.apache.tapestry5.ioc.AnnotationProvider)},
+     * but handles the {@link org.apache.tapestry5.ioc.annotations.Local} annotation.
+     *
+     * @param objectType         type of object o be injected
+     * @param annotationProvider access to annotations at point of injection
+     * @param localModule        module to limit services to, if Local annotaton present
+     * @return the service or object
+     */
+    <T> T getObject(Class<T> objectType, AnnotationProvider annotationProvider, Module localModule);
+
+    /**
      * Returns a service lifecycle by service scope name.
      *
      * @param scope the name of the service scope (case insensitive)
@@ -84,7 +96,8 @@
                                             Class<V> valueType);
 
     /**
-     * Convieience for creating a new {@link ClassFab} instance using a {@link org.apache.tapestry5.ioc.services.ClassFactory}.
+     * Convieience for creating a new {@link org.apache.tapestry5.ioc.services.ClassFab} instance using a {@link
+     * org.apache.tapestry5.ioc.services.ClassFactory}.
      *
      * @param serviceInterface the interface to be implemented by the provided class
      */

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ObjectLocatorImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ObjectLocatorImpl.java?rev=684803&r1=684802&r2=684803&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ObjectLocatorImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ObjectLocatorImpl.java Mon Aug 11 09:42:47 2008
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007 The Apache Software Foundation
+// Copyright 2006, 2007, 2008 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.
@@ -46,7 +46,7 @@
 
     public <T> T getObject(Class<T> objectType, AnnotationProvider annotationProvider)
     {
-        return registry.getObject(objectType, annotationProvider);
+        return registry.getObject(objectType, annotationProvider, module);
     }
 
     protected InternalRegistry getRegistry()

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/RegistryImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/RegistryImpl.java?rev=684803&r1=684802&r2=684803&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/RegistryImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/RegistryImpl.java Mon Aug 11 09:42:47 2008
@@ -15,6 +15,7 @@
 package org.apache.tapestry5.ioc.internal;
 
 import org.apache.tapestry5.ioc.*;
+import org.apache.tapestry5.ioc.annotations.Local;
 import org.apache.tapestry5.ioc.def.ContributionDef;
 import org.apache.tapestry5.ioc.def.DecoratorDef;
 import org.apache.tapestry5.ioc.def.ModuleDef;
@@ -84,13 +85,15 @@
 
     private SymbolSource symbolSource;
 
-    private final List<Module> modules = CollectionFactory.newList();
+    private final Map<Module, Set<ServiceDef>> moduleToServiceDefs = CollectionFactory.newMap();
 
     /**
      * From marker type to a list of marked service instances.
      */
     private final Map<Class, List<ServiceDef>> markerToServiceDef = CollectionFactory.newMap();
 
+    private final Set<ServiceDef> allServiceDefs = CollectionFactory.newSet();
+
 
     public static final class OrderedConfigurationToOrdererAdaptor<T> implements OrderedConfiguration<T>
     {
@@ -122,25 +125,17 @@
 
         tracker = scoreboardAndTracker;
 
-        addBuiltin(SERVICE_ACTIVITY_SCOREBOARD_SERVICE_ID, ServiceActivityScoreboard.class, scoreboardAndTracker);
-
-        addBuiltin(LOGGER_SOURCE_SERVICE_ID, LoggerSource.class, this.loggerSource);
-
         this.classFactory = classFactory;
 
-        addBuiltin(CLASS_FACTORY_SERVICE_ID, ClassFactory.class, this.classFactory);
-
         Logger logger = loggerForBuiltinService(PERTHREAD_MANAGER_SERVICE_ID);
 
         perthreadManager = new PerthreadManagerImpl(logger);
 
-        addBuiltin(PERTHREAD_MANAGER_SERVICE_ID, PerthreadManager.class, perthreadManager);
 
         logger = loggerForBuiltinService(REGISTRY_SHUTDOWN_HUB_SERVICE_ID);
 
         registryShutdownHub = new RegistryShutdownHubImpl(logger);
 
-        addBuiltin(REGISTRY_SHUTDOWN_HUB_SERVICE_ID, RegistryShutdownHub.class, registryShutdownHub);
 
         lifecycles.put("singleton", new SingletonServiceLifecycle());
 
@@ -158,12 +153,15 @@
 
             Module module = new ModuleImpl(this, tracker, def, classFactory, logger);
 
-            modules.add(module);
+            Set<ServiceDef> moduleServiceDefs = CollectionFactory.newSet();
 
             for (String serviceId : def.getServiceIds())
             {
                 ServiceDef serviceDef = module.getServiceDef(serviceId);
 
+                moduleServiceDefs.add(serviceDef);
+                allServiceDefs.add(serviceDef);
+
                 Module existing = serviceIdToModule.get(serviceId);
 
                 if (existing != null) throw new RuntimeException(IOCMessages.serviceIdConflict(serviceId, existing
@@ -178,8 +176,16 @@
                     InternalUtils.addToMapList(markerToServiceDef, marker, serviceDef);
 
             }
+
+            moduleToServiceDefs.put(module, moduleServiceDefs);
         }
 
+        addBuiltin(SERVICE_ACTIVITY_SCOREBOARD_SERVICE_ID, ServiceActivityScoreboard.class, scoreboardAndTracker);
+        addBuiltin(LOGGER_SOURCE_SERVICE_ID, LoggerSource.class, this.loggerSource);
+        addBuiltin(CLASS_FACTORY_SERVICE_ID, ClassFactory.class, this.classFactory);
+        addBuiltin(PERTHREAD_MANAGER_SERVICE_ID, PerthreadManager.class, perthreadManager);
+        addBuiltin(REGISTRY_SHUTDOWN_HUB_SERVICE_ID, RegistryShutdownHub.class, registryShutdownHub);
+
         scoreboardAndTracker.startup();
 
         SerializationSupport.setProvider(this);
@@ -196,7 +202,7 @@
 
         List<EagerLoadServiceProxy> proxies = CollectionFactory.newList();
 
-        for (Module m : modules)
+        for (Module m : moduleToServiceDefs.keySet())
             m.collectEagerLoadServices(proxies);
 
         // TAPESTRY-2267: Gather up all the proxies before instantiating any of them.
@@ -265,7 +271,10 @@
         };
 
         for (Class marker : serviceDef.getMarkers())
+        {
             InternalUtils.addToMapList(markerToServiceDef, marker, serviceDef);
+            allServiceDefs.add(serviceDef);
+        }
 
         tracker.define(serviceDef, Status.BUILTIN);
     }
@@ -342,9 +351,7 @@
             }
         };
 
-        Collection<Module> modules = this.modules;
-
-        for (Module m : modules)
+        for (Module m : moduleToServiceDefs.keySet())
             addToUnorderedConfiguration(configuration, objectType, serviceDef, m);
 
         return result;
@@ -362,9 +369,7 @@
 
         OrderedConfiguration<T> configuration = new OrderedConfigurationToOrdererAdaptor<T>(orderer);
 
-        Collection<Module> modules = this.modules;
-
-        for (Module m : modules)
+        for (Module m : moduleToServiceDefs.keySet())
             addToOrderedConfiguration(configuration, objectType, serviceDef, m);
 
         // An ugly hack ... perhaps we should introduce a new builtin service so that this can be
@@ -376,7 +381,7 @@
             {
                 public <T> T provide(Class<T> objectType, AnnotationProvider annotationProvider, ObjectLocator locator)
                 {
-                    return findServiceByMarkerAndType(objectType, annotationProvider);
+                    return findServiceByMarkerAndType(objectType, annotationProvider, null);
                 }
             };
 
@@ -403,9 +408,7 @@
             }
         };
 
-        Collection<Module> modules = this.modules;
-
-        for (Module m : modules)
+        for (Module m : moduleToServiceDefs.keySet())
             addToMappedConfiguration(configuration, keyToContribution, keyType, objectType, serviceDef, m);
 
         return result;
@@ -535,7 +538,7 @@
     {
         List<String> result = CollectionFactory.newList();
 
-        for (Module module : modules)
+        for (Module module : moduleToServiceDefs.keySet())
             result.addAll(module.findServiceIdsForInterface(serviceInterface));
 
         for (Map.Entry<String, Object> entry : builtinServices.entrySet())
@@ -575,7 +578,7 @@
 
         Orderer<ServiceDecorator> orderer = new Orderer<ServiceDecorator>(logger);
 
-        for (Module module : modules)
+        for (Module module : moduleToServiceDefs.keySet())
         {
             Set<DecoratorDef> decorators = module.findMatchingDecoratorDefs(serviceDef);
 
@@ -601,7 +604,8 @@
         return classFactory.newClass(serviceInterface);
     }
 
-    private <T> T getObject(Class<T> objectType, AnnotationProvider annotationProvider, ObjectLocator locator)
+    private <T> T getObject(Class<T> objectType, AnnotationProvider annotationProvider, ObjectLocator locator,
+                            Module localModule)
     {
         lock.check();
 
@@ -612,7 +616,7 @@
         // to inject into a contribution method that contributes to MasterObjectProvider.
         // We also force a contribution into MasterObjectProvider to accomplish the same thing.
 
-        T result = findServiceByMarkerAndType(objectType, annotationProvider);
+        T result = findServiceByMarkerAndType(objectType, annotationProvider, localModule);
 
         if (result != null) return result;
 
@@ -622,58 +626,111 @@
         return masterProvider.provide(objectType, effectiveProvider, locator, true);
     }
 
+    private Collection<ServiceDef> filterByType(Class<?> objectType, Collection<ServiceDef> serviceDefs)
+    {
+        Collection<ServiceDef> result = CollectionFactory.newSet();
+
+        for (ServiceDef sd : serviceDefs)
+        {
+            if (objectType.isAssignableFrom(sd.getServiceInterface()))
+            {
+                result.add(sd);
+            }
+        }
+
+        return result;
+    }
+
     @SuppressWarnings("unchecked")
-    private <T> T findServiceByMarkerAndType(Class<T> objectType, AnnotationProvider provider)
+    private <T> T findServiceByMarkerAndType(Class<T> objectType, AnnotationProvider provider, Module localModule)
     {
         if (provider == null) return null;
 
+        boolean localOnly = localModule != null && provider.getAnnotation(Local.class) != null;
+
+
+        Set<ServiceDef> matches = CollectionFactory.newSet();
+
+        matches.addAll(filterByType(objectType, localOnly
+                                                ? moduleToServiceDefs.get(localModule)
+                                                : allServiceDefs
+        ));
+
+        List<Class> markers = CollectionFactory.newList();
+
+        if (localOnly) markers.add(Local.class);
+
         for (Class marker : markerToServiceDef.keySet())
         {
             if (provider.getAnnotation(marker) == null) continue;
 
-            List<ServiceDef> matches = CollectionFactory.newList();
+            markers.add(marker);
 
-            for (ServiceDef def : markerToServiceDef.get(marker))
-            {
-                if (objectType.isAssignableFrom(def.getServiceInterface())) matches.add(def);
-            }
+            matches = intersection(matches, markerToServiceDef.get(marker));
+        }
 
-            switch (matches.size())
-            {
+        // If didn't see @Local or any recognized marker annotation, then don't try to filter that way.
+        // Continue on, eventually to the MasterObjectProvider service.
+
+        if (markers.isEmpty()) return null;
+
+        switch (matches.size())
+        {
 
-                case 1:
+            case 1:
 
-                    ServiceDef def = matches.get(0);
+                ServiceDef def = matches.iterator().next();
 
-                    return getService(def.getServiceId(), objectType);
+                return getService(def.getServiceId(), objectType);
 
-                case 0:
+            case 0:
 
-                    // It's no accident that the user put the marker annotation at the injection
-                    // point, since it matches a known marker annotation, it better be there for
-                    // a reason. So if we don't get a match, we have to assume the user expected
-                    // one, and that is an error.
+                // It's no accident that the user put the marker annotation at the injection
+                // point, since it matches a known marker annotation, it better be there for
+                // a reason. So if we don't get a match, we have to assume the user expected
+                // one, and that is an error.
 
-                    // This doesn't help when the user places an annotation they *think* is a marker
-                    // but isn't really a marker (because no service is marked by the annotation).
+                // This doesn't help when the user places an annotation they *think* is a marker
+                // but isn't really a marker (because no service is marked by the annotation).
 
-                    throw new RuntimeException(IOCMessages
-                            .noServicesMatchMarker(objectType, marker));
+                throw new RuntimeException(IOCMessages.noServicesMatchMarker(objectType, markers));
 
-                default:
-                    throw new RuntimeException(IOCMessages.manyServicesMatchMarker(objectType, marker, matches));
-            }
+            default:
+                throw new RuntimeException(IOCMessages.manyServicesMatchMarker(objectType, markers, matches));
+        }
+    }
+
+    /**
+     * Filters the set into a new set, containing only elements shared between the set and the filter collection.
+     *
+     * @param set    to be filtered
+     * @param filter values to keep from the set
+     * @return a new set containing only the shared values
+     */
+    private static <T> Set<T> intersection(Set<T> set, Collection<T> filter)
+    {
+        if (set.isEmpty()) return Collections.emptySet();
 
+        Set<T> result = CollectionFactory.newSet();
+
+        for (T elem : filter)
+        {
+            if (set.contains(elem)) result.add(elem);
         }
 
-        return null;
+        return result;
     }
 
+
     public <T> T getObject(Class<T> objectType, AnnotationProvider annotationProvider)
     {
-        lock.check();
+        return getObject(objectType, annotationProvider, this, null);
+    }
 
-        return getObject(objectType, annotationProvider, this);
+
+    public <T> T getObject(Class<T> objectType, AnnotationProvider annotationProvider, Module localModule)
+    {
+        return getObject(objectType, annotationProvider, this, localModule);
     }
 
     public void addRegistryShutdownListener(RegistryShutdownListener listener)

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/InternalUtils.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/InternalUtils.java?rev=684803&r1=684802&r2=684803&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/InternalUtils.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/InternalUtils.java Mon Aug 11 09:42:47 2008
@@ -194,7 +194,7 @@
         }
 
         // In the absence of @InjectService, try some autowiring. First, does the
-        // parameter type match on of the resources (the parameter defaults)?
+        // parameter type match one of the resources (the parameter defaults)?
 
         if (provider.getAnnotation(Inject.class) == null)
         {

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/resources/org/apache/tapestry5/ioc/internal/IOCStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/resources/org/apache/tapestry5/ioc/internal/IOCStrings.properties?rev=684803&r1=684802&r2=684803&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/resources/org/apache/tapestry5/ioc/internal/IOCStrings.properties (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/resources/org/apache/tapestry5/ioc/internal/IOCStrings.properties Mon Aug 11 09:42:47 2008
@@ -1,4 +1,4 @@
-# Copyright 2006, 2007 The Apache Software Foundation
+# Copyright 2006, 2007, 2008 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.
@@ -29,8 +29,8 @@
 no-service-matches-type=No service implements the interface %s.
 many-service-matches=Service interface %s is matched by %d services: %s.  \
   Automatic dependency resolution requires that exactly one service implement the interface.
-no-services-match-marker=Unable to locate any service assignable to type %s with marker annotation %s.
-many-services-match-marker=Unable to locate a single service assignable to type %s with marker annotation %s.  \
+no-services-match-marker=Unable to locate any service assignable to type %s with marker annotation(s) %s.
+many-services-match-marker=Unable to locate a single service assignable to type %s with marker annotation(s) %s.  \
  All of the following services match: %s.
 unknown-scope=Unknown service scope '%s'.
 decorator-method-needs-delegate-parameter=Decorator methods must a parameter for the service delegate \

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/service.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/service.apt?rev=684803&r1=684802&r2=684803&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/service.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/service.apt Mon Aug 11 09:42:47 2008
@@ -310,6 +310,18 @@
   The @Marker annotation may also be placed on an implementation class, which means that you may omit
   the call to withMarker() inside the bind() method.
 
+  Finally, the point of injection may have multiple marker annotations; only services that are marked
+  with <all> those markers will be considered for injection. Each marker annotation creates an increasingly narrow
+  subset from the set of all possible services (compatible with the indicated dependency type).
+
+Local Dependencies
+
+  A special marker interface,
+  {{{../apidocs/org/apache/tapestry5/ioc/annotations/Local.html}@Local}},
+  indicates a dependency that should only be resolved using services from within <the same module>.   
+
+  @Local can also be combined with other marker annotations.
+
 Injecting Dependencies for Autobuilt Services
 
   With autobuilt services, there's no service builder method in which to specify injections.

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/IntegrationTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/IntegrationTest.java?rev=684803&r1=684802&r2=684803&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/IntegrationTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/IntegrationTest.java Mon Aug 11 09:42:47 2008
@@ -685,7 +685,7 @@
         catch (RuntimeException ex)
         {
             assertMessageContains(ex, "Error invoking service builder method",
-                                  "Unable to locate a single service assignable to type org.apache.tapestry5.ioc.Greeter with marker annotation org.apache.tapestry5.ioc.RedMarker",
+                                  "Unable to locate a single service assignable to type org.apache.tapestry5.ioc.Greeter with marker annotation(s) org.apache.tapestry5.ioc.RedMarker",
                                   "org.apache.tapestry5.ioc.GreeterModule.buildRedGreeter1()",
                                   "org.apache.tapestry5.ioc.GreeterModule.buildRedGreeter2()");
         }
@@ -708,7 +708,7 @@
         catch (RuntimeException ex)
         {
             assertMessageContains(ex, "Error invoking service builder method",
-                                  " Unable to locate any service assignable to type org.apache.tapestry5.ioc.Greeter with marker annotation org.apache.tapestry5.ioc.YellowMarker.");
+                                  " Unable to locate any service assignable to type org.apache.tapestry5.ioc.Greeter with marker annotation(s) org.apache.tapestry5.ioc.YellowMarker.");
         }
 
         r.shutdown();
@@ -889,4 +889,18 @@
             );
         }
     }
+
+    @Test
+    public void local_annotation()
+    {
+        Registry r = buildRegistry(GreeterModule.class, LocalModule.class);
+
+        StringHolder g = r.getService("LocalGreeterHolder", StringHolder.class);
+
+        // Comes from the @Local DrawlGreeter, even though there are many other Greeter services available.
+
+        assertEquals(g.getValue(), "Hello, y'all!");
+
+        r.shutdown();
+    }
 }

Added: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/LocalModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/LocalModule.java?rev=684803&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/LocalModule.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/LocalModule.java Mon Aug 11 09:42:47 2008
@@ -0,0 +1,46 @@
+//  Copyright 2008 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.tapestry5.ioc;
+
+import org.apache.tapestry5.ioc.annotations.Local;
+
+public class LocalModule
+{
+    public Greeter buildDrawlGreeter()
+    {
+        return new Greeter()
+        {
+            public String getGreeting()
+            {
+                return "Hello, y'all!";
+            }
+        };
+    }
+
+    public StringHolder buildLocalGreeterHolder(final @Local Greeter greeter)
+    {
+        return new StringHolder()
+        {
+            public void setValue(String value)
+            {
+            }
+
+            public String getValue()
+            {
+                return greeter.getGreeting();
+            }
+        };
+    }
+}

Modified: tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry5/tutorial/entities/Address.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry5/tutorial/entities/Address.java?rev=684803&r1=684802&r2=684803&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry5/tutorial/entities/Address.java (original)
+++ tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry5/tutorial/entities/Address.java Mon Aug 11 09:42:47 2008
@@ -28,29 +28,34 @@
 {
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @NonVisual
     private Long id;
 
     private Honorific honorific;
 
+    @Validate("required")
     private String firstName;
 
+    @Validate("required")
     private String lastName;
 
     private String street1;
 
     private String street2;
 
+    @Validate("required")
     private String city;
 
+    @Validate("required")
     private String state;
 
+    @Validate("required,regexp")
     private String zip;
 
     private String email;
 
     private String phone;
 
-    @NonVisual
     public Long getId()
     {
         return id;
@@ -66,7 +71,6 @@
         return honorific;
     }
 
-    @Validate("required")
     public String getFirstName()
     {
         return firstName;
@@ -77,7 +81,6 @@
         return lastName;
     }
 
-    @Validate("required")
     public String getStreet1()
     {
         return street1;
@@ -88,19 +91,16 @@
         return street2;
     }
 
-    @Validate("required")
     public String getCity()
     {
         return city;
     }
 
-    @Validate("required")
     public String getState()
     {
         return state;
     }
 
-    @Validate("required,regexp")
     public String getZip()
     {
         return zip;

Modified: tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry5/tutorial/services/AppModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry5/tutorial/services/AppModule.java?rev=684803&r1=684802&r2=684803&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry5/tutorial/services/AppModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-tutorial1/src/main/java/org/apache/tapestry5/tutorial/services/AppModule.java Mon Aug 11 09:42:47 2008
@@ -1,4 +1,4 @@
-// Copyright 2007 The Apache Software Foundation
+// Copyright 2007, 2008 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.
@@ -17,7 +17,7 @@
 import org.apache.tapestry5.SymbolConstants;
 import org.apache.tapestry5.ioc.MappedConfiguration;
 import org.apache.tapestry5.ioc.OrderedConfiguration;
-import org.apache.tapestry5.ioc.annotations.Marker;
+import org.apache.tapestry5.ioc.annotations.Local;
 import org.apache.tapestry5.services.Request;
 import org.apache.tapestry5.services.RequestFilter;
 import org.apache.tapestry5.services.RequestHandler;
@@ -30,7 +30,6 @@
  * This module is automatically included as part of the Tapestry IoC Registry, it's a good place to configure and extend
  * Tapestry, or to place your own services.
  */
-@Marker(Local.class)
 public class AppModule
 {
     public static void contributeApplicationDefaults(

Modified: tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/forms2.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/forms2.apt?rev=684803&r1=684802&r2=684803&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/forms2.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-tutorial1/src/site/apt/forms2.apt Mon Aug 11 09:42:47 2008
@@ -78,7 +78,10 @@
     </dependencies>
 ----
 
-  The tapestry-hibernate library includes, as transitive dependencies, Hibernate and tapestry-core.
+  The tapestry-hibernate library includes, as transitive dependencies, Hibernate and tapestry-core.  This means
+  that you can simply replace "tapestry-core" with "tapestry-hibernate" inside the \<artifactId\> element.
+
+  Since Hibernate can work with so many different databases, we must explicitly add in the correct driver.
 
 * Hibernate Configuration
 
@@ -119,9 +122,7 @@
 
   For an entity class to be used with Hibernate, some Hibernate annotations must be added to the class.
 
-  Below is the updated Address class, with the Hibernate annotations (as well as the Tapestry ones).  Hibernate
-  annotations can be applied to fields or to accessor methods, but the Tapestry annotations we use below
-  are for methods only.
+  Below is the updated Address class, with the Hibernate annotations (as well as the Tapestry ones).  
 
   <<src/main/java/org/apache/tapestry5/tutorial/entities/Address.java:>>
 
@@ -142,29 +143,34 @@
 {
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @NonVisual
     private Long id;
 
     private Honorific honorific;
 
+    @Validate("required")
     private String firstName;
 
+    @Validate("required")
     private String lastName;
 
     private String street1;
 
     private String street2;
 
+    @Validate("required")
     private String city;
 
+    @Validate("required")
     private String state;
 
+    @Validate("required,regexp")
     private String zip;
 
     private String email;
 
     private String phone;
 
-    @NonVisual
     public Long getId()
     {
         return id;
@@ -180,7 +186,6 @@
         return honorific;
     }
 
-    @Validate("required")
     public String getFirstName()
     {
         return firstName;
@@ -191,7 +196,6 @@
         return lastName;
     }
 
-    @Validate("required")
     public String getStreet1()
     {
         return street1;
@@ -202,19 +206,16 @@
         return street2;
     }
 
-    @Validate("required")
     public String getCity()
     {
         return city;
     }
 
-    @Validate("required")
     public String getState()
     {
         return state;
     }
 
-    @Validate("required,regexp")
     public String getZip()
     {
         return zip;
@@ -282,6 +283,16 @@
 }
 ----
 
+  The Tapestry annotations, @NonVisual and @Validate, may be placed on the setter or getter method or on
+  the field (as we have done here).  As with the Hibernate annotations, putting the annotation on the field
+  requires that the field name match the corresponding property name.
+
+  [@NonVisual]
+    This annotation indicates a field, such as a primary key, that should not be made visible to the user.
+
+  [@Validate]
+    This annotations identifies the validations associated with a field.
+
 Updating the Database
 
   So we have a database up and running, and Hibernate is configured to connect to it.  Let's make use of that