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;
+ }
+
+}