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 00:40:19 UTC
svn commit: r419727 - in /tapestry/tapestry5/tapestry-core/trunk: ./
src/main/java/org/apache/tapestry/internal/ioc/
src/main/java/org/apache/tapestry/ioc/
src/main/java/org/apache/tapestry/ioc/annotations/
src/main/java/org/apache/tapestry/test/ src/s...
Author: hlship
Date: Thu Jul 6 15:40:18 2006
New Revision: 419727
URL: http://svn.apache.org/viewvc?rev=419727&view=rev
Log:
Initial documentation concerning Tapestry IoC.
Support a Log for each service, anonymously injectable, or via ServiceResources.
Added:
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/annotations/InjectService.java
tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/
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
Modified:
tapestry/tapestry5/tapestry-core/trunk/pom.xml
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/ioc/ServiceResources.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/BaseTestCase.java
tapestry/tapestry5/tapestry-core/trunk/src/site/site.xml
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/BasicServiceCreatorTest.java
Modified: tapestry/tapestry5/tapestry-core/trunk/pom.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/pom.xml?rev=419727&r1=419726&r2=419727&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/pom.xml (original)
+++ tapestry/tapestry5/tapestry-core/trunk/pom.xml Thu Jul 6 15:40:18 2006
@@ -92,7 +92,9 @@
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-javadoc-plugin</artifactId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <!-- Had a classloader problem with version 2.0.3 -->
+ <version>2.0</version>
<configuration>
<tags>
<tag>
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=419727&r1=419726&r2=419727&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 Thu Jul 6 15:40:18 2006
@@ -15,10 +15,10 @@
package org.apache.tapestry.internal.ioc;
import java.lang.annotation.Annotation;
-import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
+import org.apache.commons.logging.Log;
import org.apache.tapestry.ioc.ErrorLog;
import org.apache.tapestry.ioc.ServiceResources;
import org.apache.tapestry.ioc.def.ServiceDef;
@@ -55,6 +55,7 @@
_parameterDefaults.put(String.class, _serviceId);
_parameterDefaults.put(ServiceResources.class, resources);
_parameterDefaults.put(ErrorLog.class, _log);
+ _parameterDefaults.put(Log.class, resources.getServiceLog());
_parameterDefaults.put(Class.class, serviceDef.getServiceInterface());
}
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=419727&r1=419726&r2=419727&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 Thu Jul 6 15:40:18 2006
@@ -14,6 +14,8 @@
package org.apache.tapestry.ioc;
+import org.apache.commons.logging.Log;
+
/**
* Contains resources that may be provided to a service when it initializes, which includes other
* services visible to the service.
@@ -47,4 +49,12 @@
/** Returns an error log appropriate for the service. */
ErrorLog getErrorLog();
+
+ /**
+ * Returns a Log object appropriate for logging messages. Use of {@link #getErrorLog()} is
+ * better, but that's only for error situations, not ordinary logging (for debug of
+ * informational purposes).
+ */
+
+ Log getServiceLog();
}
Added: 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=419727&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/annotations/InjectService.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/annotations/InjectService.java Thu Jul 6 15:40:18 2006
@@ -0,0 +1,39 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.ioc.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Target(PARAMETER)
+@Retention(RUNTIME)
+@Documented
+/**
+ * Annotation used with parameters of builder methods to identify the service to be injected into
+ * the builder method via the parameter.
+ */
+public @interface InjectService {
+
+ /**
+ * The id of the service to inject; either a fully qualified id, or the unqualified id of a
+ * service within the same module.
+ */
+ String value();
+}
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=419727&r1=419726&r2=419727&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 Thu Jul 6 15:40:18 2006
@@ -187,4 +187,11 @@
return newMock(ServiceDef.class);
}
+ protected final void trainGetServiceLog(ServiceResources resources, Log log)
+ {
+ resources.getServiceLog();
+ setReturnValue(log);
+
+ }
+
}
Added: 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=419727&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/index.apt (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/index.apt Thu Jul 6 15:40:18 2006
@@ -0,0 +1,158 @@
+ ----
+ Tapestry IoC Introduction
+ ----
+
+Tapestry Inversion of Control Container
+
+ The inner construction of the Tapestry framework is based on {{{http://www.martinfowler.com/articles/injection.html}inversion of control}},
+ a design approach that allows a working system to be fabricated from many small, easily testable pieces.
+
+ An additional benefit of using IoC (Inversion of Control) is that, by breaking a complex system into small pieces, it becomes easier to
+ modify and extend the system, but overriding or replacing small pieces of the system.
+
+ The use of IoC in Tapestry represents an evolution from Tapestry 3 to Tapestry 4 to Tapestry 5. Tapestry 3 did not use IoC, though it included
+ some weaker mechanisms, such as extensions, that served a similar purpose. To make large scale changes to the behavior of Tapestry 3 required
+ subclassing key classes and overriding methods.
+
+ Tapestry 4 introduced the use of the {{{http://hivemind.apache.org/}HiveMind}} IoC container. In fact, the HiveMind project was created
+ specifically for use as the IoC container for Tapestry 4. Tapestry 4 has met its goals for extensibility and configurability, largely
+ because of HiveMind's flexibility.
+
+ Tapestry 5 extends on this, replacing HiveMind with a new container built into the framework,
+ designed for greater ease of use and expressiveness.
+
+* Why Not Spring?
+
+ {{{http://www.springframework.org}Spring}} is the most successful IoC container project. The Spring project combines a very good IoC container,
+ integrated {{{http://aspectj.org}AspectJ}} support, and a large number of libraries built on top of the container. Spring is an excellent
+ <application> container, but lacks a number of features necessary for a <framework> container:
+
+ * Spring does not have the concept of a <namespace>. The names of beans are simple and unqualified, which could lead to naming conflicts.
+
+ * Spring beans can be wired together by name (or id), but it is not possible to introduce additional naming abstractions. Tapestry 4's
+ "infrastructure:" abstraction was the key to allowing easy spot overrides of internal Tapestry services.
+
+ * Spring has a simple map/list/value configuration scheme, but it is not distributed; it is part of a single bean definition.
+ HiveMind and Tapestry 5 IoC allow service configuration to be assembled from multiple modules. This is very important
+ for seamless extensibility of the framework.
+
+* Why Not HiveMind?
+
+ The difficulty of managing the release schedules of two complex frameworks has proven to be an issue. HiveMind's 2.0 release will
+ incorporate similar ideas to Tapestry 5 IoC, but will also maintain legacy support for the existing XML-driven approach.
+
+ The use of HiveMind is also related to one of the common criticisms of Tapestry 4: startup time. The time it takes to parse and
+ organize all that XML shows up as several seconds of startup time. It is <hoped> that creating a streamlined IoC container that is not
+ driven by XML will alleviate those issues.
+
+ With the advent of new technologies (in particular,
+ {{{http://java.sun.com/j2se/1.5.0/docs/guide/language/annotations.html}JDK 1.5 Annotations}},
+ and runtime class generation via
+ {{{http://www.jboss.org/products/javassist}Javassist}})
+ some of the precepts of HiveMind have been undermined. That is to say, in HiveMind (and Spring), all that XML is an awkward
+ way to describe a few basic Java operations: instantiating classes and invoking methods on those classes (to inject
+ dependencies into the instantiated instances). The central concept in Tapestry IoC is to eliminate XML and build an equivalent
+ system around simple objects and methods.
+
+Goals
+
+ 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
+ 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
+ module builder class, but you can't realistically unit test an XML descriptor.
+
+ Modules should be easily packaged into JAR files and should be zero-configuration (just drop the JAR onto the classpath).
+
+ Another goal is "developer friendliness". This is a true cross-cutting concern, and one not likely to be packaged
+ into an aspect any time soon. The Tapestry IoC framework is designed to be easy to use and easy to understand.
+ Further, when things go wrong, it actively attempts to help you by careful checks and careful error messages. Further,
+ all user-visible objects implements {{{http://howardlewisship.com/blog/2003/08/importance-of-tostring.html}a reasonable toString() method}},
+ to help you understand what's going when you inevitably try to figure things out in the debugger.
+
+ In terms of building services using Tapestry IoC ... the objective here is "lightness", a term borrowed from the board
+ game {{{http://boardgamegeek.com/game/188}Go}}. In Go, groups of playing stones are "light" (or have "good shape")
+ when the minimum number of them control the maximum area on the board. Playing "heavy" just gives your opponent a free
+ chance to take control of another section of the board.
+
+ When building a registry of services, lightness refers to the proper division of responsibility, the seperation of
+ concerns, and the limiting of dependencies between different parts of the system. This style is often
+ called {{{http://www.ccs.neu.edu/research/demeter/demeter-method/LawOfDemeter/general-formulation.html}Law of Demeter}}.
+ Using an IoC container makes it easier to embrace this approach, since one critical concern, which objects are responsible for
+ instantiating which others, is entirely managed by the container. With this lifecycle concern removed, it becomes very
+ easy to reduce complex chunks of code into small, testable, reusable services.
+
+ "Light" means:
+
+ * Small interfaces of two or three methods.
+
+ * Small methods, with two or three parameters (because dependencies are injected in behind the scenes, rather than
+ passed into the method).
+
+ * Anonymous communication via events, rather than explicit method invocations. The service implementation can
+ implement an event listener interface.
+
+ []
+
+ See {{{http://www.pragmaticprogrammer.com/ppbook/index.shtml}The Pragmatic Programmer}} for more insights into
+ building solid code.
+
+Terminology
+
+ The basic unit in Tapestry IoC is a <<service>>. A service consists of a <<service interface>> and a <<service implementation>>.
+ The service interface is an ordinary Java interface. The service implementation is a Java object that implements the
+ service interface. Often there will
+ 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 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.
+
+ Services are aggregated into <<modules>>:
+
+ * 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.
+
+ * Methods on the builder class define the services provided by the module, and the same methods are responsible
+ for instantiating the service implementation.
+
+ []
+
+ The methods which define and construct services are called <<builder methods>>.
+
+ The <<registry>> is the outside world's view of the modules and services. From the registry, it is possible to obtain
+ a service, via its qualified id or by its service interface.
+
+ Services may be <<intercepted>>. Interceptors are new classes that implement the service interface, and wrap around the service implementation.
+ A service may have multiple interceptors, forming a chain of delegation.
+
+ Interceptors are defined by additional methods on the module builder. <TBD>.
+ Control is given over the order in which interceptors are applied to a service.
+
+ A service may have a <<configuration>>. The configuration is either a map, a collection, or an ordered list. The configuration is contructed
+ from <<contributions>> provided by one or more modules.
+
+ <Note: In HiveMind, services and configurations were separate, which often lead
+ to linked pairs of similarily named services and configurations. For Tapestry IoC, each service is allowed to have a single configuration,
+ which is normally sufficient.>
+
+ Services are typically instantiated as needed. In this case, "need" translates to "when a method of the service is invoked".
+ In nearly all cases, a service is represented (to the outside world, or to other services) as a <<proxy>> that implements
+ the service interface. The first time a method is invoked on the proxy, the full service (implementation and interceptors) is
+ constructed. This occurs in a completely <<thread-safe>> manner.
+
+ Services define a <<lifecycle>> that controls when the service is constructed. The default lifecycle is <<singleton>>, meaning a single
+ global instance created as needed. Other lifecycles allow service implementations to be bound to the current thread (i.e., the current
+ request in a servlet application).
+
+ <<Dependencies>> are other services (or other objects) that are needed by a service implementation. These
+ dependencies can be <<injected>> into a builder method and provided, from there, to a service implementation via
+ its constructor, or via methods on the service implementation.
+
\ No newline at end of file
Added: 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=419727&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/module.apt (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/module.apt Thu Jul 6 15:40:18 2006
@@ -0,0 +1,137 @@
+ ---
+ Tapestry IoC Modules
+ ---
+
+Tapestry IoC Modules
+
+ You inform Tapestry about your services and contributions by providing a module builder class.
+
+ The module builder is a plain Java class. A system of annotations and naming conventions allow
+ Tapestry to determine what services are provided by the module.
+
+ A module bulider defines builder methods, one for each service provided by the module.
+
+ Builder methods are public methods. Here's a trivial example:
+
++-----------------------------------------------------------------------------------+
+package org.example.myapp.services;
+
+public class MyAppModule
+{
+ public Indexer buildIndexer()
+ {
+ return new IndexerImpl();
+ }
+}
++-----------------------------------------------------------------------------------+
+
+ By default, a module's id is the same as its package name (we'll see how to override
+ that shortly). Here the module id will be org.example.myapp.services.
+
+ Any public method whose name starts with "build" is a builder method, implicitly
+ defining a service within the module. Here we're defining a service around
+ the Indexer service interface (presumably also in the org.example.myapp.services
+ package).
+
+ The service's unqualified id (the part after the module id) is derived from the method name.
+ Here "build" was stripped of "buildIndexer", leaving "Indexer". That's the unqualified id.
+ Prefixing with the module id results in the fully qualfied id
+ 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.
+
+Overriding the Module id
+
+ Adding an {{{../apidocs/org/apache/tapestry/ioc/annotations/Id.html}@Id annnotation}}
+ to the module builder class will control the module's id.
+
++-----------------------------------------------------------------------------------+
+package org.example.myapp.services;
+
+@Id("myapp")
+public class MyAppModule
+{
+ public Indexer buildIndexer()
+ {
+ return new IndexerImpl();
+ }
+}
++-----------------------------------------------------------------------------------+
+
+ This time, the module's id is "myapp" and the service's id is "myapp.Indexer".
+
+ Remember that module ids must be unique, no other module builder class, even one
+ in a different package, may declare the id "myapp", or runtime warnings or
+ errors will occur.
+
+
+Autoloading modules
+
+ When setting up the registry, Tapestry can automatically locate modules packaged into JARs.
+ It does this by searching for a particular global manifest entry.
+ <Note: Not yet implemented.>
+
+
+ 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.
+
+ Example:
+
++-----------------------------------------------------------------------------------+
+Manifest-Version: 1.0
+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
+ is as simple as some configuration in your pom.xml:
+
++-----------------------------------------------------------------------------------+
+<project>
+ . . .
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <archive>
+ <manifestEntries>
+ <Tapestry-Module-Classes>org.example.mylib.LibModule,
+ org.example.mylib.internal.InternalModule</Tapestry-Module-Classes>
+ </manifestEntries>
+ </archive>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ . . .
+</project>
++-----------------------------------------------------------------------------------+
+
+
+ More details are provided in the
+ {{{http://maven.apache.org/guides/mini/guide-manifest.html}Maven Manifest Guide}}.
+
+
+Module Builder Implementation Notes
+
+ Module builder classes are designed to be very, very simple to implement.
+
+ They must have a no-arguments public constructor.
+
+ You should <<not>> attempt to cache services. There is really no way to
+ determine in what order methods of the builder class will be invoked. Because
+ 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
+ services is the responsibility of Tapestry.
+
+ Be careful about inheritance. Tapestry will see all <public> methods,
+ even those inherited from base classes. Tapestry <only> sees public methods.
+
+ By convention, module builder class names end in Module.
+
\ No newline at end of file
Added: 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=419727&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/service.apt (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/apt/ioc/service.apt Thu Jul 6 15:40:18 2006
@@ -0,0 +1,144 @@
+ ----
+ Tapestry IoC Services
+ ----
+
+Tapestry IoC Services
+
+ Services consist of two main parts: a service interface and a service implementation.
+
+ 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
+ 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.
+
+ Tapestry doesn't know how to instantiate and configure your service; instead it relies
+ on you to provide the code to do so, in a builder method:
+
++-----------------------------------------------------------------------------------+
+package org.example.myapp.services;
+
+@Id("myapp")
+public class MyAppModule
+{
+ public Indexer buildIndexer()
+ {
+ return new IndexerImpl();
+ }
+}
++-----------------------------------------------------------------------------------+
+
+ Here the service interface is Indexer (presumably inside the org.example.myapp.services package,
+ since there isn't an import). Tapestry doesn't know about the IndexerImpl class (the
+ service implementation of the Indexer service), but it does know
+ about the buildIndexer() method.
+
+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
+ when it executes, and a FileSystem to access files and store indexes.
+
++-----------------------------------------------------------------------------------+
+ public Indexer buildIndexer(@InjectService("JobScheduler")
+ JobScheduler scheduler, @InjectService("FileSystem")
+ FileSystem fileSystem)
+ {
+ IndexerImpl indexer = new IndexerImpl(fileSystem);
+
+ scheduler.scheduleDailyJob(indexer);
+
+ return indexer;
+ }
++-----------------------------------------------------------------------------------+
+
+ Here we've annotated the parameters of the builder method to identify what
+ service to inject for that parameter. We used unqualified ids ... these are the names
+ of other services within the <same module>: there will be a buildJobScheduler()
+ and a buildFileSystem() builder methods as well.
+
+ Note that we don't invoke those builder methods ... we just "advertise" that we need
+ the named services. Tapestry IoC will provide the necessary proxies and, when we start to
+ invoke methods on those proxies, will ensure that the full service, including its
+ interceptors and its dependencies, are ready to go.
+
+ 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
+ fully qualified service id.
+
+ In some cases, it is desirable to mark a service a private, in which case, it is only
+ visible within the same module.
+
+ 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
+
+ <TBD>
+
+Injecting Resources
+
+ In addition to injecting services, Tapestry will key off 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
+
+ * java.lang.Class: service interface implemented by the service to be constructed
+
+ * {{{../apidocs/org/apache/tapestry/ioc/ServiceResources.html}ServiceResources}}: access to other services
+
+ []
+
+ No annotation is needed for these cases.
+
+ Example:
+
++-----------------------------------------------------------------------------------+
+ 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);
+
+ return indexer;
+ }
++-----------------------------------------------------------------------------------+
+
+ The order of parameters is completely irrelevant. They can come first or last or be
+ interspersed however you like.
+
+Unspecified 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
+ from the parameter type). If it finds exactly one match, then the corresponding service will be
+ injected.
+
+ If it finds more than one match, that's an error.
+
+ Obviously, this is only useful when you can be sure that there's only one service in the entire registry
+ that implements the service interface. When you are building a framework for others to use, you should
+ not use this feature ... it's all too likely that some end-user application will create a second service
+ with the same implementation and break your injections.
+
+ 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.>
+
+
+
+
\ No newline at end of file
Modified: tapestry/tapestry5/tapestry-core/trunk/src/site/site.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/site/site.xml?rev=419727&r1=419726&r2=419727&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/site/site.xml (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/site/site.xml Thu Jul 6 15:40:18 2006
@@ -37,7 +37,6 @@
<body>
<links>
<item name="Tapestry" href="http://tapestry.apache.org/" />
- <item name="Hivemind" href="http://jakarta.apache.org/hivemind/" />
<item name="Apache" href="http://www.apache.org/" />
</links>
@@ -50,7 +49,13 @@
<item name="Introduction" href="/index.html"/>
<item name="Upgrade from Tapestry 4" href="/upgrade.html"/>
</menu>
-
+
+ <menu name="IoC Container">
+ <item name="Introduction" href="ioc/index.html"/>
+ <item name="Modules" href="ioc/module.html"/>
+ <item name="Services" href="ioc/service.html"/>
+ </menu>
+
${reports}
</body>
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=419727&r1=419726&r2=419727&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 Thu Jul 6 15:40:18 2006
@@ -17,10 +17,12 @@
import java.lang.reflect.InvocationTargetException;
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;
+import org.apache.tapestry.ioc.annotations.InjectService;
import org.apache.tapestry.ioc.def.ServiceDef;
import org.testng.Assert;
import org.testng.annotations.Test;
@@ -45,6 +47,8 @@
private Class _expectedServiceInterface;
+ private Log _expectedLog;
+
private Method findMethod(String name)
{
return findMethod(this, name);
@@ -67,14 +71,11 @@
{
ServiceDef def = newServiceDef();
ServiceResources resources = newServiceResources();
- ErrorLog log = newErrorLog();
+ ErrorLog errorLog = newErrorLog();
+ Log log = newLog();
_fie = newFieService();
- trainGetServiceId(def, SERVICE_ID);
-
- trainGetErrorLog(resources, log);
-
- trainGetServiceInterface(def, FieService.class);
+ trainForConstructor(def, resources, errorLog, log);
replay();
@@ -93,25 +94,34 @@
verify();
}
+ private void trainForConstructor(ServiceDef def, ServiceResources resources, ErrorLog errorLog, Log log)
+ {
+ trainGetServiceId(def, SERVICE_ID);
+
+ trainGetErrorLog(resources, errorLog);
+
+ trainGetServiceLog(resources, log);
+
+ trainGetServiceInterface(def, FieService.class);
+ }
+
@Test
public void argsMethod()
{
ServiceDef def = newServiceDef();
ServiceResources resources = newServiceResources();
- ErrorLog log = newErrorLog();
+ ErrorLog errorLog = newErrorLog();
+ Log log = newLog();
- _expectedErrorLog = log;
+ _expectedErrorLog = errorLog;
_expectedServiceId = SERVICE_ID;
_expectedServiceInterface = FieService.class;
_expectedServiceResources = resources;
+ _expectedLog = log;
_fie = newFieService();
- trainGetServiceId(def, SERVICE_ID);
-
- trainGetErrorLog(resources, log);
-
- trainGetServiceInterface(def, FieService.class);
+ trainForConstructor(def, resources, errorLog, log);
trainGetBuilderMethod(def, findMethod("build_args"));
@@ -131,14 +141,12 @@
{
ServiceDef def = newServiceDef();
ServiceResources resources = newServiceResources();
- ErrorLog log = newErrorLog();
- _fie = null;
+ ErrorLog errorLog = newErrorLog();
+ Log log = newLog();
- trainGetServiceId(def, SERVICE_ID);
+ _fie = null;
- trainGetErrorLog(resources, log);
-
- trainGetServiceInterface(def, FieService.class);
+ trainForConstructor(def, resources, errorLog, log);
trainGetBuilderMethod(def, findMethod("build_noargs"));
@@ -169,14 +177,12 @@
{
ServiceDef def = newServiceDef();
ServiceResources resources = newServiceResources();
- ErrorLog log = newErrorLog();
- _fie = null;
+ ErrorLog errorLog = newErrorLog();
+ Log log = newLog();
- trainGetServiceId(def, SERVICE_ID);
-
- trainGetErrorLog(resources, log);
+ _fie = null;
- trainGetServiceInterface(def, FieService.class);
+ trainForConstructor(def, resources, errorLog, log);
trainGetBuilderMethod(def, findMethod("build_fail"));
@@ -211,14 +217,12 @@
{
ServiceDef def = newServiceDef();
ServiceResources resources = newServiceResources();
- ErrorLog log = newErrorLog();
- _fie = newFieService();
-
- trainGetServiceId(def, SERVICE_ID);
+ ErrorLog errorLog = newErrorLog();
+ Log log = newLog();
- trainGetErrorLog(resources, log);
+ _fie = newFieService();
- trainGetServiceInterface(def, FieService.class);
+ trainForConstructor(def, resources, errorLog, log);
Method method = findMethod("build_invalidargs");
@@ -228,7 +232,7 @@
trainGetBuilderMethod(def, method);
- log.warn(IOCMessages.unknownBuilderParameter(Object.class, method, SERVICE_ID), null);
+ errorLog.warn(IOCMessages.unknownBuilderParameter(Object.class, method, SERVICE_ID), null);
replay();
@@ -257,18 +261,25 @@
return _fie;
}
+ public FieService build_injected(@InjectService("Foe")
+ FoeService foe)
+ {
+ return _fie;
+ }
+
public FieService build_fail()
{
throw new RuntimeException("Method failed.");
}
public FieService build_args(String serviceId, ServiceResources resources, ErrorLog errorLog,
- Class serviceInterface)
+ Class serviceInterface, Log log)
{
assertEquals(serviceId, _expectedServiceId);
assertSame(resources, _expectedServiceResources);
assertSame(errorLog, _expectedErrorLog);
assertSame(serviceInterface, _expectedServiceInterface);
+ assertSame(log, _expectedLog);
return _fie;
}