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/29 03:57:36 UTC

svn commit: r426741 - in /tapestry/tapestry5/tapestry-core/trunk/src: main/java/org/apache/tapestry/internal/ioc/ main/java/org/apache/tapestry/internal/ioc/services/ main/java/org/apache/tapestry/ioc/ main/java/org/apache/tapestry/ioc/services/ test/j...

Author: hlship
Date: Fri Jul 28 18:57:35 2006
New Revision: 426741

URL: http://svn.apache.org/viewvc?rev=426741&view=rev
Log:
Add basic logging inside ClassFactory and ClassFab.
Add ability to make ClassFactory write out bytecode for generated classes.
Add support for "perthread" service lifecycle.

Added:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PerThreadServiceCreator.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ServiceLogger.java
      - copied, changed from r426658, tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/Logger.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ClassFactoryImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ServiceLoggerTest.java
      - copied, changed from r426658, tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/LoggerTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/PerThreadModule.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/StringHolder.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/StringHolderImpl.java
Removed:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/Logger.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/LoggerTest.java
Modified:
    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/RegistryImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/AbstractFab.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ClassFabImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ClassFactoryImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/CtClassSource.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/LoggingDecoratorImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PerThreadServiceLifecycle.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ThreadCleanupHubImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/Registry.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/ServiceLifecycle.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassFab.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassFactory.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ServiceLifecycleSource.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ThreadCleanupHub.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ThreadCleanupListener.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/ModuleImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ClassFabImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ThreadCleanupHubImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/UnknownLifecycleModule.java

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=426741&r1=426740&r2=426741&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 28 18:57:35 2006
@@ -307,16 +307,14 @@
 
     private Class createProxyClass(String serviceId, Class serviceInterface, String proxyDescription)
     {
-        String name = ClassFabUtils.generateClassName(serviceInterface);
-
-        // This is why ClassFactory has to be primitive.
+          // This is why ClassFactory has to be primitive.
 
         ClassFactory factory = _registry.getService(
                 IOCConstants.CLASS_FACTORY_SERVICE_ID,
                 ClassFactory.class,
                 this);
 
-        ClassFab cf = factory.newClass(name, Object.class);
+        ClassFab cf = factory.newClass(serviceInterface);
 
         cf.addField("_creator", ServiceCreator.class);
         cf.addField("_delegate", serviceInterface);

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/RegistryImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/RegistryImpl.java?rev=426741&r1=426740&r2=426741&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/RegistryImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/RegistryImpl.java Fri Jul 28 18:57:35 2006
@@ -39,6 +39,7 @@
 import org.apache.tapestry.ioc.def.ModuleDef;
 import org.apache.tapestry.ioc.def.ServiceDef;
 import org.apache.tapestry.ioc.services.ServiceLifecycleSource;
+import org.apache.tapestry.ioc.services.ThreadCleanupHub;
 import org.apache.tapestry.util.CollectionFactory;
 
 import static org.apache.tapestry.util.CollectionFactory.newList;
@@ -112,6 +113,11 @@
     public <T> T getService(String serviceId, Class<T> serviceInterface)
     {
         return getService(serviceId, serviceInterface, null);
+    }
+
+    public void cleanupThread()
+    {
+        getService("tapestry.ioc.ThreadCleanupHub", ThreadCleanupHub.class).cleanup();
     }
 
     private Module locateModuleForService(String serviceId)

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/AbstractFab.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/AbstractFab.java?rev=426741&r1=426740&r2=426741&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/AbstractFab.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/AbstractFab.java Fri Jul 28 18:57:35 2006
@@ -18,6 +18,7 @@
 
 import java.util.Map;
 
+import org.apache.commons.logging.Log;
 import org.apache.tapestry.internal.annotations.OneShot;
 import org.apache.tapestry.internal.annotations.SuppressNullCheck;
 
@@ -37,10 +38,13 @@
 
     private final CtClassSource _source;
 
-    public AbstractFab(CtClassSource source, CtClass ctClass)
+    private final Log _log;
+
+    public AbstractFab(CtClassSource source, CtClass ctClass, Log log)
     {
         _ctClass = ctClass;
         _source = source;
+        _log = log;
     }
 
     /**
@@ -52,6 +56,17 @@
     {
         CtClass ctInterfaceClass = _source.getCtClass(interfaceClass);
 
+        try
+        {
+            for (CtClass existing : _ctClass.getInterfaces())
+                if (existing == ctInterfaceClass)
+                    return;
+        }
+        catch (Exception ex)
+        {
+            // Don't think this code is actually reachable.
+        }
+
         _ctClass.addInterface(ctInterfaceClass);
     }
 
@@ -90,6 +105,9 @@
     @OneShot.Lockdown
     public Class createClass()
     {
+        if (_log.isDebugEnabled())
+            _log.debug(String.format("Creating class from %s", this));
+
         return _source.createClass(_ctClass);
     }
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ClassFabImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ClassFabImpl.java?rev=426741&r1=426740&r2=426741&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ClassFabImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ClassFabImpl.java Fri Jul 28 18:57:35 2006
@@ -25,6 +25,7 @@
 import javassist.CtMethod;
 import javassist.NotFoundException;
 
+import org.apache.commons.logging.Log;
 import org.apache.tapestry.internal.annotations.SuppressNullCheck;
 import org.apache.tapestry.internal.util.InternalUtils;
 import org.apache.tapestry.ioc.services.ClassFab;
@@ -54,9 +55,10 @@
 
     private final Set<MethodSignature> _addedSignatures = newSet();
 
-    public ClassFabImpl(CtClassSource source, CtClass ctClass)
+   
+    public ClassFabImpl(CtClassSource source, CtClass ctClass, Log log)
     {
-        super(source, ctClass);
+        super(source, ctClass, log);
     }
 
     /**
@@ -143,11 +145,11 @@
             throw new RuntimeException(ServiceMessages.unableToAddField(name, getCtClass(), ex), ex);
         }
 
-        _description
-                .append(_formatter.format("private %s %s;\n\n", ClassFabUtils.getJavaClassName(type), name));
+        _formatter.format("private %s %s;\n\n", ClassFabUtils.getJavaClassName(type), name);
     }
 
-    public void proxyMethodsToDelegate(Class serviceInterface, String delegateExpression, String toString)
+    public void proxyMethodsToDelegate(Class serviceInterface, String delegateExpression,
+            String toString)
     {
         addInterface(serviceInterface);
 
@@ -207,8 +209,8 @@
 
         // modifiers, return type, name
 
-        _description.append(_formatter.format("%s %s %s", Modifier.toString(modifiers), ClassFabUtils
-                .getJavaClassName(ms.getReturnType()), ms.getName()));
+        _formatter.format("%s %s %s", Modifier.toString(modifiers), ClassFabUtils
+                .getJavaClassName(ms.getReturnType()), ms.getName());
 
         // parameters, exceptions and body from this:
         addMethodDetailsToDescription(ms.getParameterTypes(), ms.getExceptionTypes(), body);

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ClassFactoryImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ClassFactoryImpl.java?rev=426741&r1=426740&r2=426741&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ClassFactoryImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ClassFactoryImpl.java Fri Jul 28 18:57:35 2006
@@ -16,6 +16,9 @@
 
 import javassist.CtClass;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hivemind.service.ClassFabUtils;
 import org.apache.tapestry.ioc.services.ClassFab;
 import org.apache.tapestry.ioc.services.ClassFactory;
 
@@ -26,6 +29,8 @@
  */
 public class ClassFactoryImpl implements ClassFactory
 {
+    private final Log _log;
+
     /**
      * ClassPool shared by all modules (all CtClassSource instances).
      */
@@ -33,13 +38,38 @@
 
     private CtClassSource _classSource = new CtClassSource(_pool);
 
+    public ClassFactoryImpl()
+    {
+        this(LogFactory.getLog(ClassFactoryImpl.class));
+    }
+
+    public ClassFactoryImpl(Log log)
+    {
+        _log = log;
+    }
+
+    public ClassFab newClass(Class serviceInterface)
+    {
+        String name = ClassFabUtils.generateClassName(serviceInterface);
+
+        ClassFab cf = newClass(name, Object.class);
+
+        cf.addInterface(serviceInterface);
+
+        return cf;
+    }
+
     public ClassFab newClass(String name, Class superClass)
     {
+        if (_log.isDebugEnabled())
+            _log.debug(String.format("Create ClassFab for %s (extends %s)", name, superClass
+                    .getName()));
+
         try
         {
             CtClass ctNewClass = _classSource.newClass(name, superClass);
 
-            return new ClassFabImpl(_classSource, ctNewClass);
+            return new ClassFabImpl(_classSource, ctNewClass, _log);
         }
         catch (Exception ex)
         {

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/CtClassSource.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/CtClassSource.java?rev=426741&r1=426740&r2=426741&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/CtClassSource.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/CtClassSource.java Fri Jul 28 18:57:35 2006
@@ -74,8 +74,13 @@
         return _pool.makeClass(name, ctSuperClass);
     }
 
+    private static final String WRITE_DIR = System.getProperty("javassist-write-dir");
+
     public synchronized Class createClass(CtClass ctClass)
     {
+        if (WRITE_DIR != null)
+            writeClass(ctClass);
+
         try
         {
             Class result = _pool.toClass(ctClass);
@@ -87,6 +92,24 @@
         catch (Throwable ex)
         {
             throw new RuntimeException(ServiceMessages.unableToWriteClass(ctClass, ex), ex);
+        }
+    }
+
+    private void writeClass(CtClass ctClass)
+    {
+        try
+        {
+            boolean pruning = ctClass.stopPruning(true);
+
+            ctClass.writeFile(WRITE_DIR);
+
+            ctClass.defrost();
+
+            ctClass.stopPruning(pruning);
+        }
+        catch (Exception ex)
+        {
+            ex.printStackTrace(System.err);
         }
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/LoggingDecoratorImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/LoggingDecoratorImpl.java?rev=426741&r1=426740&r2=426741&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/LoggingDecoratorImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/LoggingDecoratorImpl.java Fri Jul 28 18:57:35 2006
@@ -47,7 +47,7 @@
     {
         Class interceptorClass = createInterceptorClass(serviceInterface, serviceId);
 
-        Logger logger = new Logger(serviceLog);
+        ServiceLogger logger = new ServiceLogger(serviceLog);
 
         Constructor cc = interceptorClass.getConstructors()[0];
 
@@ -75,10 +75,13 @@
 
     private Class createInterceptorClass(Class serviceInterface, String serviceId)
     {
-        String className = ClassFabUtils.generateClassName(serviceInterface);
-        ClassFab cf = _classFactory.newClass(className, Object.class);
+        ClassFab cf = _classFactory.newClass(serviceInterface);
 
-        addInfrastructure(cf, serviceInterface);
+        cf.addField("_delegate", serviceInterface);
+        cf.addField("_logger", ServiceLogger.class);
+
+        cf.addConstructor(new Class[]
+        { serviceInterface, ServiceLogger.class }, null, "{ _delegate = $1; _logger = $2; }");
 
         addMethods(cf, serviceInterface, serviceId);
 
@@ -90,9 +93,7 @@
         MethodIterator mi = new MethodIterator(serviceInterface);
 
         while (mi.hasNext())
-        {
             addMethod(cf, mi.next());
-        }
 
         if (!mi.getToString())
             cf.addToString(ServiceMessages.loggingInterceptor(serviceId, serviceInterface));
@@ -160,16 +161,5 @@
         builder.addln("  _logger.fail(%s, ex);", quotedMethodName);
         builder.addln("throw ex;");
         builder.end();
-    }
-
-    private void addInfrastructure(ClassFab cf, Class serviceInterface)
-    {
-        cf.addInterface(serviceInterface);
-
-        cf.addField("_delegate", serviceInterface);
-        cf.addField("_logger", Logger.class);
-
-        cf.addConstructor(new Class[]
-        { serviceInterface, Logger.class }, null, "{ _delegate = $1; _logger = $2; }");
     }
 }

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PerThreadServiceCreator.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PerThreadServiceCreator.java?rev=426741&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PerThreadServiceCreator.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PerThreadServiceCreator.java Fri Jul 28 18:57:35 2006
@@ -0,0 +1,64 @@
+// 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.internal.ioc.services;
+
+import org.apache.tapestry.ioc.ServiceCreator;
+import org.apache.tapestry.ioc.services.ThreadCleanupHub;
+import org.apache.tapestry.ioc.services.ThreadCleanupListener;
+
+/**
+ * Provides per-thread implementations of services, along with end-of-request thread cleanup.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+public class PerThreadServiceCreator extends ThreadLocal implements ThreadCleanupListener,
+        ServiceCreator
+{
+    private final ThreadCleanupHub _threadCleanupHub;
+
+    private final ServiceCreator _delegate;
+
+    public PerThreadServiceCreator(ThreadCleanupHub threadCleanupHub, ServiceCreator delegate)
+    {
+        _threadCleanupHub = threadCleanupHub;
+        _delegate = delegate;
+    }
+
+    @Override
+    protected Object initialValue()
+    {
+        // First time the value is accessed per thread, set up a callback to clear out the
+        // value (at the end of the request) and use the creator to create a new instance.
+
+        _threadCleanupHub.addThreadCleanupListener(this);
+
+        return _delegate.createService();
+    }
+
+    public Object createService()
+    {
+        // Get (or create) the service.
+        return get();
+    }
+
+    public void threadDidCleanup()
+    {
+        remove();
+    }
+
+}
\ No newline at end of file

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PerThreadServiceLifecycle.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PerThreadServiceLifecycle.java?rev=426741&r1=426740&r2=426741&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PerThreadServiceLifecycle.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/PerThreadServiceLifecycle.java Fri Jul 28 18:57:35 2006
@@ -1,11 +1,33 @@
+// 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.internal.ioc.services;
 
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
+
 import org.apache.tapestry.ioc.ServiceCreator;
 import org.apache.tapestry.ioc.ServiceLifecycle;
 import org.apache.tapestry.ioc.ServiceResources;
+import org.apache.tapestry.ioc.services.ClassFab;
 import org.apache.tapestry.ioc.services.ClassFactory;
+import org.apache.tapestry.ioc.services.MethodSignature;
 import org.apache.tapestry.ioc.services.ThreadCleanupHub;
 
+import static java.lang.String.format;
+
 /**
  * Allows a service to exist "per thread" (in each thread). This involves an inner proxy, with a
  * ThreadLocal whose initial value is derived from a {@link org.apache.tapestry.ioc.ServiceCreator}.
@@ -13,8 +35,9 @@
  * {@link org.apache.tapestry.ioc.services.ThreadCleanupListener} so that it can discard the
  * per-thread implementation.
  * <p>
- * This scheme ensures that, although the service builder method will be invoked many times of the
- * life of the application, the service decoration process occurs only once.
+ * This scheme ensures that, although the service builder method will be invoked many times over the
+ * life of the application, the service decoration process occurs only once. The final calling chain
+ * is: Service Proxy --&gt; Decorator(s) --&gt; PerThread Proxy --&gt; (per thread) instance.
  * 
  * @author Howard M. Lewis Ship
  */
@@ -24,9 +47,65 @@
 
     private final ClassFactory _classFactory;
 
-    public Object createService(ServiceResources resources, ServiceCreator creator)
+    private static final String PER_THREAD_METHOD_NAME = "_perThreadInstance";
+
+    public PerThreadServiceLifecycle(ThreadCleanupHub threadCleanupHub, ClassFactory classFactory)
+    {
+        _threadCleanupHub = threadCleanupHub;
+        _classFactory = classFactory;
+    }
+
+    public Object createService(ServiceResources resources, final ServiceCreator creator)
+    {
+        Class proxyClass = createProxyClass(resources);
+
+        ServiceCreator perThreadCreator = new PerThreadServiceCreator(_threadCleanupHub, creator);
+
+        try
+        {
+            Constructor ctor = proxyClass.getConstructors()[0];
+
+            return ctor.newInstance(perThreadCreator);
+        }
+        catch (InvocationTargetException ex)
+        {
+            throw new RuntimeException(ex.getCause());
+        }
+        catch (Exception ex)
+        {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    private Class createProxyClass(ServiceResources resources)
     {
-        return null;
+        Class serviceInterface = resources.getServiceInterface();
+
+        ClassFab cf = _classFactory.newClass(serviceInterface);
+
+        cf.addField("_creator", ServiceCreator.class);
+
+        // Constructor takes a ServiceCreator
+
+        // Caution: Javassist needs this to be a block, not just a single statement!
+        cf.addConstructor(new Class[]
+        { ServiceCreator.class }, null, "{ _creator = $1; }");
+
+        String body = format("return (%s) _creator.createService();", serviceInterface.getName());
+
+        MethodSignature sig = new MethodSignature(serviceInterface, PER_THREAD_METHOD_NAME, null,
+                null);
+
+        cf.addMethod(Modifier.PRIVATE, sig, body);
+
+        String toString = format(
+                "<PerThread Proxy for %s(%s)>",
+                resources.getServiceId(),
+                serviceInterface.getName());
+
+        cf.proxyMethodsToDelegate(serviceInterface, PER_THREAD_METHOD_NAME + "()", toString);
+
+        return cf.createClass();
     }
 
     public boolean getCreateProxy()

Copied: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ServiceLogger.java (from r426658, tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/Logger.java)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ServiceLogger.java?p2=tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ServiceLogger.java&p1=tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/Logger.java&r1=426658&r2=426741&rev=426741&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/Logger.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ServiceLogger.java Fri Jul 28 18:57:35 2006
@@ -26,7 +26,7 @@
  * 
  * @author Howard M. Lewis Ship
  */
-public final class Logger
+public final class ServiceLogger
 {
     private final Log _log;
 
@@ -36,7 +36,7 @@
 
     private static final String FAIL = " FAIL";
 
-    public Logger(Log log)
+    public ServiceLogger(Log log)
     {
         _log = log;
     }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ThreadCleanupHubImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ThreadCleanupHubImpl.java?rev=426741&r1=426740&r2=426741&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ThreadCleanupHubImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/services/ThreadCleanupHubImpl.java Fri Jul 28 18:57:35 2006
@@ -1,3 +1,17 @@
+// 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.internal.ioc.services;
 
 import java.util.List;

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/Registry.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/Registry.java?rev=426741&r1=426740&r2=426741&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/Registry.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/Registry.java Fri Jul 28 18:57:35 2006
@@ -21,4 +21,11 @@
  */
 public interface Registry extends ServiceLocator
 {
+    /**
+     * Invoked at the end of a request to discard any thread-specific information accumulated during
+     * the current request.
+     * 
+     * @see org.apache.tapestry.ioc.services.ThreadCleanupHub
+     */
+    void cleanupThread();
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/ServiceLifecycle.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/ServiceLifecycle.java?rev=426741&r1=426740&r2=426741&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/ServiceLifecycle.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/ServiceLifecycle.java Fri Jul 28 18:57:35 2006
@@ -24,9 +24,6 @@
     /**
      * Returns the same creator, or a new one, that encapsulates the creation of the core service
      * implementation.
-     * <p>
-     * TODO: Still deciding if ServiceResources needs to be passed in, or just the raw
-     * ServiceCreator
      * 
      * @param resources
      *            source of information about the service to be created, and source of additional

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassFab.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassFab.java?rev=426741&r1=426740&r2=426741&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassFab.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassFab.java Fri Jul 28 18:57:35 2006
@@ -48,7 +48,9 @@
 public interface ClassFab
 {
     /**
-     * Adds the specified interface as an interface implemented by this class.
+     * Adds the specified interface as an interface implemented by this class. It is not an error to
+     * invoke this method multiple times with the same interface class (and the interface is only
+     * added once).
      */
     void addInterface(Class interfaceClass);
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassFactory.java?rev=426741&r1=426740&r2=426741&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassFactory.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassFactory.java Fri Jul 28 18:57:35 2006
@@ -22,6 +22,16 @@
 public interface ClassFactory
 {
     /**
+     * Simplified version of {@link #newClass(String, Class)} that generates a name based on the
+     * service interface name, extends from java.lang.Object, and automatically adds the
+     * serviceInterface to the returned ClassFab. This is the most common use when creating the
+     * kinds of proxies used throughout Tapestry IoC.
+     * 
+     * @param serviceInterface
+     */
+    ClassFab newClass(Class serviceInterface);
+
+    /**
      * Creates a {@link ClassFab} object for the given name; the new class is a subclass of the
      * indicated class. The new class is always public and concrete.
      * 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ServiceLifecycleSource.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ServiceLifecycleSource.java?rev=426741&r1=426740&r2=426741&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ServiceLifecycleSource.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ServiceLifecycleSource.java Fri Jul 28 18:57:35 2006
@@ -1,3 +1,17 @@
+// 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.services;
 
 import org.apache.tapestry.ioc.ServiceLifecycle;

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java?rev=426741&r1=426740&r2=426741&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java Fri Jul 28 18:57:35 2006
@@ -19,7 +19,9 @@
 import org.apache.commons.logging.Log;
 import org.apache.tapestry.internal.ioc.services.ClassFactoryImpl;
 import org.apache.tapestry.internal.ioc.services.LoggingDecoratorImpl;
+import org.apache.tapestry.internal.ioc.services.PerThreadServiceLifecycle;
 import org.apache.tapestry.internal.ioc.services.ThreadCleanupHubImpl;
+import org.apache.tapestry.ioc.MappedConfiguration;
 import org.apache.tapestry.ioc.ServiceLifecycle;
 import org.apache.tapestry.ioc.annotations.Id;
 import org.apache.tapestry.ioc.annotations.InjectService;
@@ -38,9 +40,9 @@
      * The ClassFactory service is used to create new classes at runtime.
      */
     @Lifecycle("primitive")
-    public ClassFactory buildClassFactory()
+    public ClassFactory buildClassFactory(Log log)
     {
-        return new ClassFactoryImpl();
+        return new ClassFactoryImpl(log);
     }
 
     /**
@@ -76,7 +78,17 @@
             {
                 return configuration.get(lifecycleName);
             }
-
         };
+    }
+
+    public void contributeServiceLifecycleSource(
+            MappedConfiguration<String, ServiceLifecycle> configuration,
+            @InjectService("ThreadCleanupHub")
+            ThreadCleanupHub threadCleanupHub, @InjectService("ClassFactory")
+            ClassFactory classFactory)
+    {
+        configuration.add(
+                "perthread",
+                new PerThreadServiceLifecycle(threadCleanupHub, classFactory));
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ThreadCleanupHub.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ThreadCleanupHub.java?rev=426741&r1=426740&r2=426741&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ThreadCleanupHub.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ThreadCleanupHub.java Fri Jul 28 18:57:35 2006
@@ -1,3 +1,17 @@
+// 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.services;
 
 /**

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ThreadCleanupListener.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ThreadCleanupListener.java?rev=426741&r1=426740&r2=426741&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ThreadCleanupListener.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ThreadCleanupListener.java Fri Jul 28 18:57:35 2006
@@ -1,3 +1,17 @@
+// 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.services;
 
 import java.util.EventListener;

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/ModuleImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/ModuleImplTest.java?rev=426741&r1=426740&r2=426741&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/ModuleImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/ModuleImplTest.java Fri Jul 28 18:57:35 2006
@@ -274,12 +274,14 @@
         catch (RuntimeException ex)
         {
             // The exception is now wrapped in a more generic exception.
-            assertEquals(
-                    ex.getCause().getMessage(),
-                    "Construction of service 'ioc.test.PrimitiveRecursiveFoe' has failed due to recursion: "
-                            + "the service depends on itself in some way. Please check "
-                            + ModuleImplTestModule.class.getName()
-                            + ".buildPrimitiveRecursiveFoe(FoeService) for references to another service that is itself dependent on service 'ioc.test.PrimitiveRecursiveFoe'.");
+            assertTrue(ex
+                    .getMessage()
+                    .contains(
+                            "Construction of service 'ioc.test.PrimitiveRecursiveFoe' has failed due to recursion: "
+                                    + "the service depends on itself in some way. Please check "
+                                    + ModuleImplTestModule.class.getName()
+                                    + ".buildPrimitiveRecursiveFoe(FoeService) for references to another service "
+                                    + "that is itself dependent on service 'ioc.test.PrimitiveRecursiveFoe'."));
         }
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ClassFabImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ClassFabImplTest.java?rev=426741&r1=426740&r2=426741&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ClassFabImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ClassFabImplTest.java Fri Jul 28 18:57:35 2006
@@ -23,6 +23,7 @@
 
 import javassist.CtClass;
 
+import org.apache.commons.logging.LogFactory;
 import org.apache.hivemind.impl.BaseLocatable;
 import org.apache.hivemind.util.PropertyUtils;
 import org.apache.tapestry.internal.ioc.services.LoggingDecoratorImplTest.ToStringService;
@@ -70,7 +71,7 @@
     {
         CtClass ctClass = _source.newClass(className, superClass);
 
-        return new ClassFabImpl(_source, ctClass);
+        return new ClassFabImpl(_source, ctClass, LogFactory.getLog("ClassFab"));
     }
 
     @Test

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ClassFactoryImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ClassFactoryImplTest.java?rev=426741&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ClassFactoryImplTest.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ClassFactoryImplTest.java Fri Jul 28 18:57:35 2006
@@ -0,0 +1,92 @@
+// 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.internal.ioc.services;
+
+import java.lang.reflect.Modifier;
+
+import org.apache.tapestry.ioc.services.ClassFab;
+import org.apache.tapestry.ioc.services.ClassFabUtils;
+import org.apache.tapestry.ioc.services.ClassFactory;
+import org.apache.tapestry.ioc.services.MethodSignature;
+import org.apache.tapestry.test.BaseTestCase;
+import org.testng.annotations.Test;
+
+/**
+ * @author Howard M. Lewis Ship
+ */
+public class ClassFactoryImplTest extends BaseTestCase
+{
+    public static class BaseClass
+    {
+        public void run()
+        {
+        }
+    }
+
+    @Test
+    public void new_class_with_name_and_base_class() throws Exception
+    {
+        ClassFactory factory = new ClassFactoryImpl();
+        String name = ClassFabUtils.generateClassName(Runnable.class);
+
+        ClassFab cf = factory.newClass(name, Object.class);
+        cf.addInterface(Runnable.class);
+
+        addRunMethod(cf);
+
+        Class newClass = cf.createClass();
+
+        Runnable instance = (Runnable) newClass.newInstance();
+
+        instance.run();
+    }
+
+    @Test
+    public void new_class_with_non_object_base_class() throws Exception
+    {
+        ClassFactory factory = new ClassFactoryImpl();
+        String name = ClassFabUtils.generateClassName(Runnable.class);
+
+        ClassFab cf = factory.newClass(name, BaseClass.class);
+        cf.addInterface(Runnable.class);
+
+        Class newClass = cf.createClass();
+
+        Runnable instance = (Runnable) newClass.newInstance();
+
+        instance.run();
+    }
+
+    @Test
+    public void new_class_with_interface() throws Exception
+    {
+        ClassFactory factory = new ClassFactoryImpl();
+
+        ClassFab cf = factory.newClass(Runnable.class);
+
+        addRunMethod(cf);
+
+        Class newClass = cf.createClass();
+
+        Runnable instance = (Runnable) newClass.newInstance();
+
+        instance.run();
+    }
+
+    private void addRunMethod(ClassFab cf)
+    {
+        cf.addMethod(Modifier.PUBLIC, new MethodSignature(void.class, "run", null, null), " { } ");
+    }
+}

Copied: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ServiceLoggerTest.java (from r426658, tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/LoggerTest.java)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ServiceLoggerTest.java?p2=tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ServiceLoggerTest.java&p1=tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/LoggerTest.java&r1=426658&r2=426741&rev=426741&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/LoggerTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ServiceLoggerTest.java Fri Jul 28 18:57:35 2006
@@ -26,7 +26,7 @@
 /**
  * @author Howard M. Lewis Ship
  */
-public class LoggerTest extends BaseTestCase
+public class ServiceLoggerTest extends BaseTestCase
 {
     private void try_entry(String methodName, String expected, Object... arguments)
     {
@@ -36,7 +36,7 @@
 
         replay();
 
-        new Logger(log).entry(methodName, arguments);
+        new ServiceLogger(log).entry(methodName, arguments);
 
         verify();
 
@@ -50,7 +50,7 @@
 
         replay();
 
-        new Logger(log).exit(methodName, result);
+        new ServiceLogger(log).exit(methodName, result);
 
         verify();
     }
@@ -84,7 +84,7 @@
 
         replay();
 
-        new Logger(log).voidExit("wilma");
+        new ServiceLogger(log).voidExit("wilma");
 
         verify();
     }
@@ -100,7 +100,7 @@
 
         replay();
 
-        new Logger(log).fail("wilma", t);
+        new ServiceLogger(log).fail("wilma", t);
 
         verify();
     }
@@ -115,7 +115,7 @@
 
         replay();
 
-        Logger logger = new Logger(log);
+        ServiceLogger logger = new ServiceLogger(log);
 
         assertTrue(logger.isDebugEnabled());
         assertFalse(logger.isDebugEnabled());

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ThreadCleanupHubImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ThreadCleanupHubImplTest.java?rev=426741&r1=426740&r2=426741&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ThreadCleanupHubImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ThreadCleanupHubImplTest.java Fri Jul 28 18:57:35 2006
@@ -1,3 +1,17 @@
+// 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.internal.ioc.services;
 
 import org.apache.commons.logging.Log;

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java?rev=426741&r1=426740&r2=426741&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java Fri Jul 28 18:57:35 2006
@@ -22,9 +22,11 @@
 
 import org.apache.tapestry.internal.test.InternalBaseTestCase;
 import org.apache.tapestry.ioc.services.TapestryIOCModule;
+import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
 
 /**
  * A few tests that are easiest (or even just possible) by building a Registry and trying out a few
@@ -186,5 +188,51 @@
                             + "(at org.apache.tapestry.ioc.UnknownLifecycleModule.buildUnknownLifecycle()): "
                             + "Unknown service lifecycle 'magic'.");
         }
+    }
+
+    @Test
+    public void simple_perthread() throws Exception
+    {
+        Registry r = buildRegistry(TapestryIOCModule.class, PerThreadModule.class);
+
+        final StringHolder holder = r.getService(StringHolder.class);
+
+        holder.setValue("fred");
+        assertEquals(holder.getValue(), "fred");
+
+        Runnable runnable = new Runnable()
+        {
+            public void run()
+            {
+                Assert.assertNull(holder.getValue());
+
+                holder.setValue("barney");
+                assertEquals(holder.getValue(), "barney");
+            }
+        };
+
+        Thread t = new Thread(runnable);
+
+        t.start();
+        t.join();
+
+        assertEquals(holder.getValue(), "fred");
+    }
+
+    @Test
+    public void registry_thread_cleanup()
+    {
+        Registry r = buildRegistry(TapestryIOCModule.class, PerThreadModule.class);
+
+        StringHolder holder = r.getService(StringHolder.class);
+
+        assertNull(holder.getValue());
+
+        holder.setValue("fred");
+        assertEquals(holder.getValue(), "fred");
+
+        r.cleanupThread();
+
+        assertNull(holder.getValue());
     }
 }

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/PerThreadModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/PerThreadModule.java?rev=426741&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/PerThreadModule.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/PerThreadModule.java Fri Jul 28 18:57:35 2006
@@ -0,0 +1,28 @@
+// 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;
+
+import org.apache.tapestry.ioc.annotations.Id;
+import org.apache.tapestry.ioc.annotations.Lifecycle;
+
+@Id("ioc.test")
+public class PerThreadModule
+{
+    @Lifecycle("perthread")
+    public StringHolder buildStringHolder()
+    {
+        return new StringHolderImpl();
+    }
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/StringHolder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/StringHolder.java?rev=426741&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/StringHolder.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/StringHolder.java Fri Jul 28 18:57:35 2006
@@ -0,0 +1,25 @@
+// 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;
+
+/**
+ * @author Howard M. Lewis Ship
+ */
+public interface StringHolder
+{
+    void setValue(String value);
+
+    String getValue();
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/StringHolderImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/StringHolderImpl.java?rev=426741&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/StringHolderImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/StringHolderImpl.java Fri Jul 28 18:57:35 2006
@@ -0,0 +1,33 @@
+// 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;
+
+/**
+ * @author Howard M. Lewis Ship
+ */
+public class StringHolderImpl implements StringHolder
+{
+    private String _value;
+
+    public String getValue()
+    {
+        return _value;
+    }
+
+    public void setValue(String value)
+    {
+        _value = value;
+    }
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/UnknownLifecycleModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/UnknownLifecycleModule.java?rev=426741&r1=426740&r2=426741&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/UnknownLifecycleModule.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/UnknownLifecycleModule.java Fri Jul 28 18:57:35 2006
@@ -1,3 +1,17 @@
+// 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;
 
 import org.apache.tapestry.ioc.annotations.Id;