Author: hlship
Date: Sun May 13 08:52:22 2007
New Revision: 537605

URL: http://svn.apache.org/viewvc?view=rev&rev=537605
Log:
TAPESTRY-1423: Tapestry IoC fail to get the correct class from 
javassist.CtClass when the instance is already a proxy

Added:
    
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PersistentLocaleImpl.java
    
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/services/TargetBean.java
Modified:
    
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PropertyConduitSourceImpl.java
    
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PropertyConduitSourceImplTest.java
    tapestry/tapestry5/trunk/tapestry-ioc/   (props changed)
    tapestry/tapestry5/trunk/tapestry-ioc/.classpath
    
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/ClassFactoryClassPool.java
    
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/ClassFactoryImpl.java
    
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/services/ClassFactory.java
    
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/services/ClassFactoryImplTest.java

Added: 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PersistentLocaleImpl.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PersistentLocaleImpl.java?view=auto&rev=537605
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PersistentLocaleImpl.java
 (added)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PersistentLocaleImpl.java
 Sun May 13 08:52:22 2007
@@ -0,0 +1,59 @@
+// Copyright 2007 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.services;
+
+import java.util.Locale;
+
+import org.apache.tapestry.ioc.annotations.InjectService;
+import org.apache.tapestry.services.Cookies;
+import org.apache.tapestry.services.PersistentLocale;
+
+public class PersistentLocaleImpl implements PersistentLocale
+{
+    /**
+     * Name of the cookie written to the client web browser to identify the 
locale.
+     */
+    private static final String LOCALE_COOKIE_NAME = 
"org.apache.tapestry.locale";
+
+    private Cookies _cookieSource;
+
+    public PersistentLocaleImpl(@InjectService("Cookies")
+    Cookies cookieSource)
+    {
+        _cookieSource = cookieSource;
+    }
+
+    public void set(Locale locale)
+    {
+        _cookieSource.writeCookieValue(LOCALE_COOKIE_NAME, locale.toString());
+    }
+
+    public Locale get()
+    {
+        String localeCookieValue = getCookieValue();
+        return localeCookieValue != null ? new Locale(localeCookieValue) : 
null;
+    }
+
+    private String getCookieValue()
+    {
+        return _cookieSource.readCookieValue(LOCALE_COOKIE_NAME);
+    }
+
+    public boolean isSet()
+    {
+        return getCookieValue() != null;
+    }
+
+}

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PropertyConduitSourceImpl.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PropertyConduitSourceImpl.java?view=diff&rev=537605&r1=537604&r2=537605
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PropertyConduitSourceImpl.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PropertyConduitSourceImpl.java
 Sun May 13 08:52:22 2007
@@ -44,6 +44,8 @@
 
     private final ClassFactory _classFactory;
 
+    private final Map<Class, Class> _classToEffectiveClass = 
newConcurrentMap();
+
     /** Keyed on combination of root class and expression. */
     private final Map<MultiKey, PropertyConduit> _cache = newConcurrentMap();
 
@@ -66,13 +68,15 @@
         notNull(rootClass, "rootClass");
         notBlank(expression, "expression");
 
-        MultiKey key = new MultiKey(rootClass, expression);
+        Class effectiveClass = toEffectiveClass(rootClass);
+
+        MultiKey key = new MultiKey(effectiveClass, expression);
 
         PropertyConduit result = _cache.get(key);
 
         if (result == null)
         {
-            result = build(rootClass, expression);
+            result = build(effectiveClass, expression);
             _cache.put(key, result);
 
         }
@@ -80,6 +84,20 @@
         return result;
     }
 
+    private Class toEffectiveClass(Class rootClass)
+    {
+        Class result = _classToEffectiveClass.get(rootClass);
+
+        if (result == null)
+        {
+            result = _classFactory.importClass(rootClass);
+
+            _classToEffectiveClass.put(rootClass, result);
+        }
+
+        return result;
+    }
+
     /**
      * Clears its cache when the component class loader is invalidated; this 
is because it will be
      * common to generated conduits rooted in a component class (which will no 
longer be valid and
@@ -88,6 +106,7 @@
     public void objectWasInvalidated()
     {
         _cache.clear();
+        _classToEffectiveClass.clear();
     }
 
     /**
@@ -174,8 +193,7 @@
             String term = terms[i];
 
             boolean nullable = term.endsWith("?");
-            if (nullable)
-                term = term.substring(0, term.length() - 1);
+            if (nullable) term = term.substring(0, term.length() - 1);
 
             Method readMethod = readMethodForTerm(
                     activeType,
@@ -202,8 +220,7 @@
                     previousStep,
                     readMethod.getName());
 
-            if (nullable)
-                builder.addln("if (%s == null) return null;", thisStep);
+            if (nullable) builder.addln("if (%s == null) return null;", 
thisStep);
 
             activeType = termType;
             result = readMethod;
@@ -245,8 +262,7 @@
             String term = terms[i];
 
             boolean nullable = term.endsWith("?");
-            if (nullable)
-                term = term.substring(0, term.length() - 1);
+            if (nullable) term = term.substring(0, term.length() - 1);
 
             Method readMethod = readMethodForTerm(activeType, expression, 
term, true);
 
@@ -263,8 +279,7 @@
                     previousStep,
                     readMethod.getName());
 
-            if (nullable)
-                builder.addln("if (%s == null) return;", thisStep);
+            if (nullable) builder.addln("if (%s == null) return;", thisStep);
 
             activeType = termType;
             previousStep = thisStep;
@@ -309,8 +324,7 @@
 
     private Method writeMethodForTerm(Class activeType, String expression, 
String term)
     {
-        if (term.endsWith(PARENS))
-            return null;
+        if (term.endsWith(PARENS)) return null;
 
         PropertyAdapter adapter = 
_access.getAdapter(activeType).getPropertyAdapter(term);
 

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PropertyConduitSourceImplTest.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PropertyConduitSourceImplTest.java?view=diff&rev=537605&r1=537604&r2=537605
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PropertyConduitSourceImplTest.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/PropertyConduitSourceImplTest.java
 Sun May 13 08:52:22 2007
@@ -14,9 +14,14 @@
 
 package org.apache.tapestry.internal.services;
 
+import java.io.Serializable;
+
 import org.apache.tapestry.PropertyConduit;
 import org.apache.tapestry.internal.bindings.PropBindingFactoryTest;
 import org.apache.tapestry.internal.test.InternalBaseTestCase;
+import org.apache.tapestry.ioc.internal.services.ClassFactoryImpl;
+import org.apache.tapestry.ioc.services.ClassFab;
+import org.apache.tapestry.ioc.services.ClassFactory;
 import org.apache.tapestry.services.PropertyConduitSource;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
@@ -76,4 +81,35 @@
 
         smart.set(bean, "Howard");
     }
+
+    /**
+     * Or call this the "Hibernate" case; Hibernate creates sub-classes of 
entity classes in its own
+     * class loader to do all sorts of proxying. This trips up Javassist.
+     */
+    @Test
+    public void handle_beans_from_unexpected_classloader() throws Exception
+    {
+        // First, create something that looks like a Hibernate proxy.
+
+        ClassFactory factory = new ClassFactoryImpl();
+
+        Class clazz = SimpleBean.class;
+
+        ClassFab cf = factory.newClass(clazz.getName() + "$$Proxy", clazz);
+
+        cf.addInterface(Serializable.class);
+
+        Class proxyClass = cf.createClass();
+
+        SimpleBean simple = (SimpleBean) proxyClass.newInstance();
+
+        assertTrue(simple instanceof Serializable);
+
+        simple.setFirstName("Howard");
+
+        PropertyConduit conduit = _source.create(proxyClass, "firstName");
+
+        assertEquals(conduit.get(simple), "Howard");
+    }
+
 }

Propchange: tapestry/tapestry5/trunk/tapestry-ioc/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Sun May 13 08:52:22 2007
@@ -4,3 +4,4 @@
 temp-testng-customsuite.xml
 test-output
 null
+bin-test

Modified: tapestry/tapestry5/trunk/tapestry-ioc/.classpath
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/.classpath?view=diff&rev=537605&r1=537604&r2=537605
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/.classpath (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/.classpath Sun May 13 08:52:22 2007
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
        <classpathentry kind="src" path="src/main/java"/>
-       <classpathentry kind="src" path="src/test/java"/>
+       <classpathentry kind="src" output="bin-test" path="src/test/java"/>
        <classpathentry kind="con" 
path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
        <classpathentry kind="lib" path="src/main/resources"/>
        <classpathentry kind="con" 
path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>

Modified: 
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/ClassFactoryClassPool.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/ClassFactoryClassPool.java?view=diff&rev=537605&r1=537604&r2=537605
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/ClassFactoryClassPool.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/ClassFactoryClassPool.java
 Sun May 13 08:52:22 2007
@@ -20,9 +20,13 @@
 import java.util.Set;
 import java.util.Map;
 
+import org.apache.tapestry.ioc.services.ClassFabUtils;
+
 import javassist.ClassPath;
 import javassist.ClassPool;
+import javassist.CtClass;
 import javassist.LoaderClassPath;
+import javassist.NotFoundException;
 
 /**
  * Used to ensure that [EMAIL PROTECTED] 
javassist.ClassPool#appendClassPath(javassist.ClassPath)} is invoked
@@ -43,6 +47,39 @@
         super(null);
 
         addClassLoaderIfNeeded(contextClassLoader);
+    }
+
+    /**
+     * Returns the nearest super-class of the provided class that can be 
converted to a
+     * [EMAIL PROTECTED] CtClass}. This is used to filter out Hibernate-style 
proxies (created as subclasses
+     * of oridnary classes). This will automatically add the class' 
classLoader to the pool's class
+     * path.
+     * 
+     * @param clazz
+     *            class to import
+     * @return clazz, or a super-class of clazz
+     */
+    public Class importClass(Class clazz)
+    {
+        addClassLoaderIfNeeded(clazz.getClassLoader());
+
+        while (true)
+        {
+            try
+            {
+                String name = ClassFabUtils.toJavaClassName(clazz);
+
+                get(name);
+
+                break;
+            }
+            catch (NotFoundException ex)
+            {
+                clazz = clazz.getSuperclass();
+            }
+        }
+
+        return clazz;
     }
 
     /**

Modified: 
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/ClassFactoryImpl.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/ClassFactoryImpl.java?view=diff&rev=537605&r1=537604&r2=537605
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/ClassFactoryImpl.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/ClassFactoryImpl.java
 Sun May 13 08:52:22 2007
@@ -104,6 +104,11 @@
         }
     }
 
+    public Class importClass(Class clazz)
+    {
+        return _pool.importClass(clazz);
+    }
+
     public int getCreatedClassCount()
     {
         return _classSource.getCreatedClassCount();

Modified: 
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/services/ClassFactory.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/services/ClassFactory.java?view=diff&rev=537605&r1=537604&r2=537605
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/services/ClassFactory.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/services/ClassFactory.java
 Sun May 13 08:52:22 2007
@@ -46,6 +46,18 @@
     ClassFab newClass(String name, Class superClass);
 
     /**
+     * Imports the class to make it referenceable within the factory. The 
class loader for the class
+     * is added to the class path. The class itself is returned, if its 
bytecode is available. If
+     * not, a search up the inhertance occurs until a proper class (that can 
be referenced in
+     * generated bytecode) is found. This is necessary to handle cases where a 
class is generated at
+     * runtime, outside of the class factory, and bytecode is not available 
for it.
+     * 
+     * @param clazz
+     * @return a referencable super-class
+     */
+    Class importClass(Class clazz);
+
+    /**
      * Returns the number of classes (and interfaces) actually created.
      */
 

Modified: 
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/services/ClassFactoryImplTest.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/services/ClassFactoryImplTest.java?view=diff&rev=537605&r1=537604&r2=537605
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/services/ClassFactoryImplTest.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/services/ClassFactoryImplTest.java
 Sun May 13 08:52:22 2007
@@ -18,6 +18,7 @@
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 
+import org.apache.tapestry.ioc.internal.util.LocationImpl;
 import org.apache.tapestry.ioc.services.ClassFab;
 import org.apache.tapestry.ioc.services.ClassFabUtils;
 import org.apache.tapestry.ioc.services.ClassFactory;
@@ -132,5 +133,37 @@
                 .getConstructorLocation(cc)
                 .matches(
                         
"org.apache.tapestry.ioc.internal.services.LineNumberBean\\(String, int\\) 
\\(at LineNumberBean.java:(19|20)\\)"));
+    }
+
+    /**
+     * Import a class (or two) where the class is from a known and available 
class loader.
+     */
+    @Test
+    public void import_ordinary_class()
+    {
+        ClassFactory factory = new ClassFactoryImpl();
+
+        assertSame(factory.importClass(Object.class), Object.class);
+        assertSame(factory.importClass(LocationImpl.class), 
LocationImpl.class);
+    }
+
+    /**
+     * Import a class where the bytecode is not available, to ensure that the 
super-class (from an
+     * available class loader) is returned.
+     */
+    @Test
+    public void import_proxy_class() throws Exception
+    {
+        ClassFactory alienFactory = new ClassFactoryImpl();
+
+        Class<TargetBean> clazz = TargetBean.class;
+
+        ClassFab cf = alienFactory.newClass(clazz.getName() + "$$Proxy", 
clazz);
+
+        Class alienClass = cf.createClass();
+
+        ClassFactory factory = new ClassFactoryImpl();
+
+        assertSame(factory.importClass(alienClass), clazz);
     }
 }

Added: 
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/services/TargetBean.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/services/TargetBean.java?view=auto&rev=537605
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/services/TargetBean.java
 (added)
+++ 
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/internal/services/TargetBean.java
 Sun May 13 08:52:22 2007
@@ -0,0 +1,46 @@
+// Copyright 2007 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.internal.services;
+
+/**
+ * Used by [EMAIL PROTECTED] ClassFactoryImplTest}.
+ */
+public class TargetBean
+{
+    private String _firstName;
+
+    private String _lastName;
+
+    public String getFirstName()
+    {
+        return _firstName;
+    }
+
+    public String getLastName()
+    {
+        return _lastName;
+    }
+
+    public void setFirstName(String firstName)
+    {
+        _firstName = firstName;
+    }
+
+    public void setLastName(String lastName)
+    {
+        _lastName = lastName;
+    }
+
+}


Reply via email to