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/07 17:17:41 UTC

svn commit: r419906 - in /tapestry/tapestry5/tapestry-core/trunk/src: main/java/org/apache/tapestry/internal/ioc/ main/java/org/apache/tapestry/ioc/ main/java/org/apache/tapestry/ioc/annotations/ main/java/org/apache/tapestry/ioc/def/ main/java/org/apa...

Author: hlship
Date: Fri Jul  7 08:17:39 2006
New Revision: 419906

URL: http://svn.apache.org/viewvc?rev=419906&view=rev
Log:
Add in support for annotation-driven and auto parameter injection into builder methods.
Add support for private services.

Added:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/annotations/Private.java
Modified:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/BasicServiceCreator.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/ModuleImpl.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/ioc/ServiceResources.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/annotations/InjectService.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/site/apt/index.apt
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/index.apt
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/module.apt
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/service.apt
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/BasicServiceCreatorTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/DefaultModuleDefImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/SimpleModuleBuilder.java

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/BasicServiceCreator.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/BasicServiceCreator.java?rev=419906&r1=419905&r2=419906&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/BasicServiceCreator.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/BasicServiceCreator.java Fri Jul  7 08:17:39 2006
@@ -21,6 +21,7 @@
 import org.apache.commons.logging.Log;
 import org.apache.tapestry.ioc.ErrorLog;
 import org.apache.tapestry.ioc.ServiceResources;
+import org.apache.tapestry.ioc.annotations.InjectService;
 import org.apache.tapestry.ioc.def.ServiceDef;
 
 import static org.apache.tapestry.util.CollectionFactory.newMap;
@@ -44,6 +45,8 @@
 
     private final ErrorLog _log;
 
+    private final ServiceResources _resources;
+
     public BasicServiceCreator(ServiceDef serviceDef, Object moduleBuilder,
             ServiceResources resources)
     {
@@ -51,6 +54,7 @@
         _serviceDef = serviceDef;
         _moduleBuilder = moduleBuilder;
         _log = resources.getErrorLog();
+        _resources = resources;
 
         _parameterDefaults.put(String.class, _serviceId);
         _parameterDefaults.put(ServiceResources.class, resources);
@@ -94,19 +98,38 @@
         return result;
     }
 
+    private <T extends Annotation> T findAnnotation(Annotation[] annotations,
+            Class<T> annotationClass)
+    {
+        for (Annotation a : annotations)
+        {
+            if (annotationClass.isInstance(a))
+                return annotationClass.cast(a);
+        }
+
+        return null;
+    }
+
+    @SuppressWarnings("unchecked")
     private Object calculateParameterValue(Class parameterType, Annotation[] parameterAnnotations)
     {
-        // TODO: Injecting services based on an annotation
+        InjectService is = findAnnotation(parameterAnnotations, InjectService.class);
+
+        if (is != null)
+        {
+            String serviceId = is.value();
+
+            return _resources.getService(serviceId, parameterType);
+        }
 
         // See if we have any "pre-determined" parameter type to object mappings
 
         Object result = _parameterDefaults.get(parameterType);
 
-        // TODO: Injecting services based on type
+        // This will return a non-null value, or throw an exception
 
         if (result == null)
-            _log.warn(IOCMessages.unknownBuilderParameter(parameterType, _serviceDef
-                    .getBuilderMethod(), _serviceId), null);
+            result = _resources.getService(parameterType);
 
         return result;
     }

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=419906&r1=419905&r2=419906&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 Fri Jul  7 08:17:39 2006
@@ -26,6 +26,7 @@
 import org.apache.tapestry.ioc.IOCConstants;
 import org.apache.tapestry.ioc.annotations.Id;
 import org.apache.tapestry.ioc.annotations.Lifecycle;
+import org.apache.tapestry.ioc.annotations.Private;
 import org.apache.tapestry.ioc.def.ModuleDef;
 import org.apache.tapestry.ioc.def.ServiceDef;
 
@@ -152,7 +153,8 @@
     private void addServiceBuildMethod(Method method)
     {
         // TODO: Methods named just "build"
-        String serviceId = _moduleId + "." + method.getName().substring(BUILD_METHOD_NAME_PREFIX.length());
+        String serviceId = _moduleId + "."
+                + method.getName().substring(BUILD_METHOD_NAME_PREFIX.length());
 
         ServiceDef existing = _serviceDefs.get(serviceId);
 
@@ -182,8 +184,9 @@
         }
 
         String lifecycle = extractLifecycle(method);
+        boolean isPrivate = method.isAnnotationPresent(Private.class);
 
-        _serviceDefs.put(serviceId, new ServiceDefImpl(serviceId, lifecycle, method));
+        _serviceDefs.put(serviceId, new ServiceDefImpl(serviceId, lifecycle, method, isPrivate));
     }
 
     private String extractLifecycle(Method method)

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=419906&r1=419905&r2=419906&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 Fri Jul  7 08:17:39 2006
@@ -89,15 +89,6 @@
         return MESSAGES.format("builder-method-error", m, serviceId, cause);
     }
 
-    static String unknownBuilderParameter(Class parameterType, Method method, String serviceId)
-    {
-        return MESSAGES.format(
-                "unknown-builder-parameter",
-                parameterType.getName(),
-                method,
-                serviceId);
-    }
-
     static String builderMethodReturnedNull(Method method, String serviceId)
     {
         return MESSAGES.format("builder-method-returned-null", method, serviceId);

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=419906&r1=419905&r2=419906&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 Fri Jul  7 08:17:39 2006
@@ -16,27 +16,41 @@
 
 import java.util.Map;
 
+import org.apache.tapestry.internal.annotations.SuppressNullCheck;
 import org.apache.tapestry.ioc.def.ModuleDef;
 import org.apache.tapestry.ioc.def.ServiceDef;
 
 import static org.apache.tapestry.util.CollectionFactory.newMap;
+import static org.apache.tapestry.util.Defense.notBlank;
+import static org.apache.tapestry.util.Defense.notNull;
 
 /**
  * @author Howard M. Lewis Ship
  */
 public class ModuleImpl implements Module
 {
-    private InternalRegistry _registry;
+    private final InternalRegistry _registry;
 
-    private ModuleDef _moduleDef;
+    private final ModuleDef _moduleDef;
 
     private Object _moduleBuilder;
 
+    public ModuleImpl(InternalRegistry registry, ModuleDef moduleDef)
+    {
+        _registry = registry;
+        _moduleDef = moduleDef;
+    }
+
     /** Keyed on fully qualified service id; values are instantiated services (proxies). */
     private final Map<String, Object> _services = newMap();
 
+    @SuppressNullCheck
     public <T> T getService(String serviceId, Class<T> serviceInterface, Module module)
     {
+        notBlank(serviceId, "serviceId");
+        notNull(serviceInterface, "serviceInterface");
+        // module may be null.
+
         ServiceDef def = _moduleDef.getServiceDef(serviceId);
 
         // No check yet for visibility, so ...

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=419906&r1=419905&r2=419906&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 Fri Jul  7 08:17:39 2006
@@ -29,11 +29,14 @@
 
     private final String _lifecycle;
 
-    ServiceDefImpl(String serviceId, String lifecycle, Method builderMethod)
+    private final boolean _private;
+
+    ServiceDefImpl(String serviceId, String lifecycle, Method builderMethod, boolean isprivate)
     {
         _serviceId = serviceId;
         _lifecycle = lifecycle;
         _builderMethod = builderMethod;
+        _private = isprivate;
     }
 
     public Method getBuilderMethod()
@@ -54,6 +57,11 @@
     public String getServiceLifeycle()
     {
         return _lifecycle;
+    }
+
+    public boolean isPrivate()
+    {
+        return _private;
     }
 
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/ServiceResources.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/ServiceResources.java?rev=419906&r1=419905&r2=419906&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/ServiceResources.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/ServiceResources.java Fri Jul  7 08:17:39 2006
@@ -43,9 +43,27 @@
      *            the interface the service implements
      * @return the service instance (or proxy)
      * @throws RuntimeException
-     *             if the service does not exist (this is considered programmer error)
+     *             if the service does not exist (this is considered programmer error), or if there
+     *             is an error instantiating the service
      */
     <T> T getService(String serviceId, Class<T> serviceInterface);
+
+    /**
+     * Locates a service by its service interface. This will work only if there's exactly one
+     * service in the entire registry that implements the service interface. If there's zero, or
+     * more than one, this is an error. However, service visibility should be taken into account as
+     * well. Generally, this should only be used at the top level, the application level. If a
+     * framework depends on this functionality to operate, it work out that it does not in some
+     * cases, depending on all sort of runtime configuration.
+     * 
+     * @param <T>
+     * @param serviceInterface
+     *            the interface the service must implement
+     * @return the lone service found
+     * @throws RuntimeException
+     *             if no such service can be found, or there is an error instantiating the service
+     */
+    <T> T getService(Class<T> serviceInterface);
 
     /** Returns an error log appropriate for the service. */
     ErrorLog getErrorLog();

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/annotations/InjectService.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/annotations/InjectService.java?rev=419906&r1=419905&r2=419906&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/annotations/InjectService.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/annotations/InjectService.java Fri Jul  7 08:17:39 2006
@@ -15,7 +15,6 @@
 package org.apache.tapestry.ioc.annotations;
 
 import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
@@ -28,6 +27,8 @@
 /**
  * Annotation used with parameters of builder methods to identify the service to be injected into
  * the builder method via the parameter.
+ * 
+ * @author Howard M. Lewis Ship
  */
 public @interface InjectService {
 

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/annotations/Private.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/annotations/Private.java?rev=419906&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/annotations/Private.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/annotations/Private.java Fri Jul  7 08:17:39 2006
@@ -0,0 +1,21 @@
+package org.apache.tapestry.ioc.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * An optional marker annotation that may be added to a service builder method. The annotation
+ * indicates the service is private, and should not be visible outside its containing module.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+@Target(METHOD)
+@Retention(RUNTIME)
+@Documented
+public @interface Private {
+
+}

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=419906&r1=419905&r2=419906&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 Fri Jul  7 08:17:39 2006
@@ -25,7 +25,8 @@
 {
     /**
      * Returns the method to invoke, on the corresponding module builder instance, that will provide
-     * the core service implementation.
+     * the core service implementation. This method is also the source of any annotations concerning
+     * the service.
      */
     Method getBuilderMethod();
 
@@ -44,4 +45,7 @@
      * the service.
      */
     String getServiceLifeycle();
+
+    /** Returns true if the service is private, visible only within the same module. */
+    boolean isPrivate();
 }

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=419906&r1=419905&r2=419906&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 Fri Jul  7 08:17:39 2006
@@ -194,4 +194,16 @@
     
     }
 
+    protected final <T> void trainGetService(ServiceResources resources, String serviceId, Class<T> serviceInterface, T service)
+    {
+        resources.getService(serviceId, serviceInterface);
+        setReturnValue(service);
+    }
+
+    protected final <T> void trainGetService(ServiceResources resources, Class<T> serviceInterface, T service)
+    {
+        resources.getService(serviceInterface);
+        setReturnValue(service);
+    }
+
 }

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=419906&r1=419905&r2=419906&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 Fri Jul  7 08:17:39 2006
@@ -23,5 +23,4 @@
 instantiate-builder-error=Unable to instantiate class {0} as builder for module ''{1}'': {2}
 singleton-proxy-to-string=<Singleton proxy for {0}({1})>
 builder-method-error=Error invoking builder method {0} (for service ''{1}''): {2}
-unknown-builder-parameter=Unexpected parameter type {0} for method {1} (service ''{2}''). The value null will be passed into the builder method. Expect further errors below.
 builder-method-returned-null=Builder method {0} (for service ''{1}'') returned null.

Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt?rev=419906&r1=419905&r2=419906&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt Fri Jul  7 08:17:39 2006
@@ -5,4 +5,5 @@
 Description
 
   The core module of Tapestry provides the interfaces and annotations that form the Tapestry API.
-  It also includes the core implementations and and internal code.
+  It also includes the {{{ioc/index.html}Tapestry IoC container}}, 
+  as well as the core implementations and and internal code.

Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/index.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/index.apt?rev=419906&r1=419905&r2=419906&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/index.apt (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/index.apt Fri Jul  7 08:17:39 2006
@@ -59,7 +59,7 @@
   As with Tapestry 5 in general, the goal of Tapestry IoC is greater simplicity, greater power, and an avoidance of XML.
   
   The Java language is the easiest and most succinct way to describe object creation and method invocation. Any approximation in
-  XML is ultimately more verbose and unwieldy.  As the {{{module.html}examples}} show, a small amount of Java code is simpler
+  XML is ultimately more verbose and unwieldy.  As the {{{service.html#injection}}} show, a small amount of Java code is simpler
   and easier than a big chunk of XML.
 
   In addition, moving from XML to Java code makes things easier to test; you can unit test the builder methods of your
@@ -108,7 +108,7 @@
   only be a single service per service interface, but in some situations, there may be many different services and service implementations
   all sharing the same service interface.
   
-  Services have a visibility: either public (the default) or private (only visible within the same module).  <Not yet implemented>
+  Services have a visibility: either public (the default) or private (only visible within the same module). 
   
   Services are identified by a unique id, which combines an unqualified id for the service with the containing module's id (see below).
   Typically, a service id matches the name of the service interface.
@@ -118,9 +118,10 @@
   * A module defines a <<module id>> that is used as a prefix when naming services within the module. This is very much equivalent
     to a Java package name.  Module ids must be unique within the registry of modules.
     
-  * A module is defined by a <<module builder>>, a specific class that is intantiated.
+  * A module is defined by a <<module builder>>, a specific class that is instantiated.
   
-  * Methods on the builder class define the services provided by the module, and the same methods are responsible
+  * Methods of the module builder class define the services provided by the module, 
+    and the same methods are responsible
     for instantiating the service implementation.
     
   []

Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/module.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/module.apt?rev=419906&r1=419905&r2=419906&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/module.apt (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/module.apt Fri Jul  7 08:17:39 2006
@@ -39,8 +39,8 @@
   org.example.myapp.services.Indexer.
 
   We could extend this example by adding additional builder methods, or by showing
-  how to inject dependencies. See {{{service.html}the service documentation}} for 
-  more details.
+  how to inject dependencies. See {{{service.html#Injecting Dependencies}the service documentation}} 
+  for more details.
 
 Overriding the Module id
 
@@ -75,7 +75,8 @@
   
   
   The manifest entry name is "Tapestry-Module-Classes".  The value is a comma-seperated list
-  of fully qualified class names of module builder classes.  Whitespace is ignored.
+  of fully qualified class names of module builder classes (this allows a single
+  JAR to contain multiple, related modules).  Whitespace is ignored.
   
   Example:
   
@@ -84,7 +85,7 @@
 Tapestry-Module-Classes: org.example.mylib.LibModule, org.example.mylib.internal.InternalModule
 +-----------------------------------------------------------------------------------+
 
-  If you are using Maven 2, then  the easiest way to get such entries into your JAR's manifest
+  If you are using Maven 2, then  getting these entries into your JAR's manifest
   is as simple as some configuration in your pom.xml:
   
 +-----------------------------------------------------------------------------------+
@@ -126,8 +127,8 @@
   services are generally instantiated only as needed, methods will be
   invoked in an arbitrary order, and some may never be invoked.
   
-  Again, keep the methods very simple. Use {{{service.html}parameter injection}}
-  to gain access to the dependencies you need. Don't worry about caching, caching of
+  Again, keep the methods very simple. Use {{{service.html#Injecting Dependencies}parameter injection}}
+  to gain access to the dependencies you need. Don't worry about caching; caching of
   services is the responsibility of Tapestry.
   
   Be careful about inheritance. Tapestry will see all <public> methods,

Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/service.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/service.apt?rev=419906&r1=419905&r2=419906&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/service.apt (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/service.apt Fri Jul  7 08:17:39 2006
@@ -8,7 +8,7 @@
   
   The service interface is how the service will be represented throughout the rest of the
   registry. Since what gets passed around is normally a proxy, you can't expect to cast a service
-  object down to the implementation class (you'll get see ClassCastException instead). In other
+  object down to the implementation class (you'll see a ClassCastException instead). In other
   words, you should be careful to ensure that your service interface is complete, since
   Tapestry IoC effectively walls you off from backdoors such as casts.
   
@@ -33,7 +33,7 @@
   service implementation of the Indexer service), but it does know 
   about the buildIndexer() method.
   
-Injecting Dependencies
+{Injecting Dependencies}
 
   It's pretty unlikely  that your service will be able to operate in a total vacuum. It will
   have other dependencies.  For example, let's say the Indexer needs a JobScheduler to control
@@ -43,13 +43,13 @@
   public Indexer buildIndexer(@InjectService("JobScheduler")
     JobScheduler scheduler, @InjectService("FileSystem")
     FileSystem fileSystem)
-    {
-      IndexerImpl indexer = new IndexerImpl(fileSystem);
+  {
+    IndexerImpl indexer = new IndexerImpl(fileSystem);
       
-      scheduler.scheduleDailyJob(indexer);
+    scheduler.scheduleDailyJob(indexer);
       
-      return indexer;
-    }
+    return indexer;
+  }
 +-----------------------------------------------------------------------------------+
 
   Here we've annotated the parameters of the builder method to identify what
@@ -65,8 +65,6 @@
   If we used fully qualified service ids, such as "org.examples.myapps.jobs.JobScheduler",
   then we can access services from some other module entirely.
   
-  <Note: Not yet implemented.>
-  
 Defining Visibility
 
   Normally, a service can be referenced from any other module, simply by providing a
@@ -77,8 +75,6 @@
   
   The {{{../apidocs/org/apache/tapestry/ioc/annotations/Private.html}@Private annotation}} can
   be attached to a builder method to indicate that the service in question is private.
-
-  <Note: Not yet implemented.>  
   
 Defining Service Lifecycle
 
@@ -86,13 +82,15 @@
     
 Injecting Resources
 
-  In addition to injecting services, Tapestry will key off the parameter type to allow
+  In addition to injecting services, Tapestry will key off of the parameter type to allow
   other things to be injected.
   
   * java.lang.String: fully qualified service id for the service being created
   
   * {{{../apidocs/org/apache/tapestry/ioc/ErrorLog.html}ErrorLog}}: to which errors and warnings can be logged
   
+  * org.apache.commons.logging.Log: to which normal service logging can occur
+  
   * java.lang.Class: service interface implemented by the service to be constructed
   
   * {{{../apidocs/org/apache/tapestry/ioc/ServiceResources.html}ServiceResources}}:  access to other services
@@ -107,23 +105,23 @@
   public Indexer buildIndexer(String serviceId, Log serviceLog, @InjectService("JobScheduler")
     JobScheduler scheduler, @InjectService("FileSystem")
     FileSystem fileSystem)
-    {
-      IndexerImpl indexer = new IndexerImpl(serviceLog, fileSystem);
-      
-      scheduler.scheduleDailyJob(serviceId, indexer);
+  {
+    IndexerImpl indexer = new IndexerImpl(serviceLog, fileSystem);
       
-      return indexer;
-    }
+    scheduler.scheduleDailyJob(serviceId, indexer);
+
+    return indexer;
+  }
 +-----------------------------------------------------------------------------------+  
   
   The order of parameters is completely irrelevant. They can come first or last or be
   interspersed however you like.
   
-Unspecified Injection
+Auto Lookup Injection
 
   When injecting another service, you may choose to not provide the @InjectService annotation.
   
-  Tapestry will search the registry for service that implements the exact service interface (determined
+  Tapestry will search the registry for a service that implements the <exact> service interface (determined
   from the parameter type).  If it finds exactly one match, then the corresponding service will be
   injected.
   
@@ -137,8 +135,60 @@
   On the other hand, if you are using Tapestry IoC as part of an application, then you can save yourself
   a small amount of effort and maintenance by letting Tapestry automatically identify dependencies.
   
-  <Note: Not yet implemented.>
+  Example:
+  
++-----------------------------------------------------------------------------------+
+  public Indexer buildIndexer(JobScheduler scheduler, FileSystem fileSystem)
+  {
+    IndexerImpl indexer = new IndexerImpl(fileSystem);
+      
+    scheduler.scheduleDailyJob(indexer);
+      
+    return indexer;
+  }
++-----------------------------------------------------------------------------------+  
+
+  Assuming that JobScheduler and FileSystem are unique service interfaces, the above will work.
+  It is not necessary that Indexer, JobScheduler and FileSystem be in the same
+  module.
+  
+Mutually Dependent Services
+
+  One of the benefits of Tapestry IoC's proxy-based approach to just-in-time instantitation 
+  is the automatic support for mutually dependent services.  For example, suppose that
+  the Indexer and the FileSystem needed to talk directly to each other.  Normally, this
+  would cause a "chicken-and-the-egg" problem: which one to create first?
+
+  With Tapestry IoC, this is not even considered a special case:
+  
++-----------------------------------------------------------------------------------+
+  public Indexer buildIndexer(JobScheduler scheduler, FileSystem fileSystem)
+  {
+    IndexerImpl indexer = new IndexerImpl(fileSystem);
+  
+    scheduler.scheduleDailyJob(indexer);
   
+    return indexer;
+  }
+    
+  public buildFileSystem(Indexer indexer)
+  {
+    return new FileSystemImpl(indexer);
+  }  
++-----------------------------------------------------------------------------------+   
+  
+   Here, Indexer and FileSystem are mutually dependent. Eventually, one or the other
+   of them will be created ... let's say its FileSystem. The buildFileSystem() builder
+   method will be invoked, and a proxy to Indexer will be passed in.  Inside the
+   FileSystemImpl constructor (or at some later date), a method of the Indexer service
+   will be invoked, at which point, the builderIndexer() method is invoke. It still receives
+   the proxy to the FileSystem service.
+   
+   If the order is reversed, such that Indexer is built before FileSystem, everything still
+   works the same.
+   
+   This approach can be very powerful: I've (HLS) used it to break apart untestable
+   monolithic code into two mutually dependent halves, each of which can be unit tested.
     
         
         

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/BasicServiceCreatorTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/BasicServiceCreatorTest.java?rev=419906&r1=419905&r2=419906&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/BasicServiceCreatorTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/BasicServiceCreatorTest.java Fri Jul  7 08:17:39 2006
@@ -18,7 +18,6 @@
 import java.lang.reflect.Method;
 
 import org.apache.commons.logging.Log;
-import org.apache.tapestry.internal.annotations.SuppressNullCheck;
 import org.apache.tapestry.internal.test.InternalBaseTestCase;
 import org.apache.tapestry.ioc.ErrorLog;
 import org.apache.tapestry.ioc.ServiceResources;
@@ -49,6 +48,8 @@
 
     private Log _expectedLog;
 
+    private FoeService _expectedFoe;
+
     private Method findMethod(String name)
     {
         return findMethod(this, name);
@@ -94,7 +95,8 @@
         verify();
     }
 
-    private void trainForConstructor(ServiceDef def, ServiceResources resources, ErrorLog errorLog, Log log)
+    private void trainForConstructor(ServiceDef def, ServiceResources resources, ErrorLog errorLog,
+            Log log)
     {
         trainGetServiceId(def, SERVICE_ID);
 
@@ -137,6 +139,39 @@
     }
 
     @Test
+    public void injectedServiceMethod()
+    {
+        ServiceDef def = newServiceDef();
+        ServiceResources resources = newServiceResources();
+        ErrorLog errorLog = newErrorLog();
+        Log log = newLog();
+
+        _fie = newFieService();
+        _expectedFoe = newFoe();
+
+        trainForConstructor(def, resources, errorLog, log);
+
+        trainGetBuilderMethod(def, findMethod("build_injected"));
+
+        trainGetService(resources, "Foe", FoeService.class, _expectedFoe);
+
+        replay();
+
+        ServiceCreator sc = new BasicServiceCreator(def, this, resources);
+
+        Object actual = sc.createService();
+
+        assertSame(actual, _fie);
+
+        verify();
+    }
+
+    private FoeService newFoe()
+    {
+        return newFoeService();
+    }
+
+    @Test
     public void builderMethodReturnsNull()
     {
         ServiceDef def = newServiceDef();
@@ -213,7 +248,7 @@
     }
 
     @Test
-    public void builderMethodHasUnmatchableParameter()
+    public void autoDependency()
     {
         ServiceDef def = newServiceDef();
         ServiceResources resources = newServiceResources();
@@ -221,18 +256,15 @@
         Log log = newLog();
 
         _fie = newFieService();
+        _expectedFoe = newFoeService();
 
         trainForConstructor(def, resources, errorLog, log);
 
-        Method method = findMethod("build_invalidargs");
-
-        trainGetBuilderMethod(def, method);
-
-        // The second call is when we need to report the method as having an error.
+        Method method = findMethod("build_auto");
 
         trainGetBuilderMethod(def, method);
 
-        errorLog.warn(IOCMessages.unknownBuilderParameter(Object.class, method, SERVICE_ID), null);
+        trainGetService(resources, FoeService.class, _expectedFoe);
 
         replay();
 
@@ -245,6 +277,11 @@
         assertSame(actual, _fie);
     }
 
+    private FoeService newFoeService()
+    {
+        return newMock(FoeService.class);
+    }
+
     private FieService newFieService()
     {
         return newMock(FieService.class);
@@ -255,15 +292,18 @@
         return _fie;
     }
 
-    @SuppressNullCheck
-    public FieService build_invalidargs(Object unknown)
+    public FieService build_injected(@InjectService("Foe")
+    FoeService foe)
     {
+        assertSame(_expectedFoe, foe);
+
         return _fie;
     }
 
-    public FieService build_injected(@InjectService("Foe")
-    FoeService foe)
+    public FieService build_auto(FoeService foe)
     {
+        assertSame(_expectedFoe, foe);
+
         return _fie;
     }
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/DefaultModuleDefImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/DefaultModuleDefImplTest.java?rev=419906&r1=419905&r2=419906&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/DefaultModuleDefImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/DefaultModuleDefImplTest.java Fri Jul  7 08:17:39 2006
@@ -92,6 +92,7 @@
 
         assertEquals(sd.getBuilderMethod(), SimpleModuleBuilder.class.getMethod("buildFred"));
         assertEquals(sd.getServiceLifeycle(), IOCConstants.DEFAULT_LIFECYCLE);
+        assertEquals(sd.isPrivate(), false);
 
         sd = g.getServiceDef("ioc.Barney");
 
@@ -101,6 +102,7 @@
 
         assertEquals(sd.getBuilderMethod(), SimpleModuleBuilder.class.getMethod("buildBarney"));
         assertEquals(sd.getServiceLifeycle(), "threaded");
+        assertEquals(sd.isPrivate(), true);
 
         verify();
     }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/SimpleModuleBuilder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/SimpleModuleBuilder.java?rev=419906&r1=419905&r2=419906&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/SimpleModuleBuilder.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/SimpleModuleBuilder.java Fri Jul  7 08:17:39 2006
@@ -16,6 +16,7 @@
 
 import org.apache.tapestry.ioc.annotations.Id;
 import org.apache.tapestry.ioc.annotations.Lifecycle;
+import org.apache.tapestry.ioc.annotations.Private;
 
 /**
  * Used by {@link org.apache.tapestry.internal.ioc.DefaultModuleDefImplTest}.
@@ -31,6 +32,7 @@
     }
 
     @Lifecycle("threaded")
+    @Private
     public FoeService buildBarney()
     {
         return null;