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 2006/07/24 03:12:56 UTC

svn commit: r424872 - 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/services/ test/java/org/apache/tapestry/internal/io...

Author: hlship
Date: Sun Jul 23 18:12:55 2006
New Revision: 424872

URL: http://svn.apache.org/viewvc?rev=424872&view=rev
Log:
Add new methods to ClassFab to make it easier to add an implementation of toString(), or to proxy method invocations to a delegate.

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/services/ClassFabImpl.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/ioc/services/ClassFab.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassFabUtils.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/services/ClassFabImplTest.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=424872&r1=424871&r2=424872&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 Sun Jul 23 18:12:55 2006
@@ -33,7 +33,6 @@
 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.MethodIterator;
 import org.apache.tapestry.ioc.services.MethodSignature;
 import org.apache.tapestry.util.BodyBuilder;
 
@@ -291,41 +290,19 @@
 
         ClassFab cf = factory.newClass(name, Object.class);
 
-        addInfrastructure(cf, serviceInterface);
+        cf.addField("_creator", ServiceCreator.class);
+        cf.addField("_delegate", serviceInterface);
+
+        cf.addConstructor(new Class[]
+        { ServiceCreator.class }, null, "_creator = $1;");
 
         addDelegateGetter(cf, serviceInterface);
 
-        addServiceMethods(cf, serviceId, serviceInterface, proxyDescription);
+        cf.proxyMethodsToDelegate(serviceInterface, "_delegate()", proxyDescription);
 
         return cf.createClass();
     }
 
-    private void addServiceMethods(ClassFab cf, String serviceId, Class serviceInterface,
-            String proxyDescription)
-    {
-        MethodIterator mi = new MethodIterator(serviceInterface);
-
-        while (mi.hasNext())
-        {
-            MethodSignature sig = mi.next();
-
-            // Make each method in the proxy delegates to the service implementation (or outermost
-            // interceptor). The "($r)" cast is magic for converting to the return type and
-            // understands void methods. The "$$" is just the list of method arguments.
-
-            String body = "return ($r) _delegate()." + sig.getName() + "($$);";
-
-            cf.addMethod(Modifier.PUBLIC, sig, body);
-        }
-
-        // If toString() is not part of the service interface, then add a toString()
-        // to the class. Objects visible to client code need a toString() to help
-        // with debugging.
-
-        if (!mi.getToString())
-            ClassFabUtils.addToStringMethod(cf, proxyDescription);
-    }
-
     private void addDelegateGetter(ClassFab cf, Class serviceInterface)
     {
         BodyBuilder builder = new BodyBuilder();
@@ -345,19 +322,10 @@
         MethodSignature sig = new MethodSignature(serviceInterface, "_delegate", null, null);
 
         // Here's the rub, this _delegate() method has to be synchronized. But after the first
-        // time through (when we create the service), the time inside the method is minimal.
+        // time through (when we create the service), the time inside the method is infintesmal.
         // Let's hope that they aren't lying when they say that synchronized is now super cheap!
 
         cf.addMethod(Modifier.PRIVATE | Modifier.SYNCHRONIZED, sig, builder.toString());
     }
 
-    private void addInfrastructure(ClassFab cf, Class serviceInterface)
-    {
-        cf.addInterface(serviceInterface);
-        cf.addField("_delegate", serviceInterface);
-        cf.addField("_creator", ServiceCreator.class);
-
-        cf.addConstructor(new Class[]
-        { ServiceCreator.class }, null, "_creator = $1;");
-    }
 }

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=424872&r1=424871&r2=424872&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 Sun Jul 23 18:12:55 2006
@@ -15,6 +15,7 @@
 package org.apache.tapestry.internal.ioc.services;
 
 import java.lang.reflect.Modifier;
+import java.util.Formatter;
 import java.util.Set;
 
 import javassist.CannotCompileException;
@@ -28,6 +29,7 @@
 import org.apache.tapestry.internal.util.InternalUtils;
 import org.apache.tapestry.ioc.services.ClassFab;
 import org.apache.tapestry.ioc.services.ClassFabUtils;
+import org.apache.tapestry.ioc.services.MethodIterator;
 import org.apache.tapestry.ioc.services.MethodSignature;
 import org.apache.tapestry.util.Defense;
 
@@ -48,6 +50,8 @@
      */
     private final StringBuffer _description = new StringBuffer();
 
+    private final Formatter _formatter = new Formatter(_description);
+
     private final Set<MethodSignature> _addedSignatures = newSet();
 
     public ClassFabImpl(CtClassSource source, CtClass ctClass)
@@ -140,7 +144,38 @@
         }
 
         _description
-                .append(format("private %s %s;\n\n", ClassFabUtils.getJavaClassName(type), name));
+                .append(_formatter.format("private %s %s;\n\n", ClassFabUtils.getJavaClassName(type), name));
+    }
+
+    public void proxyMethodsToDelegate(Class serviceInterface, String delegateExpression, String toString)
+    {
+        addInterface(serviceInterface);
+
+        MethodIterator mi = new MethodIterator(serviceInterface);
+
+        while (mi.hasNext())
+        {
+            MethodSignature sig = mi.next();
+
+            // ($r) properly handles void methods for us, which keeps this simple.
+
+            String body = format("return ($r) %s.%s($$);", delegateExpression, sig.getName());
+
+            addMethod(Modifier.PUBLIC, sig, body);
+        }
+
+        if (!mi.getToString())
+            addToString(toString);
+    }
+
+    public void addToString(String toString)
+    {
+        MethodSignature sig = new MethodSignature(String.class, "toString", null, null);
+
+        // TODO: Very simple quoting here, will break down if the string itself contains
+        // double quotes or various other characters that need escaping.
+
+        addMethod(Modifier.PUBLIC, sig, format("return \"%s\";", toString));
     }
 
     public void addMethod(int modifiers, MethodSignature ms, String body)
@@ -172,7 +207,7 @@
 
         // modifiers, return type, name
 
-        _description.append(format("%s %s %s", Modifier.toString(modifiers), ClassFabUtils
+        _description.append(_formatter.format("%s %s %s", Modifier.toString(modifiers), ClassFabUtils
                 .getJavaClassName(ms.getReturnType()), ms.getName()));
 
         // parameters, exceptions and body from this:

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=424872&r1=424871&r2=424872&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 Sun Jul 23 18:12:55 2006
@@ -95,9 +95,7 @@
         }
 
         if (!mi.getToString())
-            ClassFabUtils.addToStringMethod(cf, ServiceMessages.loggingInterceptor(
-                    serviceId,
-                    serviceInterface));
+            cf.addToString(ServiceMessages.loggingInterceptor(serviceId, serviceInterface));
     }
 
     private void addMethod(ClassFab cf, MethodSignature signature)

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=424872&r1=424871&r2=424872&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 Sun Jul 23 18:12:55 2006
@@ -50,13 +50,12 @@
     /**
      * Adds the specified interface as an interface implemented by this class.
      */
-    public void addInterface(Class interfaceClass);
+    void addInterface(Class interfaceClass);
 
     /**
      * Adds a new field with the given name and type. The field is added as a private field.
      */
-
-    public void addField(String name, Class type);
+    void addField(String name, Class type);
 
     /**
      * Adds a method. The method is a public instance method.
@@ -72,8 +71,7 @@
      *             if a method with that signature has already been added, or if there is a
      *             Javassist compilation error
      */
-
-    public void addMethod(int modifiers, MethodSignature signature, String body);
+    void addMethod(int modifiers, MethodSignature signature, String body);
 
     /**
      * Adds a constructor to the class. The constructor will be public.
@@ -85,11 +83,32 @@
      * @param body
      *            The body of the constructor.
      */
-    public void addConstructor(Class[] parameterTypes, Class[] exceptions, String body);
+    void addConstructor(Class[] parameterTypes, Class[] exceptions, String body);
+
+    /** Adds an implementation of toString, as a method that returns a fixed string. */
+    void addToString(String toString);
+
+    /**
+     * Makes the fabricated class implement the provided service interface. The interface will be
+     * added, and all methods in the interface will be delegate wrappers. If toString() is not part
+     * of the delegate interface, then an implementation will be supplied that returns the provided
+     * string. This method is used when creating objects that proxy their behavior to some other
+     * object.
+     * 
+     * @param serviceInterface
+     *            the interface to implement
+     * @param delegateExpression
+     *            the expression used to find the delegate on which methods should be invoked.
+     *            Typically a field name, such as "_delegate", or a method to invoke, such as
+     *            "_service()".
+     * @param toString
+     *            fixed value to be returned as the description of the resultant object
+     */
+    void proxyMethodsToDelegate(Class serviceInterface, String delegateExpression, String toString);
 
     /**
      * Invoked last to create the class. This will enforce that all abstract methods have been
      * implemented in the (concrete) class.
      */
-    public Class createClass();
+    Class createClass();
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassFabUtils.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassFabUtils.java?rev=424872&r1=424871&r2=424872&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassFabUtils.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassFabUtils.java Sun Jul 23 18:12:55 2006
@@ -15,7 +15,6 @@
 package org.apache.tapestry.ioc.services;
 
 import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
 
 import org.apache.tapestry.internal.annotations.Utility;
 
@@ -30,12 +29,6 @@
 {
     private static long _uid = System.currentTimeMillis();
 
-    private static final char QUOTE = '"';
-
-    private ClassFabUtils()
-    {
-    }
-
     /**
      * Generates a unique class name, which will be in the default package.
      */
@@ -85,68 +78,5 @@
             return false;
 
         return method.getReturnType().equals(String.class);
-    }
-
-    /**
-     * Adds a <code>toString()</code> method to a class that returns a fixed, pre-computed value.
-     * 
-     * @param classFab
-     *            ClassFab used to construct the new class.
-     * @param toStringResult
-     *            fixed result to be returned by the method.
-     */
-    public static void addToStringMethod(ClassFab classFab, String toStringResult)
-    {
-        StringBuffer buffer = new StringBuffer("return ");
-        buffer.append(QUOTE);
-        buffer.append(toStringResult);
-        buffer.append(QUOTE);
-        buffer.append(";");
-
-        classFab.addMethod(Modifier.PUBLIC, new MethodSignature(String.class, "toString", null,
-                null), buffer.toString());
-    }
-
-    /**
-     * Adds a method that does nothing. If the method returns a value, it will return null, 0 or
-     * false (depending on the type).
-     * <p>
-     * TODO: Move this into ClassFab.
-     */
-
-    public static void addNoOpMethod(ClassFab cf, MethodSignature m)
-    {
-        StringBuffer body = new StringBuffer("{ ");
-
-        Class returnType = m.getReturnType();
-
-        if (returnType != void.class)
-        {
-            body.append("return");
-
-            if (returnType.isPrimitive())
-            {
-                if (returnType == boolean.class)
-                    body.append(" false");
-                else if (returnType == long.class)
-                    body.append(" 0L");
-                else if (returnType == float.class)
-                    body.append(" 0.0f");
-                else if (returnType == double.class)
-                    body.append(" 0.0d");
-                else
-                    body.append(" 0");
-            }
-            else
-            {
-                body.append(" null");
-            }
-
-            body.append(";");
-        }
-
-        body.append(" }");
-
-        cf.addMethod(Modifier.PUBLIC, m, body.toString());
     }
 }

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=424872&r1=424871&r2=424872&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 Sun Jul 23 18:12:55 2006
@@ -14,9 +14,6 @@
 
 package org.apache.tapestry.internal.ioc.services;
 
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertTrue;
-
 import java.io.Serializable;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Modifier;
@@ -27,13 +24,16 @@
 import javassist.CtClass;
 
 import org.apache.hivemind.impl.BaseLocatable;
-import org.apache.hivemind.service.MethodFab;
 import org.apache.hivemind.util.PropertyUtils;
+import org.apache.tapestry.internal.ioc.services.LoggingDecoratorImplTest.ToStringService;
 import org.apache.tapestry.ioc.services.ClassFab;
 import org.apache.tapestry.ioc.services.MethodSignature;
 import org.apache.tapestry.test.BaseTestCase;
 import org.testng.annotations.Test;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
 /**
  * @author Howard M. Lewis Ship
  */
@@ -41,6 +41,20 @@
 {
     private final CtClassSource _source;
 
+    public interface SampleService
+    {
+        int primitiveMethod(int primitiveValue);
+
+        void voidMethod(String input);
+
+        String objectMethod(String input);
+    }
+
+    public interface SampleToStringService
+    {
+        String toString();
+    }
+
     public ClassFabImplTest()
     {
         ClassLoader threadLoader = Thread.currentThread().getContextClassLoader();
@@ -88,6 +102,84 @@
         String actual = (String) PropertyUtils.read(targetBean, "stringValue");
 
         assertEquals(actual, "Fred");
+    }
+
+    @Test
+    public void add_to_string() throws Exception
+    {
+        ClassFab cf = newClassFab("ToString", Object.class);
+
+        cf.addToString("ToString Description");
+
+        Class clazz = cf.createClass();
+
+        Object instance = clazz.newInstance();
+
+        assertEquals(instance.toString(), "ToString Description");
+    }
+
+    @Test
+    public void proxy_methods_to_delegate() throws Exception
+    {
+        ClassFab cf = newClassFab("Delegator", Object.class);
+
+        cf.addField("_delegate", SampleService.class);
+        cf.addConstructor(new Class[]
+        { SampleService.class }, null, "_delegate = $1;");
+
+        cf.proxyMethodsToDelegate(SampleService.class, "_delegate", "<Delegator>");
+
+        SampleService delegate = newMock(SampleService.class);
+
+        Class clazz = cf.createClass();
+
+        SampleService proxy = (SampleService) clazz.getConstructors()[0].newInstance(delegate);
+
+        delegate.primitiveMethod(5);
+        setReturnValue(10);
+
+        delegate.voidMethod("fred");
+
+        delegate.objectMethod("barney");
+        setReturnValue("rubble");
+
+        replay();
+
+        assertEquals(proxy.primitiveMethod(5), 10);
+
+        proxy.voidMethod("fred");
+
+        assertEquals(proxy.objectMethod("barney"), "rubble");
+        assertEquals(proxy.toString(), "<Delegator>");
+
+        verify();
+    }
+
+    @Test
+    public void proxy_methods_to_delegate_with_to_string() throws Exception
+    {
+        ClassFab cf = newClassFab("ToStringDelegator", Object.class);
+
+        cf.addField("_delegate", ToStringService.class);
+        cf.addConstructor(new Class[]
+        { ToStringService.class }, null, "_delegate = $1;");
+
+        cf.proxyMethodsToDelegate(ToStringService.class, "_delegate", "<ToStringDelegator>");
+
+        ToStringService delegate = new ToStringService()
+        {
+            @Override
+            public String toString()
+            {
+                return "ACTUAL TO-STRING";
+            }
+        };
+
+        Class clazz = cf.createClass();
+
+        ToStringService proxy = (ToStringService) clazz.getConstructors()[0].newInstance(delegate);
+
+        assertEquals(proxy.toString(), "ACTUAL TO-STRING");
     }
 
     @Test