You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2010/03/12 06:21:17 UTC

svn commit: r922146 - in /tapestry/tapestry5/trunk/tapestry-ioc/src: main/java/org/apache/tapestry5/ioc/internal/ test/java/com/ test/java/com/example/ test/java/org/apache/tapestry5/ioc/ test/resources/

Author: hlship
Date: Fri Mar 12 05:21:16 2010
New Revision: 922146

URL: http://svn.apache.org/viewvc?rev=922146&view=rev
Log:
TAP5-1013: Add unit tests for service implementation reloading

Added:
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/com/
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/com/example/
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/com/example/ReloadModule.java   (with props)
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/com/example/ReloadableService.java   (with props)
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/ReloadTest.java   (with props)
Modified:
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ReloadableObjectCreator.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ServiceBinderImpl.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/resources/log4j.properties

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ReloadableObjectCreator.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ReloadableObjectCreator.java?rev=922146&r1=922145&r2=922146&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ReloadableObjectCreator.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ReloadableObjectCreator.java Fri Mar 12 05:21:16 2010
@@ -144,7 +144,7 @@ public class ReloadableObjectCreator imp
     private Class reloadImplementationClass()
     {
         if (logger.isDebugEnabled())
-            logger.debug("%s class %s.", firstTime ? "Loading" : "Reloading", implementationClassName);
+            logger.debug(String.format("%s class %s.", firstTime ? "Loading" : "Reloading", implementationClassName));
 
         ClassLoader reloadingClassLoader = new ReloadingClassLoader(baseClassLoader);
 

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ServiceBinderImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ServiceBinderImpl.java?rev=922146&r1=922145&r2=922146&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ServiceBinderImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ServiceBinderImpl.java Fri Mar 12 05:21:16 2010
@@ -178,13 +178,18 @@ public class ServiceBinderImpl implement
         return new ReloadableObjectCreatorSource(classFactory, bindMethod, serviceInterface, serviceImplementation);
     }
 
+    @SuppressWarnings("unchecked")
     public <T> ServiceBindingOptions bind(Class<T> serviceClass)
     {
         if (serviceClass.isInterface())
         {
             try
             {
-                Class<T> implementationClass = (Class<T>) Class.forName(serviceClass.getName() + "Impl");
+                String expectedImplName = serviceClass.getName() + "Impl";
+
+                ClassLoader classLoader = classFactory.getClassLoader();
+
+                Class<T> implementationClass = (Class<T>) classLoader.loadClass(expectedImplName);
 
                 if (!implementationClass.isInterface() && serviceClass.isAssignableFrom(implementationClass)) { return bind(
                         serviceClass, implementationClass); }

Added: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/com/example/ReloadModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/com/example/ReloadModule.java?rev=922146&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/com/example/ReloadModule.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/com/example/ReloadModule.java Fri Mar 12 05:21:16 2010
@@ -0,0 +1,26 @@
+// Copyright 2010 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 com.example;
+
+import org.apache.tapestry5.ioc.ServiceBinder;
+
+public class ReloadModule
+{
+    public static void bind(ServiceBinder binder)
+    {
+        // We'll use Javassist to create an implementation at runtime, then change that implementation.
+        binder.bind(ReloadableService.class);
+    }
+}

Propchange: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/com/example/ReloadModule.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/com/example/ReloadableService.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/com/example/ReloadableService.java?rev=922146&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/com/example/ReloadableService.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/com/example/ReloadableService.java Fri Mar 12 05:21:16 2010
@@ -0,0 +1,20 @@
+// Copyright 2010 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 com.example;
+
+public interface ReloadableService
+{
+    String getStatus();
+}

Propchange: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/com/example/ReloadableService.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/ReloadTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/ReloadTest.java?rev=922146&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/ReloadTest.java (added)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/ReloadTest.java Fri Mar 12 05:21:16 2010
@@ -0,0 +1,185 @@
+// Copyright 2010 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;
+
+import java.io.File;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtConstructor;
+import javassist.CtMethod;
+
+import org.apache.tapestry5.ioc.test.TestBase;
+import org.apache.tapestry5.services.UpdateListenerHub;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.example.ReloadModule;
+import com.example.ReloadableService;
+
+/**
+ * Test the ability to perform live class reloading of a service implementation.
+ */
+public class ReloadTest extends TestBase
+{
+    private static final String PACKAGE = "com.example";
+
+    private static final String CLASS = PACKAGE + ".ReloadableServiceImpl";
+
+    private File classesDir;
+
+    private ClassLoader classLoader;
+
+    @BeforeClass
+    public void setup() throws Exception
+    {
+        String uid = Long.toHexString(System.currentTimeMillis());
+
+        classesDir = new File(System.getProperty("java.io.tmpdir"), uid);
+
+        // URLClassLoader REQUIRES that File URLs end with a slash! That's a half hour of my life gone!
+
+        URL classesURL = new URL("file:" + classesDir.getCanonicalPath() + "/");
+
+        System.out.println("Reload classes dir: " + classesURL);
+
+        classLoader = new URLClassLoader(new URL[]
+        { classesURL }, Thread.currentThread().getContextClassLoader());
+    }
+
+    @Test
+    public void reload_a_service_implementation() throws Exception
+    {
+        // First, create the initial implementation
+
+        createImplementationClass("initial");
+
+        Registry registry = createRegistry();
+
+        ReloadableService reloadable = registry.getService(ReloadableService.class);
+
+        fireUpdateCheck(registry);
+
+        assertEquals(reloadable.getStatus(), "initial");
+
+        fireUpdateCheck(registry);
+
+        // Sleep long enough that the Java millisecond clock advances.
+
+        Thread.currentThread().sleep(1500);
+
+        createImplementationClass("updated");
+
+        // Doesn't take effect until after the update check
+
+        assertEquals(reloadable.getStatus(), "initial");
+
+        fireUpdateCheck(registry);
+
+        assertEquals(reloadable.getStatus(), "updated");
+    }
+
+    private void fireUpdateCheck(Registry registry)
+    {
+        registry.getService(UpdateListenerHub.class).fireCheckForUpdates();
+    }
+
+    private Registry createRegistry()
+    {
+        RegistryBuilder builder = new RegistryBuilder(classLoader);
+
+        builder.add(ReloadModule.class);
+
+        return builder.build();
+    }
+
+    @Test
+    public void invalid_service_implementation() throws Exception
+    {
+        createImplementationClass("initial");
+
+        Registry registry = createRegistry();
+
+        ReloadableService reloadable = registry.getService(ReloadableService.class);
+
+        createInvalidImplentationClass();
+
+        Thread.currentThread().sleep(1500);
+
+        fireUpdateCheck(registry);
+
+        try
+        {
+            reloadable.getStatus();
+
+            unreachable();
+        }
+        catch (Exception ex)
+        {
+            assertEquals(ex.getMessage(),
+                    "Service implementation class com.example.ReloadableServiceImpl does not have a suitable public constructor.");
+        }
+    }
+
+    private void createImplementationClass(String status) throws Exception
+    {
+        ClassPool pool = new ClassPool(null);
+
+        pool.appendSystemPath();
+
+        CtClass ctClass = pool.makeClass(CLASS);
+
+        ctClass.addInterface(pool.get(ReloadableService.class.getName()));
+
+        CtMethod method = new CtMethod(pool.get("java.lang.String"), "getStatus", null, ctClass);
+
+        method.setBody(String.format("return \"%s\";", status));
+
+        ctClass.addMethod(method);
+
+        ctClass.writeFile(classesDir.getAbsolutePath());
+    }
+
+    private void createInvalidImplentationClass() throws Exception
+    {
+        ClassPool pool = new ClassPool(null);
+
+        pool.appendSystemPath();
+
+        CtClass ctClass = pool.makeClass(CLASS);
+
+        ctClass.addInterface(pool.get(ReloadableService.class.getName()));
+
+        CtMethod method = new CtMethod(pool.get("java.lang.String"), "getStatus", null, ctClass);
+
+        method.setBody("return \"unreachable\";");
+
+        ctClass.addMethod(method);
+
+        CtConstructor constructor = new CtConstructor(new CtClass[0], ctClass);
+
+        constructor.setBody("return $0;");
+
+        constructor.setModifiers(Modifier.PROTECTED);
+
+        ctClass.addConstructor(constructor);
+
+        ctClass.writeFile(classesDir.getAbsolutePath());
+
+    }
+}

Propchange: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/ReloadTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/test/resources/log4j.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/resources/log4j.properties?rev=922146&r1=922145&r2=922146&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/resources/log4j.properties (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/resources/log4j.properties Fri Mar 12 05:21:16 2010
@@ -1,4 +1,4 @@
-# Copyright 2005, 2006, 2009 The Apache Software Foundation
+# Copyright 2005, 2006, 2009, 2010 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.
@@ -28,5 +28,6 @@ log4j.category.tapestry.ioc.ClassFactory
 log4j.category.org.apache.tapestry5.ioc.FredModule=debug
 log4j.category.org.apache.tapestry5.ioc.AdviceDemoModule.Greeter=debug
 
+log4j.category.com.example=debug