001: // Copyright 2006, 2007 The Apache Software Foundation
002: //
003: // Licensed under the Apache License, Version 2.0 (the "License");
004: // you may not use this file except in compliance with the License.
005: // You may obtain a copy of the License at
006: //
007: // http://www.apache.org/licenses/LICENSE-2.0
008: //
009: // Unless required by applicable law or agreed to in writing, software
010: // distributed under the License is distributed on an "AS IS" BASIS,
011: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: // See the License for the specific language governing permissions and
013: // limitations under the License.
014:
015: package org.apache.tapestry.ioc.internal.services;
016:
017: import static java.lang.String.format;
018:
019: import java.lang.reflect.Constructor;
020: import java.lang.reflect.InvocationTargetException;
021: import java.lang.reflect.Modifier;
022:
023: import org.apache.tapestry.ioc.ObjectCreator;
024: import org.apache.tapestry.ioc.ServiceLifecycle;
025: import org.apache.tapestry.ioc.ServiceResources;
026: import org.apache.tapestry.ioc.annotations.InjectService;
027: import org.apache.tapestry.ioc.services.ClassFab;
028: import org.apache.tapestry.ioc.services.ClassFactory;
029: import org.apache.tapestry.ioc.services.MethodSignature;
030: import org.apache.tapestry.ioc.services.ThreadCleanupHub;
031:
032: /**
033: * Allows a service to exist "per thread" (in each thread). This involves an inner proxy, with a
034: * ThreadLocal whose initial value is derived from a {@link org.apache.tapestry.ioc.ObjectCreator}.
035: * Method invocations are delegated to the per-thread service instance. The proxy also implements
036: * {@link org.apache.tapestry.ioc.services.ThreadCleanupListener} so that it can discard the
037: * per-thread implementation.
038: * <p>
039: * This scheme ensures that, although the service builder method will be invoked many times over the
040: * life of the application, the service decoration process occurs only once. The final calling chain
041: * is: Service Proxy --> Decorator(s) --> PerThread Proxy --> (per thread) instance.
042: */
043: public class PerThreadServiceLifecycle implements ServiceLifecycle {
044: private final ThreadCleanupHub _threadCleanupHub;
045:
046: private final ClassFactory _classFactory;
047:
048: private static final String PER_THREAD_METHOD_NAME = "_perThreadInstance";
049:
050: public PerThreadServiceLifecycle(ThreadCleanupHub threadCleanupHub,
051: @InjectService("ClassFactory")
052: ClassFactory classFactory) {
053: _threadCleanupHub = threadCleanupHub;
054: _classFactory = classFactory;
055: }
056:
057: public Object createService(ServiceResources resources,
058: final ObjectCreator creator) {
059: Class proxyClass = createProxyClass(resources);
060:
061: ObjectCreator perThreadCreator = new PerThreadServiceCreator(
062: _threadCleanupHub, creator);
063:
064: try {
065: Constructor ctor = proxyClass.getConstructors()[0];
066:
067: return ctor.newInstance(perThreadCreator);
068: } catch (InvocationTargetException ex) {
069: throw new RuntimeException(ex.getCause());
070: } catch (Exception ex) {
071: throw new RuntimeException(ex);
072: }
073: }
074:
075: private Class createProxyClass(ServiceResources resources) {
076: Class serviceInterface = resources.getServiceInterface();
077:
078: ClassFab cf = _classFactory.newClass(serviceInterface);
079:
080: cf.addField("_creator", Modifier.PRIVATE | Modifier.FINAL,
081: ObjectCreator.class);
082:
083: // Constructor takes a ServiceCreator
084:
085: cf.addConstructor(new Class[] { ObjectCreator.class }, null,
086: "_creator = $1;");
087:
088: String body = format("return (%s) _creator.createObject();",
089: serviceInterface.getName());
090:
091: MethodSignature sig = new MethodSignature(serviceInterface,
092: PER_THREAD_METHOD_NAME, null, null);
093:
094: cf.addMethod(Modifier.PRIVATE, sig, body);
095:
096: String toString = format("<PerThread Proxy for %s(%s)>",
097: resources.getServiceId(), serviceInterface.getName());
098:
099: cf.proxyMethodsToDelegate(serviceInterface,
100: PER_THREAD_METHOD_NAME + "()", toString);
101:
102: return cf.createClass();
103: }
104: }
|