Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.openjpa.util;
import java.io.File;
import java.io.IOException;
import java.io.ObjectStreamException;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TimeZone;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.openjpa.kernel.OpenJPAStateManager;
import org.apache.openjpa.lib.util.ClassUtil;
import org.apache.openjpa.lib.util.Files;
import org.apache.openjpa.lib.util.J2DoPrivHelper;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.lib.util.Options;
import org.apache.openjpa.lib.util.StringUtil;
import org.apache.openjpa.util.asm.AsmHelper;
import org.apache.openjpa.util.asm.ClassWriterTracker;
import org.apache.openjpa.util.proxy.DelayedArrayListProxy;
import org.apache.openjpa.util.proxy.DelayedHashSetProxy;
import org.apache.openjpa.util.proxy.DelayedLinkedHashSetProxy;
import org.apache.openjpa.util.proxy.DelayedLinkedListProxy;
import org.apache.openjpa.util.proxy.DelayedPriorityQueueProxy;
import org.apache.openjpa.util.proxy.DelayedTreeSetProxy;
import org.apache.openjpa.util.proxy.DelayedVectorProxy;
import org.apache.openjpa.util.proxy.ProxyBean;
import org.apache.openjpa.util.proxy.ProxyCalendar;
import org.apache.openjpa.util.proxy.ProxyCollection;
import org.apache.openjpa.util.proxy.ProxyCollections;
import org.apache.openjpa.util.proxy.ProxyDate;
import org.apache.openjpa.util.proxy.ProxyMap;
import org.apache.openjpa.util.proxy.ProxyMaps;
import org.apache.xbean.asm9.ClassWriter;
import org.apache.xbean.asm9.Label;
import org.apache.xbean.asm9.MethodVisitor;
import org.apache.xbean.asm9.Opcodes;
import org.apache.xbean.asm9.Type;
/**
* Default implementation of the {@link ProxyManager} interface.
*
* @author Abe White
* @author Mark Struberg
*/
public class ProxyManagerImpl
implements ProxyManager {
private static final String PROXY_SUFFIX = "$proxy";
private static final Localizer _loc = Localizer.forPackage
(ProxyManagerImpl.class);
public static final Type TYPE_OBJECT = Type.getType(Object.class);
private static long _proxyId = 0L;
private static final Map _stdCollections = new HashMap();
private static final Map _stdMaps = new HashMap();
static {
_stdCollections.put(Collection.class, ArrayList.class);
_stdCollections.put(Set.class, HashSet.class);
_stdCollections.put(SortedSet.class, TreeSet.class);
_stdCollections.put(List.class, ArrayList.class);
_stdCollections.put(Queue.class, LinkedList.class);
_stdMaps.put(Map.class, HashMap.class);
_stdMaps.put(SortedMap.class, TreeMap.class);
}
private final Set _unproxyable = new HashSet<>();
private final Map, Proxy> _proxies = new ConcurrentHashMap<>();
private boolean _trackChanges = true;
private boolean _assertType = false;
private boolean _delayedCollectionLoading = false;
public ProxyManagerImpl() {
_unproxyable.add(TimeZone.class.getName());
}
/**
* Whether proxies produced by this factory will use {@link ChangeTracker}s
* to try to cut down on data store operations at the cost of some extra
* bookkeeping overhead. Defaults to true.
*/
public boolean getTrackChanges() {
return _trackChanges;
}
/**
* Whether proxies produced by this factory will use {@link ChangeTracker}s
* to try to cut down on data store operations at the cost of some extra
* bookkeeping overhead. Defaults to true.
*/
public void setTrackChanges(boolean track) {
_trackChanges = track;
}
/**
* Whether to perform runtime checks to ensure that all elements
* added to collection and map proxies are the proper element/key/value
* type as defined by the metadata. Defaults to false.
*/
public boolean getAssertAllowedType() {
return _assertType;
}
/**
* Whether to perform runtime checks to ensure that all elements
* added to collection and map proxies are the proper element/key/value
* type as defined by the metadata. Defaults to false.
*/
public void setAssertAllowedType(boolean assertType) {
_assertType = assertType;
}
/**
* Whether loading of collections should be delayed until an operation
* is performed that requires them to be loaded. This property only
* applies to proxies that implement java.util.Collection (ie. not arrays
* or maps). Defaults to false.
*/
@Override
public boolean getDelayCollectionLoading() {
return _delayedCollectionLoading;
}
/**
* Whether loading of collections should be delayed until an operation
* is performed that requires them to be loaded. Defaults to false.
*/
public void setDelayCollectionLoading(boolean delay) {
_delayedCollectionLoading = delay;
}
/**
* Return a mutable view of class names we know cannot be proxied
* correctly by this manager.
*/
public Collection getUnproxyable() {
return _unproxyable;
}
/**
* Provided for auto-configuration. Add the given semicolon-separated
* class names to the set of class names we know cannot be proxied correctly
* by this manager.
*/
public void setUnproxyable(String clsNames) {
if (clsNames != null)
_unproxyable.addAll(Arrays.asList(StringUtil.split(clsNames, ";", 0)));
}
@Override
public Object copyArray(Object orig) {
if (orig == null)
return null;
try {
int length = Array.getLength(orig);
Object array = Array.newInstance(orig.getClass().
getComponentType(), length);
System.arraycopy(orig, 0, array, 0, length);
return array;
} catch (Exception e) {
throw new UnsupportedException(_loc.get("bad-array",
e.getMessage()), e);
}
}
@Override
public Collection copyCollection(Collection orig) {
if (orig == null)
return null;
if (orig instanceof Proxy)
return (Collection) ((Proxy) orig).copy(orig);
ProxyCollection proxy = getFactoryProxyCollection(orig.getClass());
return (Collection) proxy.copy(orig);
}
@Override
public Proxy newCollectionProxy(Class type, Class elementType,
Comparator compare, boolean autoOff) {
type = toProxyableCollectionType(type);
ProxyCollection proxy = getFactoryProxyCollection(type);
return proxy.newInstance((_assertType) ? elementType : null, compare,
_trackChanges, autoOff);
}
@Override
public Map copyMap(Map orig) {
if (orig == null)
return null;
if (orig instanceof Proxy)
return (Map) ((Proxy) orig).copy(orig);
ProxyMap proxy = getFactoryProxyMap(orig.getClass());
return (Map) proxy.copy(orig);
}
@Override
public Proxy newMapProxy(Class type, Class keyType,
Class elementType, Comparator compare,boolean autoOff) {
type = toProxyableMapType(type);
ProxyMap proxy = getFactoryProxyMap(type);
return proxy.newInstance((_assertType) ? keyType : null,
(_assertType) ? elementType : null, compare, _trackChanges, autoOff);
}
@Override
public Date copyDate(Date orig) {
if (orig == null)
return null;
if (orig instanceof Proxy)
return (Date) ((Proxy) orig).copy(orig);
ProxyDate proxy = getFactoryProxyDate(orig.getClass());
return (Date) proxy.copy(orig);
}
@Override
public Proxy newDateProxy(Class type) {
ProxyDate proxy = getFactoryProxyDate(type);
return proxy.newInstance();
}
@Override
public Calendar copyCalendar(Calendar orig) {
if (orig == null)
return null;
if (orig instanceof Proxy)
return (Calendar) ((Proxy) orig).copy(orig);
ProxyCalendar proxy = getFactoryProxyCalendar(orig.getClass());
return (Calendar) proxy.copy(orig);
}
@Override
public Proxy newCalendarProxy(Class type, TimeZone zone) {
if (type == Calendar.class)
type = GregorianCalendar.class;
ProxyCalendar proxy = getFactoryProxyCalendar(type);
ProxyCalendar cal = proxy.newInstance();
if (zone != null)
((Calendar) cal).setTimeZone(zone);
return cal;
}
@Override
public Object copyCustom(Object orig) {
if (orig == null)
return null;
if (orig instanceof Proxy)
return ((Proxy) orig).copy(orig);
if (ImplHelper.isManageable(orig))
return null;
if (orig instanceof Collection)
return copyCollection((Collection) orig);
if (orig instanceof Map)
return copyMap((Map) orig);
if (orig instanceof Date)
return copyDate((Date) orig);
if (orig instanceof Calendar)
return copyCalendar((Calendar) orig);
ProxyBean proxy = getFactoryProxyBean(orig);
return (proxy == null) ? null : proxy.copy(orig);
}
@Override
public Proxy newCustomProxy(Object orig, boolean autoOff) {
if (orig == null)
return null;
if (orig instanceof Proxy)
return (Proxy) orig;
if (ImplHelper.isManageable(orig))
return null;
if (!isProxyable(orig.getClass()))
return null;
if (orig instanceof Collection) {
Comparator comp = (orig instanceof SortedSet)
? ((SortedSet) orig).comparator() : null;
Collection c = (Collection) newCollectionProxy(orig.getClass(),
null, comp, autoOff);
c.addAll((Collection) orig);
return (Proxy) c;
}
if (orig instanceof Map) {
Comparator comp = (orig instanceof SortedMap)
? ((SortedMap) orig).comparator() : null;
Map m = (Map) newMapProxy(orig.getClass(), null, null, comp, autoOff);
m.putAll((Map) orig);
return (Proxy) m;
}
if (orig instanceof Date) {
Date d = (Date) newDateProxy(orig.getClass());
d.setTime(((Date) orig).getTime());
if (orig instanceof Timestamp)
((Timestamp) d).setNanos(((Timestamp) orig).getNanos());
return (Proxy) d;
}
if (orig instanceof Calendar) {
Calendar c = (Calendar) newCalendarProxy(orig.getClass(),
((Calendar) orig).getTimeZone());
c.setTimeInMillis(((Calendar) orig).getTimeInMillis());
return (Proxy) c;
}
ProxyBean proxy = getFactoryProxyBean(orig);
return (proxy == null) ? null : proxy.newInstance(orig);
}
/**
* Return the concrete type for proxying.
*/
protected Class toProxyableCollectionType(Class type) {
if (type.getName().endsWith(PROXY_SUFFIX))
type = type.getSuperclass();
else if (type.isInterface()) {
type = toConcreteType(type, _stdCollections);
if (type == null)
throw new UnsupportedException(_loc.get("no-proxy-intf", type));
} else if (Modifier.isAbstract(type.getModifiers()))
throw new UnsupportedException(_loc.get("no-proxy-abstract", type));
return type;
}
/**
* Return the concrete type for proxying.
*/
protected Class toProxyableMapType(Class type) {
if (type.getName().endsWith(PROXY_SUFFIX))
type = type.getSuperclass();
else if (type.isInterface()) {
type = toConcreteType(type, _stdMaps);
if (type == null)
throw new UnsupportedException(_loc.get("no-proxy-intf", type));
} else if (Modifier.isAbstract(type.getModifiers()))
throw new UnsupportedException(_loc.get("no-proxy-abstract", type));
return type;
}
/**
* Locate a concrete type to proxy for the given collection interface.
*/
private static Class toConcreteType(Class intf, Map concretes) {
Class concrete = (Class) concretes.get(intf);
if (concrete != null)
return concrete;
Class[] intfs = intf.getInterfaces();
for (Class aClass : intfs) {
concrete = toConcreteType(aClass, concretes);
if (concrete != null)
return concrete;
}
return null;
}
/**
* Return the cached factory proxy for the given map type.
*/
private ProxyMap getFactoryProxyMap(Class type) {
// we don't lock here; ok if two proxies get generated for same type
ProxyMap proxy = (ProxyMap) _proxies.get(type);
if (proxy == null) {
ClassLoader l = GeneratedClasses.getMostDerivedLoader(type,
ProxyMap.class);
Class pcls = loadBuildTimeProxy(type, l);
if (pcls == null)
pcls = generateAndLoadProxyMap(type, true, l);
proxy = (ProxyMap) instantiateProxy(pcls, null, null);
_proxies.put(type, proxy);
}
return proxy;
}
/**
* Return the cached factory proxy for the given date type.
*/
private ProxyDate getFactoryProxyDate(Class type) {
// we don't lock here; ok if two proxies get generated for same type
ProxyDate proxy = (ProxyDate) _proxies.get(type);
if (proxy == null) {
ClassLoader l = GeneratedClasses.getMostDerivedLoader(type,
ProxyDate.class);
Class pcls = loadBuildTimeProxy(type, l);
if (pcls == null)
pcls = generateAndLoadProxyDate(type, true, l);
proxy = (ProxyDate) instantiateProxy(pcls, null, null);
_proxies.put(type, proxy);
}
return proxy;
}
/**
* Return the cached factory proxy for the given calendar type.
*/
private ProxyCalendar getFactoryProxyCalendar(Class type) {
// we don't lock here; ok if two proxies get generated for same type
ProxyCalendar proxy = (ProxyCalendar) _proxies.get(type);
if (proxy == null) {
ClassLoader l = GeneratedClasses.getMostDerivedLoader(type,
ProxyCalendar.class);
Class pcls = loadBuildTimeProxy(type, l);
if (pcls == null)
pcls = generateAndLoadProxyCalendar(type, true, l);
proxy = (ProxyCalendar) instantiateProxy(pcls, null, null);
_proxies.put(type, proxy);
}
return proxy;
}
/**
* Return the cached factory proxy for the given collection type.
*/
private ProxyCollection getFactoryProxyCollection(Class type) {
// we don't lock here; ok if two proxies get generated for same type
ProxyCollection proxy = (ProxyCollection) _proxies.get(type);
if (proxy == null) {
ClassLoader l = GeneratedClasses.getMostDerivedLoader(type,
ProxyCollection.class);
Class pcls = loadBuildTimeProxy(type, l);
if (pcls == null)
pcls = generateAndLoadProxyCollection(type, true, l);
proxy = (ProxyCollection) instantiateProxy(pcls, null, null);
_proxies.put(type, proxy);
}
return proxy;
}
/**
* Return the cached factory proxy for the given bean type.
*/
private ProxyBean getFactoryProxyBean(Object orig) {
final Class> type = orig.getClass();
if (isUnproxyable(type))
return null;
// we don't lock here; ok if two proxies get generated for same type
ProxyBean proxy = (ProxyBean) _proxies.get(type);
if (proxy == null) {
ClassLoader l = GeneratedClasses.getMostDerivedLoader(type, ProxyBean.class);
Class> pcls = loadBuildTimeProxy(type, l);
if (pcls == null) {
pcls = generateAndLoadProxyBean(type, true, l);
}
if (pcls != null)
proxy = (ProxyBean) instantiateProxy(pcls, findCopyConstructor(type), new Object[] { orig });
if (proxy == null) {
_unproxyable.add(type.getName());
} else {
_proxies.put(type, proxy);
}
}
return proxy;
}
/**
* Return whether the given type is known to be unproxyable.
*/
protected boolean isUnproxyable(Class type) {
for (; type != null && type != Object.class;
type = type.getSuperclass()) {
if (_unproxyable.contains(type.getName()))
return true;
}
return false;
}
/**
* Load the proxy class generated at build time for the given type,
* returning null if none exists.
*/
protected Class loadBuildTimeProxy(Class type, ClassLoader loader) {
try {
Class> proxyClass = null;
if (_delayedCollectionLoading) {
proxyClass = loadDelayedProxy(type);
if (proxyClass != null) {
return proxyClass;
}
}
return Class.forName(getProxyClassName(type, false), true, loader);
} catch (Throwable t) {
return null;
}
}
protected Class> loadDelayedProxy(Class> type) {
if (type.equals(java.util.ArrayList.class)) {
return DelayedArrayListProxy.class;
}
if (type.equals(java.util.HashSet.class)) {
return DelayedHashSetProxy.class;
}
if (type.equals(java.util.LinkedList.class)) {
return DelayedLinkedListProxy.class;
}
if (type.equals(java.util.Vector.class)) {
return DelayedVectorProxy.class;
}
if (type.equals(java.util.LinkedHashSet.class)) {
return DelayedLinkedHashSetProxy.class;
}
if (type.equals(java.util.SortedSet.class) || type.equals(java.util.TreeSet.class)) {
return DelayedTreeSetProxy.class;
}
if (type.equals(java.util.PriorityQueue.class)) {
return DelayedPriorityQueueProxy.class;
}
return null;
}
/**
* Instantiate the given proxy class.
*/
private Proxy instantiateProxy(Class cls, Constructor cons, Object[] args) {
try {
if (cons != null)
return (Proxy) cls.getConstructor(cons.getParameterTypes()).
newInstance(args);
return (Proxy) AccessController.doPrivileged(
J2DoPrivHelper.newInstanceAction(cls));
} catch (InstantiationException ie) {
throw new UnsupportedException(_loc.get("cant-newinstance",
cls.getSuperclass().getName()));
} catch (PrivilegedActionException pae) {
Exception e = pae.getException();
if (e instanceof InstantiationException)
throw new UnsupportedException(_loc.get("cant-newinstance",
cls.getSuperclass().getName()));
else
throw new GeneralException(cls.getName()).setCause(e);
} catch (Throwable t) {
throw new GeneralException(cls.getName()).setCause(t);
}
}
/**
* Return the name of the proxy class to generate for the given type.
*/
protected static String getProxyClassName(Class type, boolean runtime) {
String id = (runtime) ? "$" + nextProxyId() : "";
return ClassUtil.getPackageName(ProxyManagerImpl.class) + "."
+ type.getName().replace('.', '$') + id + PROXY_SUFFIX;
}
/**
* Throw appropriate exception if the given type is final.
*/
private static void assertNotFinal(Class type) {
if (Modifier.isFinal(type.getModifiers()))
throw new UnsupportedException(_loc.get("no-proxy-final", type));
}
private static boolean isProxyable(Class> cls){
int mod = cls.getModifiers();
if(Modifier.isFinal(mod)) {
return false;
}
if(Modifier.isProtected(mod) || Modifier.isPublic(mod)) {
return true;
}
// Default scoped class, we can only extend if it is in the same package as the generated proxy. Ideally
// we'd fix the code gen portion and place proxies in the same pacakge as the types being proxied.
if(cls.getPackage().getName().equals("org.apache.openjpa.util")) {
return true;
}
return false;
}
private Class generateAndLoadProxyDate(Class type, boolean runtime, ClassLoader l) {
final String proxyClassName = getProxyClassName(type, runtime);
final byte[] classBytes = generateProxyDateBytecode(type, runtime, proxyClassName);
if (classBytes == null) {
return null;
}
return GeneratedClasses.loadAsmClass(proxyClassName, classBytes, ProxyDate.class, l);
}
private Class generateAndLoadProxyCalendar(Class type, boolean runtime, ClassLoader l) {
final String proxyClassName = getProxyClassName(type, runtime);
final byte[] classBytes = generateProxyCalendarBytecode(type, runtime, proxyClassName);
if (classBytes == null) {
return null;
}
return GeneratedClasses.loadAsmClass(proxyClassName, classBytes, ProxyDate.class, l);
}
private Class generateAndLoadProxyCollection(Class type, boolean runtime, ClassLoader l) {
final String proxyClassName = getProxyClassName(type, runtime);
final byte[] classBytes = generateProxyCollectionBytecode(type, runtime, proxyClassName);
if (classBytes == null) {
return null;
}
return GeneratedClasses.loadAsmClass(proxyClassName, classBytes, ProxyCollection.class, l);
}
private Class generateAndLoadProxyMap(Class type, boolean runtime, ClassLoader l) {
final String proxyClassName = getProxyClassName(type, runtime);
final byte[] classBytes = generateProxyMapBytecode(type, runtime, proxyClassName);
if (classBytes == null) {
return null;
}
return GeneratedClasses.loadAsmClass(proxyClassName, classBytes, ProxyMap.class, l);
}
private Class generateAndLoadProxyBean(Class type, boolean runtime, ClassLoader l) {
final String proxyClassName = getProxyClassName(type, runtime);
final byte[] classBytes = generateProxyBeanBytecode(type, runtime, proxyClassName);
if (classBytes == null) {
return null;
}
return GeneratedClasses.loadAsmClass(proxyClassName, classBytes, ProxyBean.class, l);
}
/**
* Generate the bytecode for a date proxy for the given type.
*/
protected byte[] generateProxyDateBytecode(Class type, boolean runtime, String proxyClassName) {
assertNotFinal(type);
String proxyClassDef = proxyClassName.replace('.', '/');
String superClassFileNname = Type.getInternalName(type);
String[] interfaceNames = new String[]{Type.getInternalName(ProxyDate.class)};
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
cw.visit(Opcodes.V11, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, proxyClassDef,
null, superClassFileNname, interfaceNames);
ClassWriterTracker ct = new ClassWriterTracker(cw);
String classFileName = runtime ? type.getName() : proxyClassDef;
cw.visitSource(classFileName + ".java", null);
delegateConstructors(ct, type, superClassFileNname);
addInstanceVariables(ct);
addProxyMethods(ct, true, proxyClassDef, type);
addProxyDateMethods(ct, proxyClassDef, type);
proxySetters(ct, proxyClassDef, type);
addWriteReplaceMethod(ct, proxyClassDef, runtime);
return cw.toByteArray();
}
/**
* Generate the bytecode for a calendar proxy for the given type.
*/
protected byte[] generateProxyCalendarBytecode(Class type, boolean runtime, String proxyClassName) {
assertNotFinal(type);
String proxyClassDef = proxyClassName.replace('.', '/');
String superClassFileNname = Type.getInternalName(type);
String[] interfaceNames = new String[]{Type.getInternalName(ProxyCalendar.class)};
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
cw.visit(Opcodes.V11, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, proxyClassDef,
null, superClassFileNname, interfaceNames);
ClassWriterTracker ct = new ClassWriterTracker(cw);
String classFileName = runtime ? type.getName() : proxyClassDef;
cw.visitSource(classFileName + ".java", null);
delegateConstructors(ct, type, superClassFileNname);
addInstanceVariables(ct);
addProxyMethods(ct, true, proxyClassDef, type);
addProxyCalendarMethods(ct, proxyClassDef, type);
proxySetters(ct, proxyClassDef, type);
addWriteReplaceMethod(ct, proxyClassDef, runtime);
return cw.toByteArray();
}
/**
* Generate the bytecode for a collection proxy for the given type.
*/
protected byte[] generateProxyCollectionBytecode(Class type, boolean runtime, String proxyClassName) {
assertNotFinal(type);
String proxyClassDef = proxyClassName.replace('.', '/');
String superClassFileNname = Type.getInternalName(type);
String[] interfaceNames = new String[]{Type.getInternalName(ProxyCollection.class)};
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
cw.visit(Opcodes.V11, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, proxyClassDef,
null, superClassFileNname, interfaceNames);
ClassWriterTracker ct = new ClassWriterTracker(cw);
String classFileName = runtime ? type.getName() : proxyClassDef;
cw.visitSource(classFileName + ".java", null);
delegateConstructors(ct, type, superClassFileNname);
addInstanceVariables(ct);
addProxyMethods(ct, false, proxyClassDef, type);
addProxyCollectionMethods(ct, proxyClassDef, type);
proxyRecognizedMethods(ct, proxyClassDef, type, ProxyCollections.class, ProxyCollection.class);
proxySetters(ct, proxyClassDef, type);
addWriteReplaceMethod(ct, proxyClassDef, runtime);
return cw.toByteArray();
}
/**
* Generate the bytecode for a map proxy for the given type.
*/
protected byte[] generateProxyMapBytecode(Class type, boolean runtime, String proxyClassName) {
assertNotFinal(type);
String proxyClassDef = proxyClassName.replace('.', '/');
String superClassFileNname = Type.getInternalName(type);
String[] interfaceNames = new String[]{Type.getInternalName(ProxyMap.class)};
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
cw.visit(Opcodes.V11, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, proxyClassDef,
null, superClassFileNname, interfaceNames);
ClassWriterTracker ct = new ClassWriterTracker(cw);
String classFileName = runtime ? type.getName() : proxyClassDef;
cw.visitSource(classFileName + ".java", null);
delegateConstructors(ct, type, superClassFileNname);
addInstanceVariables(ct);
addProxyMethods(ct, false, proxyClassDef, type);
addProxyMapMethods(ct, proxyClassDef, type);
proxyRecognizedMethods(ct, proxyClassDef, type, ProxyMaps.class, ProxyMap.class);
proxySetters(ct, proxyClassDef, type);
addWriteReplaceMethod(ct, proxyClassDef, runtime);
return cw.toByteArray();
}
/**
* Generate the bytecode for a bean proxy for the given type.
*/
protected byte[] generateProxyBeanBytecode(Class type, boolean runtime, String proxyClassName) {
if (Modifier.isFinal(type.getModifiers())) {
return null;
}
if (ImplHelper.isManagedType(null, type)) {
return null;
}
// we can only generate a valid proxy if there is a copy constructor
// or a default constructor
Constructor cons = findCopyConstructor(type);
if (cons == null) {
Constructor[] cs = type.getConstructors();
for (int i = 0; cons == null && i < cs.length; i++) {
if (cs[i].getParameterTypes().length == 0) {
cons = cs[i];
}
}
if (cons == null)
return null;
}
String proxyClassDef = proxyClassName.replace('.', '/');
String superClassFileNname = Type.getInternalName(type);
String[] interfaceNames = new String[]{Type.getInternalName(ProxyBean.class)};
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
cw.visit(Opcodes.V11, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, proxyClassDef,
null, superClassFileNname, interfaceNames);
ClassWriterTracker ct = new ClassWriterTracker(cw);
String classFileName = runtime ? type.getName() : proxyClassDef;
cw.visitSource(classFileName + ".java", null);
delegateConstructors(ct, type, superClassFileNname);
addInstanceVariables(ct);
addProxyMethods(ct, true, proxyClassDef, type);
addProxyBeanMethods(ct, proxyClassDef, type, cons);
if (!proxySetters(ct, proxyClassDef, type)) {
return null;
}
addWriteReplaceMethod(ct, proxyClassDef, runtime);
return cw.toByteArray();
}
private void addProxyBeanMethods(ClassWriterTracker ct, String proxyClassDef, Class type, Constructor cons) {
// bean copy
{
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "copy",
Type.getMethodDescriptor(TYPE_OBJECT, TYPE_OBJECT)
, null, null);
mv.visitCode();
mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(type));
mv.visitInsn(Opcodes.DUP);
Class[] params = cons.getParameterTypes();
if (params.length == 1) {
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(params[0]));
}
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(type), "",
Type.getMethodDescriptor(Type.VOID_TYPE, AsmHelper.getParamTypes(params)), false);
int beanVarPos = params.length+2; // params+DUP
if (params.length == 0) {
mv.visitVarInsn(Opcodes.ASTORE, beanVarPos);
copyBeanProperties(mv, type, beanVarPos);
mv.visitVarInsn(Opcodes.ALOAD, beanVarPos);
}
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
// new instance factory
{
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "newInstance",
Type.getMethodDescriptor(Type.getType(ProxyBean.class), Type.getType(Object.class))
, null, null);
mv.visitCode();
mv.visitTypeInsn(Opcodes.NEW, proxyClassDef);
mv.visitInsn(Opcodes.DUP);
Class[] params = cons.getParameterTypes();
if (params.length == 1) {
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(params[0]));
}
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, proxyClassDef, "",
Type.getMethodDescriptor(Type.VOID_TYPE, AsmHelper.getParamTypes(params)), false);
int beanVarPos = params.length+2; // params+DUP
if (params.length == 0) {
mv.visitVarInsn(Opcodes.ASTORE, beanVarPos);
copyBeanProperties(mv, type, beanVarPos);
mv.visitVarInsn(Opcodes.ALOAD, beanVarPos);
}
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
}
private void copyBeanProperties(MethodVisitor mv, Class type, int copyVarPos) {
Method[] meths = type.getMethods();
Method getter;
int mods;
for (Method meth : meths) {
mods = meth.getModifiers();
if (!Modifier.isPublic(mods) || Modifier.isStatic(mods)) {
continue;
}
if (!startsWith(meth.getName(), "set") || meth.getParameterTypes().length != 1) {
continue;
}
getter = findGetter(type, meth);
if (getter == null) {
continue;
}
// copy.setXXX(orig.getXXX());
mv.visitVarInsn(Opcodes.ALOAD, copyVarPos);
mv.visitVarInsn(Opcodes.ALOAD, copyVarPos-1);
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(type));
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), getter.getName(),
Type.getMethodDescriptor(getter), false);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), meth.getName(),
Type.getMethodDescriptor(meth), false);
}
}
private void addProxyCollectionMethods(ClassWriterTracker ct, String proxyClassDef, Class type) {
// change tracker
{
ct.getCw().visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_TRANSIENT,
"changeTracker", Type.getDescriptor(CollectionChangeTracker.class), null, null).visitEnd();
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "getChangeTracker",
Type.getMethodDescriptor(Type.getType(ChangeTracker.class))
, null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, proxyClassDef, "changeTracker", Type.getDescriptor(CollectionChangeTracker.class));
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
// collection copy
{
Constructor cons = findCopyConstructor(type);
if (cons == null && SortedSet.class.isAssignableFrom(type)) {
cons = findComparatorConstructor(type);
}
Class[] params = (cons == null) ? new Class[0]
: cons.getParameterTypes();
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "copy",
Type.getMethodDescriptor(TYPE_OBJECT, TYPE_OBJECT)
, null, null);
mv.visitCode();
mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(type));
mv.visitInsn(Opcodes.DUP);
if (params.length == 1) {
mv.visitVarInsn(Opcodes.ALOAD, 1);
if (params[0] == Comparator.class) {
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(SortedSet.class));
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type.getInternalName(SortedSet.class), "comparator",
Type.getMethodDescriptor(Type.getType(Comparator.class)), true);
}
else {
// otherwise just pass the parameter
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(params[0]));
}
}
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(type), "",
Type.getMethodDescriptor(Type.VOID_TYPE, AsmHelper.getParamTypes(params)), false);
if (params.length == 0 || params[0] == Comparator.class) {
mv.visitInsn(Opcodes.DUP);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Collection.class));
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), "addAll",
Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.getType(Collection.class)), false);
mv.visitInsn(Opcodes.POP);
}
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
// element type
{
ct.getCw().visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_TRANSIENT,
"elementType", Type.getDescriptor(Class.class), null, null).visitEnd();
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "getElementType",
Type.getMethodDescriptor(Type.getType(Class.class))
, null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, proxyClassDef, "elementType", Type.getDescriptor(Class.class));
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
// new instance factory
{
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "newInstance",
Type.getMethodDescriptor(Type.getType(ProxyCollection.class),
Type.getType(Class.class), Type.getType(Comparator.class), Type.BOOLEAN_TYPE, Type.BOOLEAN_TYPE)
, null, null);
mv.visitCode();
mv.visitTypeInsn(Opcodes.NEW, proxyClassDef);
mv.visitInsn(Opcodes.DUP);
Constructor cons = findComparatorConstructor(type);
Class[] params = (cons == null) ? new Class[0] : cons.getParameterTypes();
if (params.length == 1) {
mv.visitVarInsn(Opcodes.ALOAD, 2);
}
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, proxyClassDef, "",
Type.getMethodDescriptor(Type.VOID_TYPE, AsmHelper.getParamTypes(params)), false);
mv.visitVarInsn(Opcodes.ASTORE, 5);
mv.visitVarInsn(Opcodes.ALOAD, 5);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitFieldInsn(Opcodes.PUTFIELD, proxyClassDef, "elementType", Type.getDescriptor(Class.class));
mv.visitVarInsn(Opcodes.ILOAD, 3);
Label lNotTrack = new Label();
mv.visitJumpInsn(Opcodes.IFEQ, lNotTrack);
mv.visitVarInsn(Opcodes.ALOAD, 5);
mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(CollectionChangeTrackerImpl.class));
mv.visitInsn(Opcodes.DUP);
mv.visitVarInsn(Opcodes.ALOAD, 5);
mv.visitInsn(allowsDuplicates(type) ? Opcodes.ICONST_1 : Opcodes.ICONST_0);
mv.visitInsn(isOrdered(type) ? Opcodes.ICONST_1 : Opcodes.ICONST_0);
mv.visitVarInsn(Opcodes.ILOAD, 4);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(CollectionChangeTrackerImpl.class), "",
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Collection.class),
Type.BOOLEAN_TYPE, Type.BOOLEAN_TYPE, Type.BOOLEAN_TYPE),
false);
mv.visitFieldInsn(Opcodes.PUTFIELD, proxyClassDef, "changeTracker", Type.getDescriptor(CollectionChangeTracker.class));
mv.visitLabel(lNotTrack);
mv.visitVarInsn(Opcodes.ALOAD, 5);
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
}
private void addProxyMapMethods(ClassWriterTracker ct, String proxyClassDef, Class type) {
// change tracker
{
ct.getCw().visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_TRANSIENT,
"changeTracker", Type.getDescriptor(MapChangeTracker.class), null, null).visitEnd();
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "getChangeTracker",
Type.getMethodDescriptor(Type.getType(ChangeTracker.class))
, null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, proxyClassDef, "changeTracker", Type.getDescriptor(MapChangeTracker.class));
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
// Map copy
{
Constructor cons = findCopyConstructor(type);
if (cons == null && SortedMap.class.isAssignableFrom(type)) {
cons = findComparatorConstructor(type);
}
Class[] params = (cons == null) ? new Class[0]
: cons.getParameterTypes();
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "copy",
Type.getMethodDescriptor(TYPE_OBJECT, TYPE_OBJECT)
, null, null);
mv.visitCode();
mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(type));
mv.visitInsn(Opcodes.DUP);
if (params.length == 1) {
mv.visitVarInsn(Opcodes.ALOAD, 1);
if (params[0] == Comparator.class) {
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(SortedMap.class));
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type.getInternalName(SortedMap.class), "comparator",
Type.getMethodDescriptor(Type.getType(Comparator.class)), true);
}
else {
// otherwise just pass the parameter
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(params[0]));
}
}
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(type), "",
Type.getMethodDescriptor(Type.VOID_TYPE, AsmHelper.getParamTypes(params)), false);
if (params.length == 0 || params[0] == Comparator.class) {
mv.visitInsn(Opcodes.DUP);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Map.class));
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), "putAll",
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Map.class)), false);
}
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
// key type
{
ct.getCw().visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_TRANSIENT,
"keyType", Type.getDescriptor(Class.class), null, null).visitEnd();
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "getKeyType",
Type.getMethodDescriptor(Type.getType(Class.class))
, null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, proxyClassDef, "keyType", Type.getDescriptor(Class.class));
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
// value type
{
ct.getCw().visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_TRANSIENT,
"valueType", Type.getDescriptor(Class.class), null, null).visitEnd();
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "getValueType",
Type.getMethodDescriptor(Type.getType(Class.class))
, null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, proxyClassDef, "valueType", Type.getDescriptor(Class.class));
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
// new instance factory
{
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "newInstance",
Type.getMethodDescriptor(Type.getType(ProxyMap.class),
Type.getType(Class.class), Type.getType(Class.class), Type.getType(Comparator.class),
Type.BOOLEAN_TYPE, Type.BOOLEAN_TYPE)
, null, null);
mv.visitCode();
mv.visitTypeInsn(Opcodes.NEW, proxyClassDef);
mv.visitInsn(Opcodes.DUP);
Constructor cons = findComparatorConstructor(type);
Class[] params = (cons == null) ? new Class[0] : cons.getParameterTypes();
if (params.length == 1) {
mv.visitVarInsn(Opcodes.ALOAD, 3);
}
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, proxyClassDef, "",
Type.getMethodDescriptor(Type.VOID_TYPE, AsmHelper.getParamTypes(params)), false);
mv.visitVarInsn(Opcodes.ASTORE, 6);
mv.visitVarInsn(Opcodes.ALOAD, 6);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitFieldInsn(Opcodes.PUTFIELD, proxyClassDef, "keyType", Type.getDescriptor(Class.class));
mv.visitVarInsn(Opcodes.ALOAD, 6);
mv.visitVarInsn(Opcodes.ALOAD, 2);
mv.visitFieldInsn(Opcodes.PUTFIELD, proxyClassDef, "valueType", Type.getDescriptor(Class.class));
mv.visitVarInsn(Opcodes.ILOAD, 4);
Label lNotTrack = new Label();
mv.visitJumpInsn(Opcodes.IFEQ, lNotTrack);
mv.visitVarInsn(Opcodes.ALOAD, 6);
mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(MapChangeTrackerImpl.class));
mv.visitInsn(Opcodes.DUP);
mv.visitVarInsn(Opcodes.ALOAD, 6);
mv.visitVarInsn(Opcodes.ILOAD, 5);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(MapChangeTrackerImpl.class), "",
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Map.class), Type.BOOLEAN_TYPE),
false);
mv.visitFieldInsn(Opcodes.PUTFIELD, proxyClassDef, "changeTracker", Type.getDescriptor(MapChangeTracker.class));
mv.visitLabel(lNotTrack);
mv.visitVarInsn(Opcodes.ALOAD, 6);
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
}
private void proxyRecognizedMethods(ClassWriterTracker ct, String proxyClassDef, Class> type,
Class> helper, Class> proxyType) {
Method[] meths = type.getMethods();
for (Method meth : meths) {
// Java 8 methods with a return type of KeySetView do not need to be proxied
if (meth.getReturnType().getName().contains("KeySetView")) {
continue;
}
Class[] helperParams = toHelperParameters(meth.getParameterTypes(), proxyType);
// first check for overriding method
try {
Method match;
match = helper.getMethod(meth.getName(), helperParams);
proxyOverrideMethod(ct, meth, match, helperParams);
continue;
}
catch (NoSuchMethodException nsme) {
// all fine
}
catch (Exception e) {
throw new GeneralException(e);
}
// check for before and after methods, either of which may not
// exist
Method before = null;
try {
before = helper.getMethod("before" + StringUtil.capitalize(meth.getName()), helperParams);
}
catch (NoSuchMethodException nsme) {
// all fine
}
catch (Exception e) {
throw new GeneralException(e);
}
Method after = null;
Class[] afterParams = null;
try {
afterParams = toHelperAfterParameters(helperParams,
meth.getReturnType(), (before == null)
? void.class : before.getReturnType());
after = helper.getMethod("after"
+ StringUtil.capitalize(meth.getName()), afterParams);
}
catch (NoSuchMethodException nsme) {
}
catch (Exception e) {
throw new GeneralException(e);
}
if (before != null || after != null)
proxyBeforeAfterMethod(ct, type, meth, helperParams, before, after, afterParams);
}
}
/**
* Proxy the given method with one that overrides it by calling into the
* given helper.
*/
private void proxyOverrideMethod(ClassWriterTracker ct, Method meth, Method helper, Class[] helperParams) {
MethodVisitor mv = ct.visitMethod(meth.getModifiers() & ~Modifier.SYNCHRONIZED, meth.getName(),
Type.getMethodDescriptor(meth), null, null);
mv.visitCode();
// push all the method params to the stack
// we only start at param[1] as param[0] of the helper method is the instance itself
// and will get loaded with ALOAD_0 (this)
mv.visitVarInsn(Opcodes.ALOAD, 0);
for (int i = 1; i < helperParams.length; i++)
{
mv.visitVarInsn(AsmHelper.getLoadInsn(helperParams[i]), i);
}
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(helper.getDeclaringClass()), helper.getName(),
Type.getMethodDescriptor(helper), false);
mv.visitInsn(AsmHelper.getReturnInsn(meth.getReturnType()));
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
/**
* Proxy the given method with one that overrides it by calling into the
* given helper.
*/
private void proxyBeforeAfterMethod(ClassWriterTracker ct, Class type, Method meth, Class[] helperParams,
Method before, Method after, Class[] afterParams) {
MethodVisitor mv = ct.visitMethod(meth.getModifiers() & ~Modifier.SYNCHRONIZED, meth.getName(),
Type.getMethodDescriptor(meth), null, null);
mv.visitCode();
int beforeRetPos = -1;
int variableNr = helperParams.length;;
// invoke before
if (before != null) {
// push all the method params to the stack
// we only start at param[1] as param[0] of the helper method is the instance itself
// and will get loaded with ALOAD_0 (this)
mv.visitVarInsn(Opcodes.ALOAD, 0);
for (int i = 1; i < helperParams.length; i++)
{
mv.visitVarInsn(AsmHelper.getLoadInsn(helperParams[i]), i);
}
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(before.getDeclaringClass()), before.getName(),
Type.getMethodDescriptor(before), false);
if (after != null && before.getReturnType() != void.class) {
// this is always a boolean and 1 after the
beforeRetPos = variableNr++;
mv.visitVarInsn(AsmHelper.getStoreInsn(before.getReturnType()), beforeRetPos);
}
}
// invoke super
mv.visitVarInsn(Opcodes.ALOAD, 0);
for (int i = 1; i < helperParams.length; i++)
{
mv.visitVarInsn(AsmHelper.getLoadInsn(helperParams[i]), i);
}
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(type), meth.getName(),
Type.getMethodDescriptor(meth), false);
// invoke after
if (after != null) {
int retPos = -1;
if (meth.getReturnType() != void.class) {
retPos = variableNr++;
mv.visitVarInsn(AsmHelper.getStoreInsn(meth.getReturnType()), retPos);
}
// push all the method params to the stack
// we only start at param[1] as param[0] of the helper method is the instance itself
// and will get loaded with ALOAD_0 (this)
mv.visitVarInsn(Opcodes.ALOAD, 0);
for (int i = 1; i < helperParams.length; i++)
{
mv.visitVarInsn(AsmHelper.getLoadInsn(helperParams[i]), i);
}
if (retPos != -1) {
mv.visitVarInsn(AsmHelper.getLoadInsn(meth.getReturnType()),retPos);
}
if (beforeRetPos != -1) {
mv.visitVarInsn(AsmHelper.getLoadInsn(before.getReturnType()),beforeRetPos);
}
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(after.getDeclaringClass()), after.getName(),
Type.getMethodDescriptor(after), false);
}
mv.visitInsn(AsmHelper.getReturnInsn(meth.getReturnType()));
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
/**
* add the instance variables to the class to be generated
*/
private void addInstanceVariables(ClassWriterTracker ct) {
// variable #1, the state manager
ct.getCw().visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_TRANSIENT,
"sm", Type.getDescriptor(OpenJPAStateManager.class), null, null).visitEnd();
// variable #2, the state manager
ct.getCw().visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_TRANSIENT,
"field", Type.getDescriptor(int.class), null, null).visitEnd();
}
/**
* Create pass-through constructors to base type.
*/
private void delegateConstructors(ClassWriterTracker ct, Class type, String superClassFileNname) {
Constructor[] constructors = type.getConstructors();
for (Constructor constructor : constructors) {
Class[] params = constructor.getParameterTypes();
String[] exceptionTypes = AsmHelper.getInternalNames(constructor.getExceptionTypes());
String descriptor = Type.getConstructorDescriptor(constructor);
MethodVisitor mv = ct.visitMethod(Opcodes.ACC_PUBLIC, "", descriptor, null, exceptionTypes);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
int stackPos = 1;
for (Class param : params) {
mv.visitVarInsn(AsmHelper.getLoadInsn(param), stackPos);
stackPos += Type.getType(param).getSize();
}
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, superClassFileNname, "", descriptor, false);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
}
/**
* Implement the methods in the {@link Proxy} interface, with the exception
* of {@link Proxy#copy}.
*
* @param defaultChangeTracker whether to implement a null change tracker; if false
* the change tracker method is left unimplemented
* @param proxyClassDef
*/
private void addProxyMethods(ClassWriterTracker ct, boolean defaultChangeTracker, String proxyClassDef, Class> parentClass) {
{
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "setOwner",
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(OpenJPAStateManager.class), Type.INT_TYPE)
, null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitFieldInsn(Opcodes.PUTFIELD, proxyClassDef, "sm", Type.getDescriptor(OpenJPAStateManager.class));
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ILOAD, 2);
mv.visitFieldInsn(Opcodes.PUTFIELD, proxyClassDef, "field", Type.getDescriptor(Integer.TYPE));
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
{
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "getOwner",
Type.getMethodDescriptor(Type.getType(OpenJPAStateManager.class))
, null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, proxyClassDef, "sm", Type.getDescriptor(OpenJPAStateManager.class));
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
{
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "getOwnerField",
Type.getMethodDescriptor(Type.INT_TYPE)
, null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, proxyClassDef, "field", Type.INT_TYPE.getDescriptor());
mv.visitInsn(Opcodes.IRETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
{
/*
* clone (return detached proxy object)
* Note: This method is only being provided to satisfy a quirk with
* the IBM JDK -- while comparing Calendar objects, the clone() method
* was invoked. So, we are now overriding the clone() method so as to
* provide a detached proxy object (null out the StateManager).
*/
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "clone",
Type.getMethodDescriptor(TYPE_OBJECT)
, null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(parentClass), "clone",
Type.getMethodDescriptor(TYPE_OBJECT), false);
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Proxy.class));
mv.visitVarInsn(Opcodes.ASTORE, 1);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitInsn(Opcodes.ACONST_NULL);
mv.visitInsn(Opcodes.ICONST_0);
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type.getInternalName(Proxy.class), "setOwner",
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(OpenJPAStateManager.class), Type.INT_TYPE), true);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
if (defaultChangeTracker) {
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "getChangeTracker",
Type.getMethodDescriptor(Type.getType(ChangeTracker.class))
, null, null);
mv.visitCode();
mv.visitInsn(Opcodes.ACONST_NULL);
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
}
/**
* Implement the methods in the {@link ProxyDate} interface.
*/
private void addProxyDateMethods(ClassWriterTracker ct, String proxyClassDef, Class type) {
final boolean hasDefaultCons = hasConstructor(type);
final boolean hasMillisCons = hasConstructor(type, long.class);
if (!hasDefaultCons && !hasMillisCons) {
throw new UnsupportedException(_loc.get("no-date-cons", type));
}
// add a default constructor that delegates to the millis constructor
if (!hasDefaultCons) {
MethodVisitor mv = ct.visitMethod(Opcodes.ACC_PUBLIC, "",
Type.getMethodDescriptor(Type.VOID_TYPE), null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(System.class), "currentTimeMillis",
Type.getMethodDescriptor(Type.LONG_TYPE), false);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(type), "",
Type.getMethodDescriptor(Type.VOID_TYPE, Type.LONG_TYPE), false);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
{
// date copy
Constructor cons = findCopyConstructor(type);
Class[] params;
if (cons != null) {
params = cons.getParameterTypes();
}
else if (hasMillisCons) {
params = new Class[]{long.class};
}
else {
params = new Class[0];
}
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "copy",
Type.getMethodDescriptor(TYPE_OBJECT, TYPE_OBJECT)
, null, null);
mv.visitCode();
mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(type));
mv.visitInsn(Opcodes.DUP);
if (params.length == 1) {
mv.visitVarInsn(Opcodes.ALOAD, 1);
if (params[0] == long.class) {
// call getTime on the given Date if the current type has a long constructor
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(java.util.Date.class));
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(java.util.Date.class), "getTime",
Type.getMethodDescriptor(Type.LONG_TYPE), false);
}
else {
// otherwise just pass the parameter
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(params[0]));
}
}
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(type), "",
Type.getMethodDescriptor(Type.VOID_TYPE, AsmHelper.getParamTypes(params)), false);
if (params.length == 0) {
mv.visitInsn(Opcodes.DUP);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(java.util.Date.class));
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(java.util.Date.class), "getTime",
Type.getMethodDescriptor(Type.LONG_TYPE), false);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), "setTime",
Type.getMethodDescriptor(Type.VOID_TYPE, Type.LONG_TYPE), false);
}
if ((params.length == 0 || params[0] == long.class)
&& Timestamp.class.isAssignableFrom(type)) {
mv.visitInsn(Opcodes.DUP);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Timestamp.class));
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Timestamp.class), "getNanos",
Type.getMethodDescriptor(Type.INT_TYPE), false);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), "setNanos",
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE), false);
}
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
{
// new instance factory
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "newInstance",
Type.getMethodDescriptor(Type.getType(ProxyDate.class))
, null, null);
mv.visitCode();
mv.visitTypeInsn(Opcodes.NEW, proxyClassDef);
mv.visitInsn(Opcodes.DUP);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, proxyClassDef, "",
Type.getMethodDescriptor(Type.VOID_TYPE), false);
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
}
private void addProxyCalendarMethods(ClassWriterTracker ct, String proxyClassDef, Class type) {
// calendar copy
{
Constructor cons = findCopyConstructor(type);
Class[] params = (cons == null) ? new Class[0] : cons.getParameterTypes();
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "copy",
Type.getMethodDescriptor(TYPE_OBJECT, TYPE_OBJECT)
, null, null);
mv.visitCode();
mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(type));
mv.visitInsn(Opcodes.DUP);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(type), "",
Type.getMethodDescriptor(Type.VOID_TYPE, AsmHelper.getParamTypes(params)), false);
// timeInMillis
mv.visitInsn(Opcodes.DUP);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Calendar.class));
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Calendar.class), "getTimeInMillis",
Type.getMethodDescriptor(Type.LONG_TYPE), false);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), "setTimeInMillis",
Type.getMethodDescriptor(Type.VOID_TYPE, Type.LONG_TYPE), false);
// lenient
mv.visitInsn(Opcodes.DUP);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Calendar.class));
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Calendar.class), "isLenient",
Type.getMethodDescriptor(Type.BOOLEAN_TYPE), false);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), "setLenient",
Type.getMethodDescriptor(Type.VOID_TYPE, Type.BOOLEAN_TYPE), false);
// firstDayOfWeek
mv.visitInsn(Opcodes.DUP);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Calendar.class));
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Calendar.class), "getFirstDayOfWeek",
Type.getMethodDescriptor(Type.INT_TYPE), false);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), "setFirstDayOfWeek",
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE), false);
// minimalDaysInFirstWeek
mv.visitInsn(Opcodes.DUP);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Calendar.class));
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Calendar.class), "getMinimalDaysInFirstWeek",
Type.getMethodDescriptor(Type.INT_TYPE), false);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), "setMinimalDaysInFirstWeek",
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE), false);
// timeZone
mv.visitInsn(Opcodes.DUP);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Calendar.class));
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Calendar.class), "getTimeZone",
Type.getMethodDescriptor(Type.getType(TimeZone.class)), false);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), "setTimeZone",
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(TimeZone.class)), false);
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
// newInstance factory
{
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "newInstance",
Type.getMethodDescriptor(Type.getType(ProxyCalendar.class))
, null, null);
mv.visitCode();
mv.visitTypeInsn(Opcodes.NEW, proxyClassDef);
mv.visitInsn(Opcodes.DUP);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, proxyClassDef, "",
Type.getMethodDescriptor(Type.VOID_TYPE), false);
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
// proxy the protected computeFields method b/c it is called on
// mutate, and some setters are final and therefore not proxyable
{
MethodVisitor mv = ct.visitMethod(Modifier.PROTECTED, "computeFields",
Type.getMethodDescriptor(Type.VOID_TYPE)
, null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitInsn(Opcodes.ICONST_1);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Proxies.class), "dirty",
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Proxy.class), Type.BOOLEAN_TYPE), false);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(type), "computeFields",
Type.getMethodDescriptor(Type.VOID_TYPE), false);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
}
/**
* Proxy setter methods of the given type.
*
* @return true if we generated any setters, false otherwise
*/
private boolean proxySetters(ClassWriterTracker ct, String proxyClassDef, Class type) {
Method[] meths = type.getMethods();
int setters = 0;
for (Method meth : meths) {
if (isSetter(meth) && !Modifier.isFinal(meth.getModifiers())) {
setters++;
proxySetter(ct, type, meth);
}
}
return setters > 0;
}
private void proxySetter(ClassWriterTracker ct, Class type, Method meth) {
Class[] params = meth.getParameterTypes();
Class ret = meth.getReturnType();
final String methodDescriptor = Type.getMethodDescriptor(Type.getType(ret), AsmHelper.getParamTypes(params));
if (ct.hasMethod(meth.getName(), methodDescriptor)) {
// this method already got created
return;
}
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, meth.getName(), methodDescriptor, null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitInsn(Opcodes.ICONST_1);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Proxies.class), "dirty",
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Proxy.class), Type.BOOLEAN_TYPE), false);
mv.visitVarInsn(Opcodes.ALOAD, 0);
// push all the method params to the stack
int stackPos = 1;
for (int i = 1; i <= params.length; i++) {
Class param = params[i-1];
mv.visitVarInsn(AsmHelper.getLoadInsn(param), stackPos);
stackPos += Type.getType(param).getSize();
}
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(type), meth.getName(),
methodDescriptor, false);
mv.visitInsn(AsmHelper.getReturnInsn(ret));
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
/**
* Add a writeReplace implementation that serializes to a non-proxy type
* unless detached and this is a build-time generated class.
*/
private void addWriteReplaceMethod(ClassWriterTracker ct, String proxyClassDef, boolean runtime) {
MethodVisitor mv = ct.visitMethod(Modifier.PROTECTED, "writeReplace",
Type.getMethodDescriptor(TYPE_OBJECT)
, null, new String[]{Type.getInternalName(ObjectStreamException.class)});
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitInsn(runtime ? Opcodes.ICONST_0 : Opcodes.ICONST_1); // !runtime
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Proxies.class), "writeReplace",
Type.getMethodDescriptor(TYPE_OBJECT, Type.getType(Proxy.class), Type.BOOLEAN_TYPE), false);
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
private boolean hasConstructor(Class type, Class>... paramTypes) {
try {
return type.getDeclaredConstructor(paramTypes) != null;
}
catch (NoSuchMethodException e) {
return false;
}
}
/**
* Return whether the given collection type allows duplicates.
*/
protected boolean allowsDuplicates(Class type) {
return !Set.class.isAssignableFrom(type);
}
/**
* Return whether the given collection type maintains an artificial
* ordering.
*/
protected boolean isOrdered(Class type) {
return List.class.isAssignableFrom(type)
|| "java.util.LinkedHashSet".equals(type.getName());
}
/**
* Return the parameter types to the corresponding helper class method.
*/
private static Class[] toHelperParameters(Class[] cls, Class helper) {
Class[] params = new Class[cls.length + 1];
params[0] = helper;
System.arraycopy(cls, 0, params, 1, cls.length);
return params;
}
/**
* Return the parameter types to the corresponding helper class "after"
* method.
*/
private static Class[] toHelperAfterParameters(Class[] cls, Class ret,
Class beforeRet) {
if (ret == void.class && beforeRet == void.class)
return cls;
int len = cls.length;
if (ret != void.class)
len++;
if (beforeRet != void.class)
len++;
Class[] params = new Class[len];
System.arraycopy(cls, 0, params, 0, cls.length);
int pos = cls.length;
if (ret != void.class)
params[pos++] = ret;
if (beforeRet != void.class)
params[pos++] = beforeRet;
return params;
}
/**
* Return whether the given method is a setter.
*/
protected boolean isSetter(Method meth) {
return startsWith(meth.getName(), "set")
|| startsWith(meth.getName(), "add")
|| startsWith(meth.getName(), "remove")
|| startsWith(meth.getName(), "insert")
|| startsWith(meth.getName(), "clear")
|| startsWith(meth.getName(), "roll"); // used by Calendar
}
/**
* Return the getter corresponding to the given setter, or null.
*/
protected Method findGetter(Class type, Method setter) {
String name = setter.getName().substring(3);
Class param = setter.getParameterTypes()[0];
Method getter;
try {
getter = type.getMethod("get" + name, (Class[]) null);
if (getter.getReturnType().isAssignableFrom(param)
|| param.isAssignableFrom(getter.getReturnType()))
return getter;
} catch (NoSuchMethodException nsme) {
} catch (Exception e) {
throw new GeneralException(e);
}
if (param == boolean.class || param == Boolean.class) {
try {
getter = type.getMethod("is" + name, (Class[]) null);
if (getter.getReturnType().isAssignableFrom(param)
|| param.isAssignableFrom(getter.getReturnType()))
return getter;
} catch (NoSuchMethodException nsme) {
} catch (Exception e) {
throw new GeneralException(e);
}
}
return null;
}
/**
* Return whether the target string stars with the given token.
*/
private static boolean startsWith(String str, String token) {
return str.startsWith(token)
&& (str.length() == token.length()
|| Character.isUpperCase(str.charAt(token.length())));
}
/**
* Create a unique id to avoid proxy class name conflicts.
*/
private static synchronized long nextProxyId() {
return _proxyId++;
}
/**
* Find an appropriate copy constructor for the given type, or return null
* if none.
*/
protected Constructor findCopyConstructor(Class cls) {
Constructor[] cons = cls.getConstructors();
Constructor match = null;
Class matchParam = null;
Class[] params;
for (Constructor con : cons) {
params = con.getParameterTypes();
if (params.length != 1)
continue;
// quit immediately on exact match
if (params[0] == cls)
return con;
if (params[0].isAssignableFrom(cls) && (matchParam == null
|| matchParam.isAssignableFrom(params[0]))) {
// track most derived collection constructor
match = con;
matchParam = params[0];
}
}
return match;
}
/**
* Return the constructor that takes a comparator for the given type, or
* null if none.
*/
private static Constructor findComparatorConstructor(Class cls) {
try {
return cls.getConstructor(new Class[] { Comparator.class });
} catch (NoSuchMethodException nsme) {
return null;
} catch (Exception e) {
throw new GeneralException(e);
}
}
/**
* Usage: java org.apache.openjpa.util.proxy.ProxyManagerImpl [option]*
* <class name>+
* Where the following options are recognized:
*
*
-utils/-u <number>: 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.
*
*
* The main method generates .class files for the proxies to the classes
* given on the command line. It writes the generated classes to beside the
* ProxyManagerImpl.class file if possible; otherwise it writes to the
* current directory. The proxy manager looks for these classes
* before generating its own proxies at runtime.
*/
public static void main(String[] args)
throws ClassNotFoundException, IOException {
File dir = Files.getClassFile(ProxyManagerImpl.class);
dir = (dir == null) ? new File(AccessController.doPrivileged(
J2DoPrivHelper.getPropertyAction("user.dir")))
: dir.getParentFile();
Options opts = new Options();
args = opts.setFromCmdLine(args);
List types = new ArrayList(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",
}));
}
final ProxyManagerImpl mgr = new ProxyManagerImpl();
Class cls;
for (Object type : types) {
cls = Class.forName((String) type);
try {
if (Class.forName(getProxyClassName(cls, false), true,
GeneratedClasses.getMostDerivedLoader(cls, Proxy.class))
!= null)
continue;
}
catch (Throwable t) {
// expected if the class hasn't been generated
}
final String proxyClassName = getProxyClassName(cls, false);
byte[] bytes = null;
if (Date.class.isAssignableFrom(cls)) {
bytes = mgr.generateProxyDateBytecode(cls, false, proxyClassName);
}
else if (Calendar.class.isAssignableFrom(cls)) {
bytes = mgr.generateProxyCalendarBytecode(cls, false, proxyClassName);
}
else if (Collection.class.isAssignableFrom(cls)) {
bytes = mgr.generateProxyCollectionBytecode(cls, false, proxyClassName);
}
else if (Map.class.isAssignableFrom(cls)) {
bytes = mgr.generateProxyMapBytecode(cls, false, proxyClassName);
}
else {
bytes = mgr.generateProxyBeanBytecode(cls, false, proxyClassName);
}
if (bytes != null) {
final String fileName = cls.getName().replace('.', '$') + PROXY_SUFFIX + ".class";
java.nio.file.Files.write(new File(dir, fileName).toPath(), bytes);
}
}
}
}