Author: hlship
Date: Wed Oct 10 11:37:44 2007
New Revision: 583573

URL: http://svn.apache.org/viewvc?rev=583573&view=rev
Log:
TAPESTRY-1798: Injection via Marker Annotations

Modified:
    tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/index.apt
    tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/module.apt
    tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/run.apt
    tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/service.apt
    tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/shadow.apt

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/index.apt
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/index.apt?rev=583573&r1=583572&r2=583573&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/index.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/index.apt Wed Oct 10 
11:37:44 2007
@@ -8,7 +8,7 @@
   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, by overriding or replacing small pieces of the 
system.
+  modify and extend the system, by overriding or replacing selected parts 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
@@ -19,7 +19,7 @@
   because of HiveMind's flexibility.
   
   Tapestry 5 extends on this, replacing HiveMind with a new container 
specifically build for Tapestry 5,
-  designed for greater ease of use, expressiveness and performance.  And it 
can be used seperately from the rest of Tapestry!
+  designed for greater ease of use, expressiveness and performance.  And it 
can be used separately from the rest of Tapestry!
   
 * Why Not Spring?
 
@@ -64,6 +64,16 @@
   
   Tapestry IoC also represents many simplifications of HiveMind, representing 
lessons learned while creating both
   HiveMind and Tapestry 4.
+
+* Why not Guice?
+
+  {{{http://code.google.com/p/google-guice/}Google Guice}} is a newcomer to 
the IoC landscape.  Guice and T5 IoC are very close and, in fact,
+  T5 IoC expressly borrows many great and innovative ideas from Guice. Guice 
abandons not only XML but even any concept of a service id ...
+  for injection, services are matched by type and perhaps filtered based on 
annotations.
+
+  Guice is still missing some core ideas needed in T5 IoC.  There's no concept 
of configurations or anything similar.
+  And there are limitations on injection based on scope (a request scoped 
value can't be injected into a global scope service; in T5 IoC, scope
+  is internal to the proxy and never an issue).
   
 Goals
 
@@ -104,7 +114,7 @@
   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.
   
-   In software development, we are also attempting to create complex systems
+  In software development, we are also attempting to create complex systems
   from simple pieces, but our tension is derived from the need to add 
functionality balanced against the need
   to test and maintain existing code.  Too often in the world of software 
development, the need to add functionality
   trumps all, and testing and maintenance is deferred ... until too late.
@@ -151,7 +161,7 @@
   Services are aggregated into <<modules>>:
       
   * A module is defined by a <<module builder>>, a specific class containing a 
mix of static or instance methods, used to define
-   services, decorate them (see below), or contribute to service 
configurations (again, more below).
+    services, decorate them (see below), or contribute to service 
configurations (again, more below).
   
   * Methods of the module builder class define the services provided by the 
module, 
     and the same methods are responsible
@@ -163,7 +173,7 @@
   
   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 unique id or by its service interface.  Access by unique 
id is <caseless> (meaning, a match will be found
-  even the case of the search key doesn't match the case of the service 
itself). 
+  even the case of the search key doesn't match the case of the service id 
itself). 
     
     
   Services may be <<decorated>> by <<service decorator methods>>.  These 
methods create
@@ -173,12 +183,12 @@
   Control is given over the order in which decorators are applied to a service.
   
   A service may have a <<configuration>>. The configuration is either a map, a 
collection, or an ordered list. The service defines the type
-  of object allowed to be contributed into the configuration. The 
configuration is contructed
+  of object allowed to be contributed into the configuration. The 
configuration is constructed
   from <<contributions>> provided by one or more modules.   <<Service 
contributor methods>> are invoked to contribute objects into
   configurations.
   
   <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,
+  to linked pairs of similarly named services and configurations. For Tapestry 
IoC, each service is allowed to have a single configuration,
   which is normally sufficient.>
   
   Services are instantiated as needed. In this case, "need" translates to 
"when a method of the service is invoked".
@@ -187,7 +197,7 @@
   constructed. This occurs in a completely <<thread-safe>> manner. 
Just-in-time instantiation allows for more complex, more finely grained 
networks of services, and improves
   startup time.
   
-  Services define a <<scope>> that controls when the service is constructed, 
as well as its visiblity.  The default scope is <<singleton>>, meaning a single
+  Services define a <<scope>> that controls when the service is constructed, 
as well as its visibility.  The default scope is <<singleton>>, meaning a single
   global instance created as needed.  Other scopes allow service 
implementations to be bound to the current thread (i.e., the current
   request in a servlet application).
   

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/module.apt
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/module.apt?rev=583573&r1=583572&r2=583573&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/module.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/module.apt Wed Oct 10 
11:37:44 2007
@@ -9,7 +9,22 @@
   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.
+  A module class exists for the following reasons:
+
+  * To <bind> service interfaces to service implementations
+
+  * To contribute configuration data <into> services
+
+  * To <decorate> services by providing <interceptors> around them
+
+  * To provide explicit code for building a service
+
+  []
+
+  A module builder defines builder methods, one for each service provided by 
the module.
+
+  <<This page needs to be rewritten or reorganized; using the bind() method is 
the preferred way to define services,
+  service builder methods are now used in very limited circumstances.>>
 
   Service builder methods are public methods. They are often static. Here's a 
trivial example:
 
@@ -79,11 +94,18 @@
 
   The {{{service.html}service}} documentation goes into much greater detail 
about autobuilding of services. In most cases,
   autobuilding is the <preferred> approach.
+
+  Generally speaking, you should always bind and autobuild your services. The 
only exceptions are when:
+
+  * You wish to do more than just instantiate a class; for example, to 
register the class as an event listener with some other service.
+
+  * There is <no implementation class>; in some cases, you can create your 
implementation on the fly using JDK dynamic proxies or bytecode generation.
   
 {Cacheing Services}
 
-  You will often find yourself in the position of injecting the same services
-  into your service builder or service decorator methods repeatedly. This can 
be quite
+  You will occasionally find yourself in the position of injecting the same 
services
+  into your service builder or service decorator methods repeatedly (this 
occurs much less often since the introduction of
+  service autobuilding). This can result in quite
   a bit of redundant typing.  Less code is better code, so as an alternative, 
you may define a <constructor> for your
   module that accepts annotated parameters (as with 
   {{{service.html#Injecting Dependencies}service builder injection}}).
@@ -143,10 +165,10 @@
   built by different threads simultaneously. Each module builder class is 
instantiated at most once, and
   making these fields final ensures that the values are available across 
multiple threads.
   Refer to Brian Goetz's {{{http://www.javaconcurrencyinpractice.com/}Java 
Concurrency in Practice}}
-  for a more complete explantation of the relationship between final fields, 
constructors, and threads ...
+  for a more complete explanation of the relationship between final fields, 
constructors, and threads ...
   or just trust us!
   
-  Care should be taken with this approach: in some circustances, you may force 
a situtation in which
+  Care should be taken with this approach: in some circumstances, you may 
force a situation in which
   the module constructor is dependent on itself. For example, if you invoke a 
method on any injected services
   defined within the same module from the module builder's constructor,
   then the service implementation will be needed. Creating service 
implementations
@@ -159,7 +181,7 @@
   When setting up the registry, Tapestry can automatically locate modules 
packaged into JARs.
   It does this by searching for a particular global manifest entry. 
   
-  The manifest entry name is "Tapestry-Module-Classes".  The value is a 
comma-seperated list
+  The manifest entry name is "Tapestry-Module-Classes".  The value is a 
comma-separated list
   of fully qualified class names of module builder classes (this allows a 
single
   JAR to contain multiple, related modules).  Whitespace is ignored.
   

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/run.apt
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/run.apt?rev=583573&r1=583572&r2=583573&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/run.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/run.apt Wed Oct 10 
11:37:44 2007
@@ -44,7 +44,7 @@
 Building the Default Registry
 
   The default registry is available by invoking the static method
-  
{{{../apidocs/org/apache/tapestry/ioc/IOCUtilities.html#buildDefaultRegistry()}ICCUtilities.buildDefaultRegistry()}}.
+  
{{{../apidocs/org/apache/tapestry/ioc/IOCUtilities.html#buildDefaultRegistry()}IOCUtilities.buildDefaultRegistry()}}.
   This method builds a Registry using
   {{{module.html#Autoloading modules}autoloading logic}}, where modules to load
   are identified via a JAR Manifest entry.

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=583573&r1=583572&r2=583573&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/service.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/service.apt Wed Oct 10 
11:37:44 2007
@@ -12,6 +12,34 @@
   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.
   
+Service Life-cycle
+
+  Every service has a very specific life-cycle.
+
+  * Defined: The service has a definition (from some module) but has not yet 
been referenced.
+
+  * Virtual: The service has been referenced, so a proxy for the class has 
been created.
+
+  * Realized: A method on the proxy has been invoked, so the service 
implementation has 
+    been instantiated, and any decorators applied.
+
+  * Shutdown: The entire Registry has been shut down and with it, all the 
proxies have been disabled.
+
+  []
+
+  When the Registry is first created, all modules are scanned and the 
definitions for all services
+  are created.  
+
+  Services will be referenced by either accessing them using the Registry, or 
as dependencies
+  of other realized services.  
+
+  Tapestry IoC waits until the last possible moment to <realize> the service: 
that's defined
+  as when a method of the service is invoked. Tapestry is <thread-safe>, so 
even in a heavily
+  contested, highly threaded envrionment (such as a servlet container or 
application server) 
+  things <Just Work>.
+
+Service Builder Methods
+
   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 service builder method:
   
@@ -36,16 +64,18 @@
   all the different ways possible to create a service; those things are best 
expressed in Java code.
   For a simple case (as here), it would be hard for external configuration 
(again, in XML or Java annotations)
   to be shorter than "new IndexerImpl()".
+
+  <The above paragraph was written before Binding and Autobuilding were 
introduced.>
   
   For more complex and realistic scenarios, such as injecting dependencies via 
the constructor, or
   doing more interest work (such as registering the newly created service for 
events published by some other service),
   the Java code is simply the most direct, flexible, extensible and readable 
approach.
   
-Autobuilding
+Binding and Autobuilding
+
+  Tapestry IoC can also <autobuild> your service. Autobuilding is the 
<preferred> way to 
+  instantiate your services.
 
-  Tapestry IoC can also <autobuild> your service. Autobuilding is an alternate 
way to register services
-  with the container.
-  
   Every module may have an optional, static bind() method which is passed a
   {{{../apidocs/org/apache/tapestry/ioc/ServiceBinder.html}ServiceBinder}}.  
Services may be registered with
   the container by "binding" a service interface to a service implementation:
@@ -64,11 +94,12 @@
 }
 +----+
 
-  You can make repeated calls to bind(), to register more services.
+  You can make repeated calls to ServiceBinder.bind(), to bind additional 
services.
 
 
   You might ask, "which is better, a builder method for each service, or a 
bind() method for the module?"  For simple services,
-  those that are just an instantiated instance with simple dependencies, 
binding is better than building.
+  those that are just an instantiated instance with simple dependencies, 
binding is better than building. That covers
+  at least 90% of all services, so bind away!
   
   There are many cases, however, where constructing a service is more than 
just instantiating a class. Often the new service
   will (for example) be registered as a listener with some other service. In 
other cases, the implementation of the
@@ -171,7 +202,95 @@
   {{{module.html#Caching Services}cache dependency injections}} in your 
module, by defining
   a constructor.  This reduces duplication in your module.
  
-  
+Disambiguation with Marker Annotations
+
+  In the previous example we were faced with a problem: multiple versions of 
the JobScheduler
+  service.  They had the same service interface but unique service ids.  If 
you try to inject
+  based on type, the service to inject will be ambiguous.  Tapestry will throw 
an exception (identifying
+  the parameter type and the matching services that implement that type).
+
+  The problem is that when injecting a JobScheduler into some other service we 
need to know 
+  which <one> to inject. Rather than using the service id, another approach is 
to
+  use a <marker annotation>.
+
+  You may optionally link a service implementation with a marker annotation.
+
+  For example, maybe you have one JobScheduler implementation where the jobs 
are spread across
+  a number of nodes in a cluster, and you have another JobScheduler where the 
jobs are all executed exclusively
+  in the current process.
+
+  We can associate those two JobSchedulers with two annotations.
+
++----+
[EMAIL PROTECTED](
+{ PARAMETER, FIELD })
[EMAIL PROTECTED](RUNTIME)
[EMAIL PROTECTED]
+public @interface Clustered
+{
+
+}
+
[EMAIL PROTECTED](
+{ PARAMETER, FIELD })
[EMAIL PROTECTED](RUNTIME)
[EMAIL PROTECTED]
+public @interface InProcess
+{
+
+}
+
+
+public class MyModule
+{
+  public static void bind(ServiceBinder binder)
+  {
+    binder.bind(JobScheduler.class, 
ClusteredJobSchedulerImpl.class).withId("ClusteredJobScheduler").withMarker(Clustered.class);
+    binder.bind(JobScheduler.class, 
SimpleJobSchedulerImpl.class).withId("InProcessJobScheduler").withMarker(InProcess.class);
+  }
+}
++---+
+
+  Notice that the marker annotations have no attributes.  Further, we support 
markers on fields
+  (for use in Tapestry components) as well as parameters.
+
+  To get the right version of the service, you use one of the annotations:
+
++---+
+public class MyServiceImpl implements MyService
+{
+  private final JobScheduler _jobScheduler;
+
+  public MyServiceImpl(@Clustered JobScheduler jobScheduler)
+  {
+    _jobScheduler = jobScheduler;
+  }
+
+  . . .
+}  
++---+
+
+  The @Clustered annotation on the parameter is combined with the parameter 
type (JobScheduler) to find the exact
+  service implementation.
+
+  Why is this better than using the service id?  It's more refactoring-safe.  
Service ids can change, which can break
+  your services.  However, using an IDE to rename or move an annotation class 
or service interface
+  will be able to update all the uses of the annotation or interface.
+
+  With a service builder method, you use the
+  {{{../apidocs/org/apache/tapestry/ioc/annotations/[EMAIL PROTECTED] 
annotation:
+
++---+
+  @Marker(Clustered.class)
+  public JobScheduler buildClusteredJobScheduler()
+  {
+    return . . .;
+  }
++---+
+
+  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.
+
 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/site/apt/shadow.apt
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/shadow.apt?rev=583573&r1=583572&r2=583573&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/shadow.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/shadow.apt Wed Oct 10 
11:37:44 2007
@@ -10,31 +10,31 @@
   
   Effectively, it is used to allow a property of another service to be exposed 
as its own service.
   
-  For example, the tapestry-core module provides a WebRequest property as a 
shadow of the RequestGlobals
+  For example, the tapestry-core module provides a Request property as a 
shadow of the RequestGlobals
   service's request property:
   
 +----+
-public WebRequest build()
+public Request build()
 {
-  return _shadowBuilder.build(_requestGlobals, "request", WebRequest.class);
+  return _shadowBuilder.build(_requestGlobals, "request", Request.class);
 }
 +----+
 
   This can be thought of as similar to:
   
 +----+
-public WebRequest build()
+public Request build()
 {
   return _requestGlobals.getRequest();
 }
 +----+
     
   However there is a <critical> difference between the two:  a shadow property 
is <re-evaluated on each method invocation>.
-  In the former case, the WebRequest service will always obtain the current 
value of the request property from the
+  In the former case, the Request service will always obtain the current value 
of the request property from the
   per-thread RequestGlobals service. The second example is more than likely 
broken, since it will expose whatever
-  value is in the request property of the RequestGlobals <at the time the 
WebRequest service implementation is created>.
+  value is in the request property of the RequestGlobals <at the time the 
Request service implementation is created>.
   
-  Notice that in this example, the WebRequest service is a normal singleton. 
This service can be freely injected
+  Notice that in this example, the Request service is a normal singleton. This 
service can be freely injected
   into any service throughout the framework or application. Invoking methods 
on this service will always delegate
   to the current thread's request. Callers don't have to be aware of this 
internal delegation; it just happens.    
   


Reply via email to