Option to create proxies for standard java.util types at build time. These

proxies can be serialized as proxies for use with detached state managers,
whereas runtime-generated proxies have to serialize themselves as a copied 
instance of their corresponding java.util type in case they are transferred to 
a different classloading environment.



git-svn-id: https://svn.apache.org/repos/asf/incubator/openjpa/trunk@486825 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
A. Abram White 2006-12-13 20:14:26 +00:00
parent dfca20d465
commit d6dbadaa3c
3 changed files with 156 additions and 79 deletions

View File

@ -78,13 +78,10 @@ public class Proxies {
/** /**
* Used by proxy types to serialize non-proxy versions. * Used by proxy types to serialize non-proxy versions.
*/ */
public static Object writeReplace(Proxy proxy) { public static Object writeReplace(Proxy proxy, boolean detachable) {
// we can't send dynamically-created classes to another JVM if (detachable && (proxy == null || proxy.getOwner() == null
/* || proxy.getOwner().isDetached()))
if (proxy == null || proxy.getOwner() == null
|| proxy.getOwner().isDetached())
return proxy; return proxy;
*/
return proxy.copy(proxy); return proxy.copy(proxy);
} }
} }

View File

@ -24,6 +24,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collection; import java.util.Collection;
import java.util.Comparator; import java.util.Comparator;
@ -46,6 +47,7 @@ import org.apache.openjpa.kernel.OpenJPAStateManager;
import org.apache.openjpa.lib.util.Files; import org.apache.openjpa.lib.util.Files;
import org.apache.openjpa.lib.util.JavaVersions; import org.apache.openjpa.lib.util.JavaVersions;
import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.lib.util.Options;
import org.apache.openjpa.lib.util.concurrent.ConcurrentHashMap; import org.apache.openjpa.lib.util.concurrent.ConcurrentHashMap;
import serp.bytecode.BCClass; import serp.bytecode.BCClass;
import serp.bytecode.BCClassLoader; import serp.bytecode.BCClassLoader;
@ -331,9 +333,11 @@ public class ProxyManagerImpl
// we don't lock here; ok if two proxies get generated for same type // we don't lock here; ok if two proxies get generated for same type
ProxyCollection proxy = (ProxyCollection) _proxies.get(type); ProxyCollection proxy = (ProxyCollection) _proxies.get(type);
if (proxy == null) { if (proxy == null) {
proxy = (ProxyCollection) instantiateProxy ClassLoader l = getMostDerivedLoader(type, ProxyCollection.class);
(generateProxyCollectionBytecode(type), type, Class pcls = loadBuildTimeProxy(type, l);
ProxyCollection.class, null, null); if (pcls == null)
pcls = loadProxy(generateProxyCollectionBytecode(type, true),l);
proxy = (ProxyCollection) instantiateProxy(pcls, null, null);
_proxies.put(type, proxy); _proxies.put(type, proxy);
} }
return proxy; return proxy;
@ -346,8 +350,11 @@ public class ProxyManagerImpl
// we don't lock here; ok if two proxies get generated for same type // we don't lock here; ok if two proxies get generated for same type
ProxyMap proxy = (ProxyMap) _proxies.get(type); ProxyMap proxy = (ProxyMap) _proxies.get(type);
if (proxy == null) { if (proxy == null) {
proxy = (ProxyMap) instantiateProxy(generateProxyMapBytecode(type), ClassLoader l = getMostDerivedLoader(type, ProxyMap.class);
type, ProxyMap.class, null, null); Class pcls = loadBuildTimeProxy(type, l);
if (pcls == null)
pcls = loadProxy(generateProxyMapBytecode(type, true), l);
proxy = (ProxyMap) instantiateProxy(pcls, null, null);
_proxies.put(type, proxy); _proxies.put(type, proxy);
} }
return proxy; return proxy;
@ -360,9 +367,11 @@ public class ProxyManagerImpl
// we don't lock here; ok if two proxies get generated for same type // we don't lock here; ok if two proxies get generated for same type
ProxyDate proxy = (ProxyDate) _proxies.get(type); ProxyDate proxy = (ProxyDate) _proxies.get(type);
if (proxy == null) { if (proxy == null) {
proxy = (ProxyDate) instantiateProxy ClassLoader l = getMostDerivedLoader(type, ProxyDate.class);
(generateProxyDateBytecode(type), type, ProxyDate.class, null, Class pcls = loadBuildTimeProxy(type, l);
null); if (pcls == null)
pcls = loadProxy(generateProxyDateBytecode(type, true), l);
proxy = (ProxyDate) instantiateProxy(pcls, null, null);
_proxies.put(type, proxy); _proxies.put(type, proxy);
} }
return proxy; return proxy;
@ -375,9 +384,11 @@ public class ProxyManagerImpl
// we don't lock here; ok if two proxies get generated for same type // we don't lock here; ok if two proxies get generated for same type
ProxyCalendar proxy = (ProxyCalendar) _proxies.get(type); ProxyCalendar proxy = (ProxyCalendar) _proxies.get(type);
if (proxy == null) { if (proxy == null) {
proxy = (ProxyCalendar) instantiateProxy ClassLoader l = getMostDerivedLoader(type, ProxyCalendar.class);
(generateProxyCalendarBytecode(type), type, ProxyCalendar.class, Class pcls = loadBuildTimeProxy(type, l);
null, null); if (pcls == null)
pcls = loadProxy(generateProxyCalendarBytecode(type, true), l);
proxy = (ProxyCalendar) instantiateProxy(pcls, null, null);
_proxies.put(type, proxy); _proxies.put(type, proxy);
} }
return proxy; return proxy;
@ -388,40 +399,62 @@ public class ProxyManagerImpl
*/ */
private ProxyBean getFactoryProxyBean(Object orig) { private ProxyBean getFactoryProxyBean(Object orig) {
// we don't lock here; ok if two proxies get generated for same type // we don't lock here; ok if two proxies get generated for same type
ProxyBean proxy = (ProxyBean) _proxies.get(orig.getClass()); Class type = orig.getClass();
if (proxy == null && !_proxies.containsKey(orig.getClass())) { ProxyBean proxy = (ProxyBean) _proxies.get(type);
BCClass bc = generateProxyBeanBytecode(orig.getClass()); if (proxy == null && !_proxies.containsKey(type)) {
if (bc == null) ClassLoader l = getMostDerivedLoader(type, ProxyBean.class);
_proxies.put(orig.getClass(), null); Class pcls = loadBuildTimeProxy(type, l);
else { if (pcls == null) {
BCMethod m = bc.getDeclaredMethod("<init>", (Class[]) null); BCClass bc = generateProxyBeanBytecode(type, true);
proxy = (ProxyBean) instantiateProxy(bc, orig.getClass(), if (bc != null)
ProxyBean.class, findCopyConstructor(orig.getClass()), pcls = loadProxy(bc, l);
new Object[] {orig});
_proxies.put(orig.getClass(), proxy);
} }
if (pcls != null)
proxy = (ProxyBean) instantiateProxy(pcls,
findCopyConstructor(type), new Object[] {orig});
_proxies.put(type, proxy);
} }
return proxy; return proxy;
} }
/** /**
* Instantiate the given proxy bytecode. * Load the proxy class generated at build time for the given type,
* returning null if none exists.
*/ */
private Proxy instantiateProxy(BCClass bc, Class type, Class proxy, private static Class loadBuildTimeProxy(Class type, ClassLoader loader) {
Constructor cons, Object[] args) { try {
BCClassLoader loader = new BCClassLoader(bc.getProject(), return Class.forName(getProxyClassName(type, false), true, loader);
getMostDerivedClassLoader(type, proxy)); } catch (Throwable t) {
return null;
}
}
/**
* Load the proxy class represented by the given bytecode.
*/
private Class loadProxy(BCClass bc, ClassLoader loader) {
BCClassLoader bcloader = new BCClassLoader(bc.getProject(), loader);
try {
return Class.forName(bc.getName(), true, bcloader);
} catch (Throwable t) {
throw new GeneralException(bc.getName()).setCause(t);
}
}
/**
* Instantiate the given proxy class.
*/
private Proxy instantiateProxy(Class cls, Constructor cons, Object[] args) {
try { try {
Class cls = Class.forName(bc.getName(), true, loader);
if (cons != null) if (cons != null)
return (Proxy) cls.getConstructor(cons.getParameterTypes()). return (Proxy) cls.getConstructor(cons.getParameterTypes()).
newInstance(args); newInstance(args);
return (Proxy) cls.newInstance(); return (Proxy) cls.newInstance();
} catch (InstantiationException ie) { } catch (InstantiationException ie) {
throw new UnsupportedException(_loc.get("cant-classforname", throw new UnsupportedException(_loc.get("cant-newinstance",
bc.getSuperclassName())); cls.getSuperclass().getName()));
} catch (Throwable t) { } catch (Throwable t) {
throw new GeneralException(t); throw new GeneralException(cls.getName()).setCause(t);
} }
} }
@ -429,7 +462,7 @@ public class ProxyManagerImpl
* Return the more derived loader of the class laoders for the given * Return the more derived loader of the class laoders for the given
* classes. * classes.
*/ */
private static ClassLoader getMostDerivedClassLoader(Class c1, Class c2) { private static ClassLoader getMostDerivedLoader(Class c1, Class c2) {
ClassLoader l1 = c1.getClassLoader(); ClassLoader l1 = c1.getClassLoader();
ClassLoader l2 = c2.getClassLoader(); ClassLoader l2 = c2.getClassLoader();
if (l1 == l2) if (l1 == l2)
@ -448,12 +481,11 @@ public class ProxyManagerImpl
/** /**
* Generate the bytecode for a collection proxy for the given type. * Generate the bytecode for a collection proxy for the given type.
*/ */
protected BCClass generateProxyCollectionBytecode(Class type) { protected BCClass generateProxyCollectionBytecode(Class type,
boolean runtime) {
assertNotFinal(type); assertNotFinal(type);
Project project = new Project(); Project project = new Project();
BCClass bc = project.loadClass(Strings.getPackageName BCClass bc = project.loadClass(getProxyClassName(type, runtime));
(ProxyManagerImpl.class) + "." + type.getName().replace('.', '$')
+ "$" + nextProxyId() + PROXY_SUFFIX);
bc.setSuperclass(type); bc.setSuperclass(type);
bc.declareInterface(ProxyCollection.class); bc.declareInterface(ProxyCollection.class);
@ -463,10 +495,19 @@ public class ProxyManagerImpl
proxyRecognizedMethods(bc, type, ProxyCollections.class, proxyRecognizedMethods(bc, type, ProxyCollections.class,
ProxyCollection.class); ProxyCollection.class);
proxySetters(bc, type); proxySetters(bc, type);
addWriteReplaceMethod(bc); addWriteReplaceMethod(bc, runtime);
return bc; return bc;
} }
/**
* Return the name of the proxy class to generate for the given type.
*/
private static String getProxyClassName(Class type, boolean runtime) {
String id = (runtime) ? "$" + nextProxyId() : "";
return Strings.getPackageName(ProxyManagerImpl.class) + "."
+ type.getName().replace('.', '$') + id + PROXY_SUFFIX;
}
/** /**
* Throw appropriate exception if the given type is final. * Throw appropriate exception if the given type is final.
*/ */
@ -478,12 +519,10 @@ public class ProxyManagerImpl
/** /**
* Generate the bytecode for a map proxy for the given type. * Generate the bytecode for a map proxy for the given type.
*/ */
protected BCClass generateProxyMapBytecode(Class type) { protected BCClass generateProxyMapBytecode(Class type, boolean runtime) {
assertNotFinal(type); assertNotFinal(type);
Project project = new Project(); Project project = new Project();
BCClass bc = project.loadClass(Strings.getPackageName BCClass bc = project.loadClass(getProxyClassName(type, runtime));
(ProxyManagerImpl.class) + "." + type.getName().replace('.', '$')
+ "$" + nextProxyId() + PROXY_SUFFIX);
bc.setSuperclass(type); bc.setSuperclass(type);
bc.declareInterface(ProxyMap.class); bc.declareInterface(ProxyMap.class);
@ -492,19 +531,17 @@ public class ProxyManagerImpl
addProxyMapMethods(bc, type); addProxyMapMethods(bc, type);
proxyRecognizedMethods(bc, type, ProxyMaps.class, ProxyMap.class); proxyRecognizedMethods(bc, type, ProxyMaps.class, ProxyMap.class);
proxySetters(bc, type); proxySetters(bc, type);
addWriteReplaceMethod(bc); addWriteReplaceMethod(bc, runtime);
return bc; return bc;
} }
/** /**
* Generate the bytecode for a date proxy for the given type. * Generate the bytecode for a date proxy for the given type.
*/ */
protected BCClass generateProxyDateBytecode(Class type) { protected BCClass generateProxyDateBytecode(Class type, boolean runtime) {
assertNotFinal(type); assertNotFinal(type);
Project project = new Project(); Project project = new Project();
BCClass bc = project.loadClass(Strings.getPackageName BCClass bc = project.loadClass(getProxyClassName(type, runtime));
(ProxyManagerImpl.class) + "." + type.getName().replace('.', '$')
+ "$" + nextProxyId() + PROXY_SUFFIX);
bc.setSuperclass(type); bc.setSuperclass(type);
bc.declareInterface(ProxyDate.class); bc.declareInterface(ProxyDate.class);
@ -512,19 +549,18 @@ public class ProxyManagerImpl
addProxyMethods(bc, type, true); addProxyMethods(bc, type, true);
addProxyDateMethods(bc, type); addProxyDateMethods(bc, type);
proxySetters(bc, type); proxySetters(bc, type);
addWriteReplaceMethod(bc); addWriteReplaceMethod(bc, runtime);
return bc; return bc;
} }
/** /**
* Generate the bytecode for a calendar proxy for the given type. * Generate the bytecode for a calendar proxy for the given type.
*/ */
protected BCClass generateProxyCalendarBytecode(Class type) { protected BCClass generateProxyCalendarBytecode(Class type,
boolean runtime) {
assertNotFinal(type); assertNotFinal(type);
Project project = new Project(); Project project = new Project();
BCClass bc = project.loadClass(Strings.getPackageName BCClass bc = project.loadClass(getProxyClassName(type, runtime));
(ProxyManagerImpl.class) + "." + type.getName().replace('.', '$')
+ "$" + nextProxyId() + PROXY_SUFFIX);
bc.setSuperclass(type); bc.setSuperclass(type);
bc.declareInterface(ProxyCalendar.class); bc.declareInterface(ProxyCalendar.class);
@ -532,14 +568,14 @@ public class ProxyManagerImpl
addProxyMethods(bc, type, true); addProxyMethods(bc, type, true);
addProxyCalendarMethods(bc, type); addProxyCalendarMethods(bc, type);
proxySetters(bc, type); proxySetters(bc, type);
addWriteReplaceMethod(bc); addWriteReplaceMethod(bc, runtime);
return bc; return bc;
} }
/** /**
* Generate the bytecode for a bean proxy for the given type. * Generate the bytecode for a bean proxy for the given type.
*/ */
protected BCClass generateProxyBeanBytecode(Class type) { protected BCClass generateProxyBeanBytecode(Class type, boolean runtime) {
if (Modifier.isFinal(type.getModifiers())) if (Modifier.isFinal(type.getModifiers()))
return null; return null;
if (ImplHelper.isManagedType(type)) if (ImplHelper.isManagedType(type))
@ -558,9 +594,7 @@ public class ProxyManagerImpl
} }
Project project = new Project(); Project project = new Project();
BCClass bc = project.loadClass(Strings.getPackageName BCClass bc = project.loadClass(getProxyClassName(type, runtime));
(ProxyManagerImpl.class) + "." + type.getName().replace('.', '$')
+ "$" + nextProxyId() + PROXY_SUFFIX);
bc.setSuperclass(type); bc.setSuperclass(type);
bc.declareInterface(ProxyBean.class); bc.declareInterface(ProxyBean.class);
@ -569,7 +603,7 @@ public class ProxyManagerImpl
addProxyBeanMethods(bc, type, cons); addProxyBeanMethods(bc, type, cons);
if (!proxySetters(bc, type)) if (!proxySetters(bc, type))
return null; return null;
addWriteReplaceMethod(bc); addWriteReplaceMethod(bc, runtime);
return bc; return bc;
} }
@ -1409,16 +1443,17 @@ public class ProxyManagerImpl
/** /**
* Add a writeReplace implementation that serializes to a non-proxy type * Add a writeReplace implementation that serializes to a non-proxy type
* unless detached. * unless detached and this is a build-time generated class.
*/ */
private void addWriteReplaceMethod(BCClass bc) { private void addWriteReplaceMethod(BCClass bc, boolean runtime) {
BCMethod m = bc.declareMethod("writeReplace", Object.class, null); BCMethod m = bc.declareMethod("writeReplace", Object.class, null);
m.makeProtected(); m.makeProtected();
m.getExceptions(true).addException(ObjectStreamException.class); m.getExceptions(true).addException(ObjectStreamException.class);
Code code = m.getCode(true); Code code = m.getCode(true);
code.aload().setThis(); code.aload().setThis();
code.constant().setValue(!runtime);
code.invokestatic().setMethod(Proxies.class, "writeReplace", code.invokestatic().setMethod(Proxies.class, "writeReplace",
Object.class, new Class[] { Proxy.class }); Object.class, new Class[] { Proxy.class, boolean.class });
code.areturn(); code.areturn();
code.calculateMaxLocals(); code.calculateMaxLocals();
code.calculateMaxStack(); code.calculateMaxStack();
@ -1474,13 +1509,20 @@ public class ProxyManagerImpl
} }
/** /**
* Usage: java org.apache.openjpa.util.proxy.ProxyManagerImpl * Usage: java org.apache.openjpa.util.proxy.ProxyManagerImpl [option]*
* &lt;class name&gt;+ * &lt;class name&gt;+<br />
* Where the following options are recognized:
* <ul>
* <li><i>-utils/-u &lt;number&gt;</i>: Generate proxies for the standard
* java.util collection, map, date, and calendar classes of the given Java
* version. Use 4 for Java 1.4, 5 for Java 5, etc.</li>
* </ul>
* *
* The main method generates .class files for the proxies to the classes * The main method generates .class files for the proxies to the classes
* given on the command line. The .class files are placed in the same * given on the command line. It writes the generated classes to beside the
* package as this class, or the current directory if the directory for this * ProxyManagerImpl.class file if possible; otherwise it writes to the
* class cannot be accessed. * current directory. The proxy manager looks for these classes
* before generating its own proxies at runtime.
*/ */
public static void main(String[] args) public static void main(String[] args)
throws ClassNotFoundException, IOException { throws ClassNotFoundException, IOException {
@ -1488,21 +1530,59 @@ public class ProxyManagerImpl
dir = (dir == null) ? new File(System.getProperty("user.dir")) dir = (dir == null) ? new File(System.getProperty("user.dir"))
: dir.getParentFile(); : dir.getParentFile();
Options opts = new Options();
args = opts.setFromCmdLine(args);
List types = new ArrayList();
types.addAll(Arrays.asList(args));
int utils = opts.removeIntProperty("utils", "u", 0);
if (utils >= 4) {
types.addAll(Arrays.asList(new String[] {
java.sql.Date.class.getName(),
java.sql.Time.class.getName(),
java.sql.Timestamp.class.getName(),
java.util.ArrayList.class.getName(),
java.util.Date.class.getName(),
java.util.GregorianCalendar.class.getName(),
java.util.HashMap.class.getName(),
java.util.HashSet.class.getName(),
java.util.Hashtable.class.getName(),
java.util.LinkedList.class.getName(),
java.util.Properties.class.getName(),
java.util.TreeMap.class.getName(),
java.util.TreeSet.class.getName(),
java.util.Vector.class.getName(),
}));
}
if (utils >= 5) {
types.addAll(Arrays.asList(new String[] {
"java.util.EnumMap",
"java.util.IdentityHashMap",
"java.util.LinkedHashMap",
"java.util.LinkedHashSet",
"java.util.PriorityQueue",
}));
}
ProxyManagerImpl mgr = new ProxyManagerImpl(); ProxyManagerImpl mgr = new ProxyManagerImpl();
Class cls; Class cls;
BCClass bc; BCClass bc;
for (int i = 0; i < args.length; i++) { for (int i = 0; i < types.size(); i++) {
cls = Class.forName(args[i]); cls = Class.forName((String) types.get(i));
if (loadBuildTimeProxy(cls, getMostDerivedLoader(cls, Proxy.class))
!= null)
continue;
if (Collection.class.isAssignableFrom(cls)) if (Collection.class.isAssignableFrom(cls))
bc = mgr.generateProxyCollectionBytecode(cls); bc = mgr.generateProxyCollectionBytecode(cls, false);
else if (Map.class.isAssignableFrom(cls)) else if (Map.class.isAssignableFrom(cls))
bc = mgr.generateProxyMapBytecode(cls); bc = mgr.generateProxyMapBytecode(cls, false);
else if (Date.class.isAssignableFrom(cls)) else if (Date.class.isAssignableFrom(cls))
bc = mgr.generateProxyDateBytecode(cls); bc = mgr.generateProxyDateBytecode(cls, false);
else if (Calendar.class.isAssignableFrom(cls)) else if (Calendar.class.isAssignableFrom(cls))
bc = mgr.generateProxyCalendarBytecode(cls); bc = mgr.generateProxyCalendarBytecode(cls, false);
else else
bc = mgr.generateProxyBeanBytecode(cls); bc = mgr.generateProxyBeanBytecode(cls, false);
System.out.println(bc.getName()); System.out.println(bc.getName());
bc.write(new File(dir, bc.getClassName() + ".class")); bc.write(new File(dir, bc.getClassName() + ".class"));

View File

@ -49,7 +49,7 @@ no-proxy-intf: Unable to create a second class object proxy for interface \
"{0}". No corresponding concrete types are known. "{0}". No corresponding concrete types are known.
no-proxy-abstract: Unable to create a second class object proxy for abstract \ no-proxy-abstract: Unable to create a second class object proxy for abstract \
type "{0}". You must use a concrete type or a recognized interface. type "{0}". You must use a concrete type or a recognized interface.
cant-classforname: Unable to instantiate proxy for type "{0}". Make sure the \ cant-newinstance: Unable to instantiate proxy for type "{0}". Make sure the \
class has a default constructor. class has a default constructor.
no-date-cons: Custom date type "{0}" needs a default constructor or a \ no-date-cons: Custom date type "{0}" needs a default constructor or a \
millisecond constructor. millisecond constructor.