Author: hlship
Date: Mon Jan 5 10:19:30 2009
New Revision: 731641
URL: http://svn.apache.org/viewvc?rev=731641&view=rev
Log:
TAP5-316: Add service overriding capabilities to tapestry-ioc, similar to what
tapestry-core has (the Alias service)
Added:
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ServiceOverrideImpl.java
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ServiceOverride.java
tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/cookbook/override.apt
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/GreeterServiceOverrideModule.java
Modified:
tapestry/tapestry5/trunk/src/site/apt/cookbook/exceptions.apt
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/TapestryIOCModule.java
tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/injection.apt
tapestry/tapestry5/trunk/tapestry-ioc/src/site/site.xml
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/IntegrationTest.java
Modified: tapestry/tapestry5/trunk/src/site/apt/cookbook/exceptions.apt
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/src/site/apt/cookbook/exceptions.apt?rev=731641&r1=731640&r2=731641&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/src/site/apt/cookbook/exceptions.apt (original)
+++ tapestry/tapestry5/trunk/src/site/apt/cookbook/exceptions.apt Mon Jan 5
10:19:30 2009
@@ -224,8 +224,48 @@
[error3.png] Errors show on Index page
+Version 3: Overriding RequestExceptionHandler using ServiceOverride
+
+ The Alias service is part of tapestry-core; starting in Tapestry 5.1, there
is an additional service, part of
+ tapestry-ioc, for overriding services. It is a bit simpler than Alias:
+
+ <<AppModule.java>> (partial)
+
+----
+ public RequestExceptionHandler buildAppRequestExceptionHandler(
+ final Logger logger,
+ final ResponseRenderer renderer,
+ final ComponentSource componentSource)
+ {
+ return new RequestExceptionHandler()
+ {
+ public void handleRequestException(Throwable exception) throws
IOException
+ {
+ logger.error("Unexpected runtime exception: " +
exception.getMessage(), exception);
+
+ ExceptionReporter index = (ExceptionReporter)
componentSource.getPage("Index");
+
+ index.reportException(exception);
+
+ renderer.renderPageMarkupResponse("Index");
+ }
+ };
+ }
+
+ public void contributeServiceOverride(
+ MappedConfiguration<Class, Object> configuration,
+
+ @Local
+ RequestExceptionHandler handler)
+ {
+ configuration.add(RequestExceptionHandler.class, handler);
+ }
+----
+
+ This solution is similar to version 3, except that instead of contributing
to Alias, we contribute
+ to ServiceOverride, and use a mapped configuration.
-Version 3: Decorating the RequestExceptionHandler
+Version 4: Decorating the RequestExceptionHandler
A third option is available: we don't define a <new> service, but instead
<decorate> the existing
RequestExceptionHandler service. This approach means we don't have to make a
contribution to the Alias service.
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java?rev=731641&r1=731640&r2=731641&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
Mon Jan 5 10:19:30 2009
@@ -552,11 +552,11 @@
}
};
- configuration.add("Alias", wrapper, "after:Value,Symbol");
+ configuration.add("Alias", wrapper, "after:ServiceOverride");
- configuration.add("Asset", assetObjectProvider, "before:Alias");
+ configuration.add("Asset", assetObjectProvider,
"before:AnnotationBasedContributions");
- configuration.add("Service", new ServiceAnnotationObjectProvider(),
"before:Alias");
+ configuration.add("Service", new ServiceAnnotationObjectProvider(),
"before:AnnotationBasedContributions");
}
Added:
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ServiceOverrideImpl.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ServiceOverrideImpl.java?rev=731641&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ServiceOverrideImpl.java
(added)
+++
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ServiceOverrideImpl.java
Mon Jan 5 10:19:30 2009
@@ -0,0 +1,43 @@
+// Copyright 2009 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.ioc.internal.services;
+
+import org.apache.tapestry5.ioc.AnnotationProvider;
+import org.apache.tapestry5.ioc.ObjectLocator;
+import org.apache.tapestry5.ioc.ObjectProvider;
+import org.apache.tapestry5.ioc.services.ServiceOverride;
+
+import java.util.Map;
+
+public class ServiceOverrideImpl implements ServiceOverride
+{
+ private final Map<Class, Object> configuration;
+
+ public ServiceOverrideImpl(Map<Class, Object> configuration)
+ {
+ this.configuration = configuration;
+ }
+
+ public ObjectProvider getServiceOverrideProvider()
+ {
+ return new ObjectProvider()
+ {
+ public <T> T provide(Class<T> objectType, AnnotationProvider
annotationProvider, ObjectLocator locator)
+ {
+ return objectType.cast(configuration.get(objectType));
+ }
+ };
+ }
+}
Added:
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ServiceOverride.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ServiceOverride.java?rev=731641&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ServiceOverride.java
(added)
+++
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/ServiceOverride.java
Mon Jan 5 10:19:30 2009
@@ -0,0 +1,27 @@
+// Copyright 2009 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.ioc.services;
+
+import org.apache.tapestry5.ioc.ObjectProvider;
+import org.apache.tapestry5.ioc.annotations.UsesMappedConfiguration;
+
+/**
+ * Used to override built in services.
+ */
+...@usesmappedconfiguration(key = Class.class, value = Object.class)
+public interface ServiceOverride
+{
+ ObjectProvider getServiceOverrideProvider();
+}
Modified:
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/TapestryIOCModule.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/TapestryIOCModule.java?rev=731641&r1=731640&r2=731641&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/TapestryIOCModule.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/TapestryIOCModule.java
Mon Jan 5 10:19:30 2009
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009 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.
@@ -15,6 +15,7 @@
package org.apache.tapestry5.ioc.services;
import org.apache.tapestry5.ioc.*;
+import org.apache.tapestry5.ioc.annotations.Local;
import org.apache.tapestry5.ioc.annotations.Marker;
import org.apache.tapestry5.ioc.annotations.PreventServiceDecoration;
import org.apache.tapestry5.ioc.internal.services.*;
@@ -56,6 +57,7 @@
binder.bind(ClassNameLocator.class, ClassNameLocatorImpl.class);
binder.bind(AspectDecorator.class, AspectDecoratorImpl.class);
binder.bind(ClasspathURLConverter.class,
ClasspathURLConverterImpl.class);
+ binder.bind(ServiceOverride.class, ServiceOverrideImpl.class);
}
/**
@@ -83,15 +85,33 @@
}
/**
- * <dl> <dt>Value</dt> <dd>Supports the {...@link
org.apache.tapestry5.ioc.annotations.Value} annotation</dd>
- * <dt>Symbol</dt> <dd>Supports the {...@link
org.apache.tapestry5.ioc.annotations.Symbol} annotations</dd>
- * <dt>Autobuild</dt> <dd>Supports the {...@link
org.apache.tapestry5.ioc.annotations.Autobuild} annotation</dd> </dl>
+ * <dl> <dt>AnnotationBasedContributions</dt> <dd>Empty placeholder used
to seperate annotation-based ObjectProvider
+ * contributions (which come before) from non-annotation based
(ServiceOverride here, Alias in tapestry-core) which
+ * come after. </dd> <dt>Value</dt> <dd>Supports the {...@link
org.apache.tapestry5.ioc.annotations.Value}
+ * annotation</dd> <dt>Symbol</dt> <dd>Supports the {...@link
org.apache.tapestry5.ioc.annotations.Symbol}
+ * annotations</dd> <dt>Autobuild</dt> <dd>Supports the {...@link
org.apache.tapestry5.ioc.annotations.Autobuild}
+ * annotation</dd> <dt>ServiceOverride</dt> <dd>Allows simple service
overrides via the {...@link
+ * org.apache.tapestry5.ioc.services.ServiceOverride} service (and its
configuration)</dl>
*/
- public static void
contributeMasterObjectProvider(OrderedConfiguration<ObjectProvider>
configuration)
+ public static void
contributeMasterObjectProvider(OrderedConfiguration<ObjectProvider>
configuration,
+ @Local final
ServiceOverride serviceOverride)
{
- configuration.addInstance("Value", ValueObjectProvider.class);
- configuration.addInstance("Symbol", SymbolObjectProvider.class);
- configuration.add("Autobuild", new AutobuildObjectProvider());
+ configuration.add("AnnotationBasedContributions", null);
+
+ configuration.addInstance("Value", ValueObjectProvider.class,
"before:AnnotationBasedContributions");
+ configuration.addInstance("Symbol", SymbolObjectProvider.class,
"before:AnnotationBasedContributions");
+ configuration.add("Autobuild", new AutobuildObjectProvider(),
"before:AnnotationBasedContributions");
+
+
+ ObjectProvider wrapper = new ObjectProvider()
+ {
+ public <T> T provide(Class<T> objectType, AnnotationProvider
annotationProvider, ObjectLocator locator)
+ {
+ return
serviceOverride.getServiceOverrideProvider().provide(objectType,
annotationProvider, locator);
+ }
+ };
+
+ configuration.add("ServiceOverride", wrapper,
"after:AnnotationBasedContributions");
}
/**
Added: tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/cookbook/override.apt
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/cookbook/override.apt?rev=731641&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/cookbook/override.apt
(added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/cookbook/override.apt
Mon Jan 5 10:19:30 2009
@@ -0,0 +1,96 @@
+ ----
+ Overriding Services
+ ----
+
+Overriding Services
+
+ Tapestry is designed to be easy to customize, and the IoC container is key
to that customizability.
+
+ Part of Tapestry's core functionality is resolving injected objects; that
is, when Tapestry is building an object
+ or service and sees a constructor parameter or a field, what value does it
plug in? Most of the time,
+ the injected object is a service defined elsewhere within the container
(and, in fact, that actual instance
+ will be a proxy to the service, which may not have been fully realized yet).
+
+ However, there are cases where you might want to override how Tapestry
operates in some specific way.
+
+ The strategy used to determine what object gets injected where is
+ {{{../injection.html}defined inside Tapestry IoC itself}}; thus we can take
advantage of
+ several features of the IoC container in order to take control over specific
injections.
+
+Overriding Services
+
+ In most cases, services are injected by matching just type; there no
@InjectService annotation,
+ just a method or constructor parameter whose type matches the service's
interface.
+
+ In this case, it is very easy to supply your own alternate implementation of
a service.
+
+ <<AppModule.java>> (partial)
+
+----
+ public static void
contributeServiceOverride(MappedConfiguration<Class,Object> configuration)
+ {
+ configuration.add(SomeServiceType.class, new SomeServiceType() { . . . });
+ }
+----
+
+ In this example, the service to be overriden is provided as an inner class
implementing the interface.
+
+ Sometimes you'll want to define the override as a service of its own: this
is useful if you want
+ to inject a Logger specific to the service, or if the overriding
implementation needs a configuration:
+
+
+ <<AppModule.java>> (partial)
+
+----
+ public static void bind(ServiceBinder binder)
+ {
+ binder.bind(SomeServiceType.class,
SomeServiceTypeOverrideImpl.class).withId("Override");
+ }
+
+ public static void
contributeServiceOverride(MappedConfiguration<Class,Object> configuration,
@Local SomeServiceType override)
+ {
+ configuration.add(SomeServiceType.class, override);
+ }
+----
+
+ Here we're defining a service local to this module using the bind() method.
+
+ Every service in the IoC container must have a unique id, that's why we used
the withId() method; if we we hadn't,
+ the default service id would have been "SomeServiceType" which is a likely
conflict with the very service we're trying to
+ override.
+
+ We can inject our overriding implementation of SomeServiceType using the
special
+ @{{{../../apidocs/org/apache/tapestry5/annotations/Local.html}Local}}
annotation, which indicates that a service
+ within the same module only should be injected: otherwise there would be a
problem because the override parameter
+ would need to be resolved using the MasterObjectProvider and, ultimately,
the ServiceOverride service; this would cause
+ Tapestry to throw an exception indicating that ServiceOverride depends on
itself. We defuse that situation by using
+ @Local, which prevents the MasterObjectProvider service from being used to
resolve the override parameter.
+
+Decorating Services
+
+ Another option is to {{{../deocrator.html}decorate}} the existing service.
Perhaps you want to extend some of the behavior
+ of the service but keep the rest.
+
+ Alternately, this approach is useful to override a service that is matched
using marker annotations.
+
+ <<AppModule.java>> (partial)
+
+----
+ public SomeServiceType decorateSomeServiceType(SomeServiceType original)
+ {
+ return new SomeServiceType() { . . . };
+ }
+----
+
+ This decorate method is invoked because its name matches the service id of
the original service, "SomeServiceType"
+ (you have to adjust the name to match the service id).
+
+ It is passed the original service and its job it to return an <interceptor>,
and object that implements the same
+ interface, wrapping around the original service.
+
+ Note that the object passed in as original may be the core service
implementation, or it may be some other
+ interceptor from some other decorator for the same service (often, such a
parameter is named "delegate" to highlight
+ this ambiguity).
+
+
+
Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/injection.apt
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/injection.apt?rev=731641&r1=731640&r2=731641&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/injection.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/injection.apt Mon Jan 5
10:19:30 2009
@@ -179,6 +179,13 @@
if so, autobuilds the value for the parameter. Of course, the object being
built will itself
be configured via injection.
+** ServiceOverride ObjectProvider
+
+ Checks any contributions to the
+
{{{../apidocs/org/apache/tapestry5/ioc/services/ServiceOverride.html}ServiceOverride}}
service. Contributions
+ map a type to an object of that type. Thus, ServiceOverrides will override
injections of services
+ that are not qualified with a marker annotation.
+
** Alias ObjectProvider (tapestry-core)
Uses the
@@ -186,7 +193,7 @@
that can be injected.
This is commonly used to override a built-in service by contributing an
object with the
- exact same interface.
+ exact same interface. This is an older and more complex version of
the ServiceOverride provider.
** Asset ObjectProvider (tapestry-core)
Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/site/site.xml
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/site/site.xml?rev=731641&r1=731640&r2=731641&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/site/site.xml (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/site/site.xml Mon Jan 5 10:19:30
2009
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
- Copyright 2006 The Apache Software Foundation
+ Copyright 2006, 2008, 2009 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.
@@ -78,6 +78,7 @@
<item name="Basics" href="cookbook/basics.html"/>
<item name="Service Configurations" href="cookbook/servconf.html"/>
<item name="Using Patterns" href="cookbook/patterns.html"/>
+ <item name="Overriding Services" href="cookbook/override.html"/>
</menu>
Added:
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/GreeterServiceOverrideModule.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/GreeterServiceOverrideModule.java?rev=731641&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/GreeterServiceOverrideModule.java
(added)
+++
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/GreeterServiceOverrideModule.java
Mon Jan 5 10:19:30 2009
@@ -0,0 +1,40 @@
+// Copyright 2009 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.ioc;
+
+public class GreeterServiceOverrideModule
+{
+ public Greeter buildDefaultGreeter()
+ {
+ return new Greeter()
+ {
+ public String getGreeting()
+ {
+ return "Hello";
+ }
+ };
+ }
+
+ public static void contributeServiceOverride(MappedConfiguration<Class,
Object> configuration)
+ {
+ configuration.add(Greeter.class, new Greeter()
+ {
+ public String getGreeting()
+ {
+ return "Override Greeting";
+ }
+ });
+ }
+}
Modified:
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/IntegrationTest.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/IntegrationTest.java?rev=731641&r1=731640&r2=731641&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/IntegrationTest.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/IntegrationTest.java
Mon Jan 5 10:19:30 2009
@@ -1280,4 +1280,17 @@
"Contribution key fred has already been
overridden");
}
}
+
+ /**
+ * TAP5-316
+ */
+ @Test
+ public void service_override()
+ {
+ Registry r = buildRegistry(GreeterServiceOverrideModule.class);
+
+ Greeter g = r.getObject(Greeter.class, null);
+
+ assertEquals(g.getGreeting(), "Override Greeting");
+ }
}