/* * 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.PrivilegedAction; 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.Set; import java.util.SortedMap; import java.util.SortedSet; import java.util.TimeZone; import java.util.TreeMap; import java.util.TreeSet; import java.util.Queue; import org.apache.openjpa.enhance.AsmAdaptor; 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 java.util.concurrent.ConcurrentHashMap; import serp.bytecode.BCClass; import serp.bytecode.BCField; import serp.bytecode.BCMethod; import serp.bytecode.Code; import serp.bytecode.JumpInstruction; import serp.bytecode.Project; /** * Default implementation of the {@link ProxyManager} interface. * * @author Abe White */ public class ProxyManagerImpl implements ProxyManager { private static final String PROXY_SUFFIX = "$proxy"; private static final Localizer _loc = Localizer.forPackage (ProxyManagerImpl.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<String> _unproxyable = new HashSet<String>(); private final Map<Class<?>, Proxy> _proxies = new ConcurrentHashMap<Class<?>, Proxy>(); 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. */ 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))); } 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); } } 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); } 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); } 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); } 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); } 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); } public Proxy newDateProxy(Class type) { ProxyDate proxy = getFactoryProxyDate(type); return proxy.newInstance(); } 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); } 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; } 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); } 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 (int i = 0; i < intfs.length; i++) { concrete = toConcreteType(intfs[i], concretes); if (concrete != null) return concrete; } return null; } /** * 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 = GeneratedClasses.loadBCClass( generateProxyCollectionBytecode(type, true), l); proxy = (ProxyCollection) instantiateProxy(pcls, null, null); _proxies.put(type, proxy); } return proxy; } /** * 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 = GeneratedClasses.loadBCClass( generateProxyMapBytecode(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 = GeneratedClasses.loadBCClass( generateProxyDateBytecode(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 = GeneratedClasses.loadBCClass( generateProxyCalendarBytecode(type, true), l); proxy = (ProxyCalendar) 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) { // TODO Move this to J2DOPrivHelper? BCClass bc = AccessController.doPrivileged(new PrivilegedAction<BCClass>() { public BCClass run() { return generateProxyBeanBytecode(type, true); } }); if (bc != null) pcls = GeneratedClasses.loadBCClass(bc, 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 org.apache.openjpa.util.DelayedArrayListProxy.class; } if (type.equals(java.util.HashSet.class)) { return org.apache.openjpa.util.DelayedHashSetProxy.class; } if (type.equals(java.util.LinkedList.class)) { return org.apache.openjpa.util.DelayedLinkedListProxy.class; } if (type.equals(java.util.Vector.class)) { return org.apache.openjpa.util.DelayedVectorProxy.class; } if (type.equals(java.util.LinkedHashSet.class)) { return org.apache.openjpa.util.DelayedLinkedHashSetProxy.class; } if (type.equals(java.util.SortedSet.class) || type.equals(java.util.TreeSet.class)) { return org.apache.openjpa.util.DelayedTreeSetProxy.class; } if (type.equals(java.util.PriorityQueue.class)) { return org.apache.openjpa.util.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); } } /** * Generate the bytecode for a collection proxy for the given type. */ protected BCClass generateProxyCollectionBytecode(Class type, boolean runtime) { assertNotFinal(type); Project project = new Project(); BCClass bc = AccessController.doPrivileged(J2DoPrivHelper .loadProjectClassAction(project, getProxyClassName(type, runtime))); bc.setSuperclass(type); bc.declareInterface(ProxyCollection.class); delegateConstructors(bc, type); addProxyMethods(bc, false); addProxyCollectionMethods(bc, type); proxyRecognizedMethods(bc, type, ProxyCollections.class, ProxyCollection.class); proxySetters(bc, type); addWriteReplaceMethod(bc, runtime); 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 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; } /** * Generate the bytecode for a map proxy for the given type. */ protected BCClass generateProxyMapBytecode(Class type, boolean runtime) { assertNotFinal(type); Project project = new Project(); BCClass bc = AccessController.doPrivileged(J2DoPrivHelper .loadProjectClassAction(project, getProxyClassName(type, runtime))); bc.setSuperclass(type); bc.declareInterface(ProxyMap.class); delegateConstructors(bc, type); addProxyMethods(bc, false); addProxyMapMethods(bc, type); Class<? extends ProxyMaps> mapProxyClassType = ProxyConcurrentMaps.class; proxyRecognizedMethods(bc, type, mapProxyClassType, ProxyMap.class); proxySetters(bc, type); addWriteReplaceMethod(bc, runtime); return bc; } /** * Generate the bytecode for a date proxy for the given type. */ protected BCClass generateProxyDateBytecode(Class type, boolean runtime) { assertNotFinal(type); Project project = new Project(); BCClass bc = AccessController.doPrivileged(J2DoPrivHelper .loadProjectClassAction(project, getProxyClassName(type, runtime))); bc.setSuperclass(type); bc.declareInterface(ProxyDate.class); delegateConstructors(bc, type); addProxyMethods(bc, true); addProxyDateMethods(bc, type); proxySetters(bc, type); addWriteReplaceMethod(bc, runtime); return bc; } /** * Generate the bytecode for a calendar proxy for the given type. */ protected BCClass generateProxyCalendarBytecode(Class type, boolean runtime) { assertNotFinal(type); Project project = new Project(); BCClass bc = AccessController.doPrivileged(J2DoPrivHelper .loadProjectClassAction(project, getProxyClassName(type, runtime))); bc.setSuperclass(type); bc.declareInterface(ProxyCalendar.class); delegateConstructors(bc, type); addProxyMethods(bc, true); addProxyCalendarMethods(bc, type); proxySetters(bc, type); addWriteReplaceMethod(bc, runtime); return bc; } /** * Generate the bytecode for a bean proxy for the given type. */ protected BCClass generateProxyBeanBytecode(Class type, boolean runtime) { 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; } Project project = new Project(); BCClass bc = AccessController.doPrivileged(J2DoPrivHelper .loadProjectClassAction(project, getProxyClassName(type, runtime))); bc.setSuperclass(type); bc.declareInterface(ProxyBean.class); delegateConstructors(bc, type); addProxyMethods(bc, true); addProxyBeanMethods(bc, type, cons); if (!proxySetters(bc, type)) return null; addWriteReplaceMethod(bc, runtime); return bc; } /** * Create pass-through constructors to base type. */ private void delegateConstructors(BCClass bc, Class type) { Constructor[] cons = type.getConstructors(); Class[] params; BCMethod m; Code code; for (int i = 0; i < cons.length; i++) { params = cons[i].getParameterTypes(); m = bc.declareMethod("<init>", void.class, params); m.makePublic(); code = m.getCode(true); code.aload().setThis(); for (int j = 0; j < params.length; j++) code.xload().setParam(j).setType(params[j]); code.invokespecial().setMethod(cons[i]); code.vreturn(); code.calculateMaxStack(); code.calculateMaxLocals(); } } /** * Implement the methods in the {@link Proxy} interface, with the exception * of {@link Proxy#copy}. * * @param changeTracker whether to implement a null change tracker; if false * the change tracker method is left unimplemented */ private void addProxyMethods(BCClass bc, boolean changeTracker) { BCField sm = bc.declareField("sm", OpenJPAStateManager.class); sm.setTransient(true); BCField field = bc.declareField("field", int.class); field.setTransient(true); BCMethod m = bc.declareMethod("setOwner", void.class, new Class[] { OpenJPAStateManager.class, int.class }); m.makePublic(); Code code = m.getCode(true); code.aload().setThis(); code.aload().setParam(0); code.putfield().setField(sm); code.aload().setThis(); code.iload().setParam(1); code.putfield().setField(field); code.vreturn(); code.calculateMaxStack(); code.calculateMaxLocals(); m = bc.declareMethod("getOwner", OpenJPAStateManager.class, null); m.makePublic(); code = m.getCode(true); code.aload().setThis(); code.getfield().setField(sm); code.areturn(); code.calculateMaxStack(); code.calculateMaxLocals(); m = bc.declareMethod("getOwnerField", int.class, null); m.makePublic(); code = m.getCode(true); code.aload().setThis(); code.getfield().setField(field); code.ireturn(); code.calculateMaxStack(); code.calculateMaxLocals(); /* * 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). */ m = bc.declareMethod("clone", Object.class, null); m.makePublic(); code = m.getCode(true); code.aload().setThis(); code.invokespecial().setMethod(bc.getSuperclassType(), "clone", Object.class, null); code.checkcast().setType(Proxy.class); int other = code.getNextLocalsIndex(); code.astore().setLocal(other); code.aload().setLocal(other); code.constant().setNull(); code.constant().setValue(0); code.invokeinterface().setMethod(Proxy.class, "setOwner", void.class, new Class[] { OpenJPAStateManager.class, int.class }); code.aload().setLocal(other); code.areturn(); code.calculateMaxStack(); code.calculateMaxLocals(); if (changeTracker) { m = bc.declareMethod("getChangeTracker", ChangeTracker.class, null); m.makePublic(); code = m.getCode(true); code.constant().setNull(); code.areturn(); code.calculateMaxStack(); code.calculateMaxLocals(); } } /** * Implement the methods in the {@link ProxyCollection} interface. */ private void addProxyCollectionMethods(BCClass bc, Class type) { // change tracker BCField changeTracker = bc.declareField("changeTracker", CollectionChangeTracker.class); changeTracker.setTransient(true); BCMethod m = bc.declareMethod("getChangeTracker", ChangeTracker.class, null); m.makePublic(); Code code = m.getCode(true); code.aload().setThis(); code.getfield().setField(changeTracker); code.areturn(); code.calculateMaxStack(); code.calculateMaxLocals(); // 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(); m = bc.declareMethod("copy", Object.class, new Class[] {Object.class}); m.makePublic(); code = m.getCode(true); code.anew().setType(type); code.dup(); if (params.length == 1) { code.aload().setParam(0); if (params[0] == Comparator.class) { code.checkcast().setType(SortedSet.class); code.invokeinterface().setMethod(SortedSet.class, "comparator", Comparator.class, null); } else code.checkcast().setType(params[0]); } code.invokespecial().setMethod(type, "<init>", void.class, params); if (params.length == 0 || params[0] == Comparator.class) { code.dup(); code.aload().setParam(0); code.checkcast().setType(Collection.class); code.invokevirtual().setMethod(type, "addAll", boolean.class, new Class[] { Collection.class }); code.pop(); } code.areturn(); code.calculateMaxStack(); code.calculateMaxLocals(); // element type BCField elementType = bc.declareField("elementType", Class.class); elementType.setTransient(true); m = bc.declareMethod("getElementType", Class.class, null); m.makePublic(); code = m.getCode(true); code.aload().setThis(); code.getfield().setField(elementType); code.areturn(); code.calculateMaxStack(); code.calculateMaxLocals(); // new instance factory m = bc.declareMethod("newInstance", ProxyCollection.class, new Class[] { Class.class, Comparator.class, boolean.class, boolean.class }); m.makePublic(); code = m.getCode(true); code.anew().setType(bc); code.dup(); cons = findComparatorConstructor(type); params = (cons == null) ? new Class[0] : cons.getParameterTypes(); if (params.length == 1) code.aload().setParam(1); code.invokespecial().setMethod("<init>", void.class, params); int ret = code.getNextLocalsIndex(); code.astore().setLocal(ret); // set element type code.aload().setLocal(ret); code.aload().setParam(0); code.putfield().setField(elementType); // create change tracker and set it code.iload().setParam(2); JumpInstruction ifins = code.ifeq(); code.aload().setLocal(ret); code.anew().setType(CollectionChangeTrackerImpl.class); code.dup(); code.aload().setLocal(ret); code.constant().setValue(allowsDuplicates(type)); code.constant().setValue(isOrdered(type)); code.aload().setParam(3); code.invokespecial().setMethod(CollectionChangeTrackerImpl.class, "<init>", void.class, new Class[] { Collection.class, boolean.class, boolean.class, boolean.class }); code.putfield().setField(changeTracker); ifins.setTarget(code.aload().setLocal(ret)); code.areturn(); code.calculateMaxStack(); code.calculateMaxLocals(); } /** * 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()); } /** * Implement the methods in the {@link ProxyMap} interface. */ private void addProxyMapMethods(BCClass bc, Class type) { // change tracker BCField changeTracker = bc.declareField("changeTracker", MapChangeTracker.class); changeTracker.setTransient(true); BCMethod m = bc.declareMethod("getChangeTracker", ChangeTracker.class, null); m.makePublic(); Code code = m.getCode(true); code.aload().setThis(); code.getfield().setField(changeTracker); code.areturn(); code.calculateMaxStack(); code.calculateMaxLocals(); // 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(); m = bc.declareMethod("copy", Object.class, new Class[] {Object.class}); m.makePublic(); code = m.getCode(true); code.anew().setType(type); code.dup(); if (params.length == 1) { code.aload().setParam(0); if (params[0] == Comparator.class) { code.checkcast().setType(SortedMap.class); code.invokeinterface().setMethod(SortedMap.class, "comparator", Comparator.class, null); } else code.checkcast().setType(params[0]); } code.invokespecial().setMethod(type, "<init>", void.class, params); if (params.length == 0 || params[0] == Comparator.class) { code.dup(); code.aload().setParam(0); code.checkcast().setType(Map.class); code.invokevirtual().setMethod(type, "putAll", void.class, new Class[] { Map.class }); } code.areturn(); code.calculateMaxStack(); code.calculateMaxLocals(); // key type BCField keyType = bc.declareField("keyType", Class.class); keyType.setTransient(true); m = bc.declareMethod("getKeyType", Class.class, null); m.makePublic(); code = m.getCode(true); code.aload().setThis(); code.getfield().setField(keyType); code.areturn(); code.calculateMaxStack(); code.calculateMaxLocals(); // value type BCField valueType = bc.declareField("valueType", Class.class); valueType.setTransient(true); m = bc.declareMethod("getValueType", Class.class, null); m.makePublic(); code = m.getCode(true); code.aload().setThis(); code.getfield().setField(valueType); code.areturn(); code.calculateMaxStack(); code.calculateMaxLocals(); // new instance factory m = bc.declareMethod("newInstance", ProxyMap.class, new Class[] { Class.class, Class.class, Comparator.class, boolean.class,boolean.class }); m.makePublic(); code = m.getCode(true); code.anew().setType(bc); code.dup(); cons = findComparatorConstructor(type); params = (cons == null) ? new Class[0] : cons.getParameterTypes(); if (params.length == 1) code.aload().setParam(2); code.invokespecial().setMethod("<init>", void.class, params); int ret = code.getNextLocalsIndex(); code.astore().setLocal(ret); // set key and value types code.aload().setLocal(ret); code.aload().setParam(0); code.putfield().setField(keyType); code.aload().setLocal(ret); code.aload().setParam(1); code.putfield().setField(valueType); // create change tracker and set it code.iload().setParam(3); JumpInstruction ifins = code.ifeq(); code.aload().setLocal(ret); code.anew().setType(MapChangeTrackerImpl.class); code.dup(); code.aload().setLocal(ret); code.aload().setParam(4); code.invokespecial().setMethod(MapChangeTrackerImpl.class, "<init>", void.class, new Class[] { Map.class, boolean.class }); code.putfield().setField(changeTracker); ifins.setTarget(code.aload().setLocal(ret)); code.areturn(); code.calculateMaxStack(); code.calculateMaxLocals(); } /** * Implement the methods in the {@link ProxyDate} interface. */ private void addProxyDateMethods(BCClass bc, Class type) { boolean hasDefaultCons = bc.getDeclaredMethod("<init>", (Class[]) null) != null; boolean hasMillisCons = bc.getDeclaredMethod("<init>", new Class[] { long.class }) != null; if (!hasDefaultCons && !hasMillisCons) throw new UnsupportedException(_loc.get("no-date-cons", type)); // add a default constructor that delegates to the millis constructor BCMethod m; Code code; if (!hasDefaultCons) { m = bc.declareMethod("<init>", void.class, null); m.makePublic(); code = m.getCode(true); code.aload().setThis(); code.invokestatic().setMethod(System.class, "currentTimeMillis", long.class, null); code.invokespecial().setMethod(type, "<init>", void.class, new Class[] { long.class }); code.vreturn(); code.calculateMaxStack(); code.calculateMaxLocals(); } // 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]; m = bc.declareMethod("copy", Object.class, new Class[] {Object.class}); m.makePublic(); code = m.getCode(true); code.anew().setType(type); code.dup(); if (params.length == 1) { if (params[0] == long.class) { code.aload().setParam(0); code.checkcast().setType(Date.class); code.invokevirtual().setMethod(Date.class, "getTime", long.class, null); } else { code.aload().setParam(0); code.checkcast().setType(params[0]); } } code.invokespecial().setMethod(type, "<init>", void.class, params); if (params.length == 0) { code.dup(); code.aload().setParam(0); code.checkcast().setType(Date.class); code.invokevirtual().setMethod(Date.class, "getTime", long.class, null); code.invokevirtual().setMethod(type, "setTime", void.class, new Class[] { long.class }); } if ((params.length == 0 || params[0] == long.class) && Timestamp.class.isAssignableFrom(type)) { code.dup(); code.aload().setParam(0); code.checkcast().setType(Timestamp.class); code.invokevirtual().setMethod(Timestamp.class, "getNanos", int.class, null); code.invokevirtual().setMethod(type, "setNanos", void.class, new Class[] { int.class }); } code.areturn(); code.calculateMaxStack(); code.calculateMaxLocals(); // new instance factory m = bc.declareMethod("newInstance", ProxyDate.class, null); m.makePublic(); code = m.getCode(true); code.anew().setType(bc); code.dup(); code.invokespecial().setMethod("<init>", void.class, null); code.areturn(); code.calculateMaxStack(); code.calculateMaxLocals(); } /** * Implement the methods in the {@link ProxyCalendar} interface. */ private void addProxyCalendarMethods(BCClass bc, Class type) { // calendar copy Constructor cons = findCopyConstructor(type); Class[] params = (cons == null) ? new Class[0] : cons.getParameterTypes(); BCMethod m = bc.declareMethod("copy", Object.class, new Class[] {Object.class}); m.makePublic(); Code code = m.getCode(true); code.anew().setType(type); code.dup(); if (params.length == 1) { code.aload().setParam(0); code.checkcast().setType(params[0]); } code.invokespecial().setMethod(type, "<init>", void.class, params); if (params.length == 0) { code.dup(); code.aload().setParam(0); code.checkcast().setType(Calendar.class); code.invokevirtual().setMethod(Calendar.class, "getTimeInMillis", long.class, null); code.invokevirtual().setMethod(type, "setTimeInMillis", void.class, new Class[] { long.class }); code.dup(); code.aload().setParam(0); code.checkcast().setType(Calendar.class); code.invokevirtual().setMethod(Calendar.class, "isLenient", boolean.class, null); code.invokevirtual().setMethod(type, "setLenient", void.class, new Class[] { boolean.class }); code.dup(); code.aload().setParam(0); code.checkcast().setType(Calendar.class); code.invokevirtual().setMethod(Calendar.class, "getFirstDayOfWeek", int.class, null); code.invokevirtual().setMethod(type, "setFirstDayOfWeek", void.class, new Class[] { int.class }); code.dup(); code.aload().setParam(0); code.checkcast().setType(Calendar.class); code.invokevirtual().setMethod(Calendar.class, "getMinimalDaysInFirstWeek", int.class, null); code.invokevirtual().setMethod(type, "setMinimalDaysInFirstWeek", void.class, new Class[] { int.class }); code.dup(); code.aload().setParam(0); code.checkcast().setType(Calendar.class); code.invokevirtual().setMethod(Calendar.class, "getTimeZone", TimeZone.class, null); code.invokevirtual().setMethod(type, "setTimeZone", void.class, new Class[] { TimeZone.class }); } code.areturn(); code.calculateMaxStack(); code.calculateMaxLocals(); // new instance factory m = bc.declareMethod("newInstance", ProxyCalendar.class, null); m.makePublic(); code = m.getCode(true); code.anew().setType(bc); code.dup(); code.invokespecial().setMethod("<init>", void.class, null); code.areturn(); code.calculateMaxStack(); code.calculateMaxLocals(); // proxy the protected computeFields method b/c it is called on // mutate, and some setters are final and therefore not proxyable m = bc.declareMethod("computeFields", void.class, null); m.makeProtected(); code = m.getCode(true); code.aload().setThis(); code.constant().setValue(true); code.invokestatic().setMethod(Proxies.class, "dirty", void.class, new Class[] { Proxy.class, boolean.class }); code.aload().setThis(); code.invokespecial().setMethod(type, "computeFields", void.class, null); code.vreturn(); code.calculateMaxStack(); code.calculateMaxLocals(); } /** * Implement the methods in the {@link ProxyBean} interface. */ private void addProxyBeanMethods(BCClass bc, Class type, Constructor cons) { // bean copy BCMethod m = bc.declareMethod("copy", Object.class, new Class[] { Object.class }); m.makePublic(); Code code = m.getCode(true); code.anew().setType(type); code.dup(); Class[] params = cons.getParameterTypes(); if (params.length == 1) { code.aload().setParam(0); code.checkcast().setType(params[0]); } code.invokespecial().setMethod(cons); if (params.length == 0) copyProperties(type, code); code.areturn(); code.calculateMaxStack(); code.calculateMaxLocals(); // new instance factory m = bc.declareMethod("newInstance", ProxyBean.class, new Class[] { Object.class }); m.makePublic(); code = m.getCode(true); code.anew().setType(bc); code.dup(); if (params.length == 1) { code.aload().setParam(0); code.checkcast().setType(params[0]); } code.invokespecial().setMethod("<init>", void.class, params); if (params.length == 0) copyProperties(type, code); code.areturn(); code.calculateMaxStack(); code.calculateMaxLocals(); } /** * Copy bean properties. Called with the copy object on the stack. Must * return with the copy object on the stack. */ private void copyProperties(Class type, Code code) { int copy = code.getNextLocalsIndex(); code.astore().setLocal(copy); Method[] meths = type.getMethods(); Method getter; int mods; for (int i = 0; i < meths.length; i++) { mods = meths[i].getModifiers(); if (!Modifier.isPublic(mods) || Modifier.isStatic(mods)) continue; if (!startsWith(meths[i].getName(), "set") || meths[i].getParameterTypes().length != 1) continue; getter = findGetter(type, meths[i]); if (getter == null) continue; // copy.setXXX(orig.getXXX()); code.aload().setLocal(copy); code.aload().setParam(0); code.checkcast().setType(type); code.invokevirtual().setMethod(getter); code.invokevirtual().setMethod(meths[i]); } code.aload().setLocal(copy); } /** * Proxy recognized methods to invoke helpers in given helper class. */ private void proxyRecognizedMethods(BCClass bc, Class type, Class helper, Class proxyType) { Method[] meths = type.getMethods(); Class[] params; Class[] afterParams; Method match; Method after; for (int i = 0; i < meths.length; i++) { // Java 8 methods with a return type of KeySetView do not need to be proxied if (meths[i].getReturnType().getName().contains("KeySetView")) continue; params = toHelperParameters(meths[i].getParameterTypes(), proxyType); // first check for overriding method try { match = helper.getMethod(meths[i].getName(), params); proxyOverrideMethod(bc, meths[i], match, params); continue; } catch (NoSuchMethodException nsme) { } catch (Exception e) { throw new GeneralException(e); } // check for before and after methods, either of which may not // exist match = null; try { match = helper.getMethod("before" + StringUtil.capitalize(meths[i].getName()), params); } catch (NoSuchMethodException nsme) { } catch (Exception e) { throw new GeneralException(e); } after = null; afterParams = null; try { afterParams = toHelperAfterParameters(params, meths[i].getReturnType(), (match == null) ? void.class : match.getReturnType()); after = helper.getMethod("after" + StringUtil.capitalize(meths[i].getName()), afterParams); } catch (NoSuchMethodException nsme) { } catch (Exception e) { throw new GeneralException(e); } if (match != null || after != null) proxyBeforeAfterMethod(bc, type, meths[i], match, params, after, afterParams); } } /** * 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; } /** * Proxy setter methods of the given type. * * @return true if we find any setters, false otherwise */ private boolean proxySetters(BCClass bc, Class type) { Method[] meths = type.getMethods(); int setters = 0; for (int i = 0; i < meths.length; i++) { if (isSetter(meths[i]) && !Modifier.isFinal(meths[i].getModifiers()) && bc.getDeclaredMethod(meths[i].getName(), meths[i].getParameterTypes()) == null) { setters++; proxySetter(bc, type, meths[i]); } } return setters > 0; } /** * Proxy the given method with one that overrides it by calling into the * given helper. */ private void proxyOverrideMethod(BCClass bc, Method meth, Method helper, Class[] params) { BCMethod m = bc.declareMethod(meth.getName(), meth.getReturnType(), meth.getParameterTypes()); m.makePublic(); Code code = m.getCode(true); code.aload().setThis(); for (int i = 1; i < params.length; i++) code.xload().setParam(i - 1).setType(params[i]); code.invokestatic().setMethod(helper); code.xreturn().setType(meth.getReturnType()); code.calculateMaxStack(); code.calculateMaxLocals(); } /** * Proxy the given method with one that overrides it by calling into the * given helper. */ private void proxyBeforeAfterMethod(BCClass bc, Class type, Method meth, Method before, Class[] params, Method after, Class[] afterParams) { BCMethod m = bc.declareMethod(meth.getName(), meth.getReturnType(), meth.getParameterTypes()); m.makePublic(); Code code = m.getCode(true); // invoke before int beforeRet = -1; if (before != null) { code.aload().setThis(); for (int i = 1; i < params.length; i++) code.xload().setParam(i - 1).setType(params[i]); code.invokestatic().setMethod(before); if (after != null && before.getReturnType() != void.class) { beforeRet = code.getNextLocalsIndex(); code.xstore().setLocal(beforeRet). setType(before.getReturnType()); } } // invoke super code.aload().setThis(); for (int i = 1; i < params.length; i++) code.xload().setParam(i - 1).setType(params[i]); code.invokespecial().setMethod(type, meth.getName(), meth.getReturnType(), meth.getParameterTypes()); // invoke after if (after != null) { int ret = -1; if (meth.getReturnType() != void.class) { ret = code.getNextLocalsIndex(); code.xstore().setLocal(ret).setType(meth.getReturnType()); } code.aload().setThis(); for (int i = 1; i < params.length; i++) code.xload().setParam(i - 1).setType(params[i]); if (ret != -1) code.xload().setLocal(ret).setType(meth.getReturnType()); if (beforeRet != -1) code.xload().setLocal(beforeRet). setType(before.getReturnType()); code.invokestatic().setMethod(after); } code.xreturn().setType(meth.getReturnType()); code.calculateMaxStack(); code.calculateMaxLocals(); } /** * 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()))); } /** * Proxy the given setter method to dirty the proxy owner. */ private void proxySetter(BCClass bc, Class type, Method meth) { Class[] params = meth.getParameterTypes(); Class ret = meth.getReturnType(); BCMethod m = bc.declareMethod(meth.getName(), ret, params); m.makePublic(); Code code = m.getCode(true); code.aload().setThis(); code.constant().setValue(true); code.invokestatic().setMethod(Proxies.class, "dirty", void.class, new Class[] { Proxy.class, boolean.class }); code.aload().setThis(); for (int i = 0; i < params.length; i++) code.xload().setParam(i).setType(params[i]); code.invokespecial().setMethod(type, meth.getName(), ret, params); code.xreturn().setType(ret); code.calculateMaxStack(); code.calculateMaxLocals(); } /** * Add a writeReplace implementation that serializes to a non-proxy type * unless detached and this is a build-time generated class. */ private void addWriteReplaceMethod(BCClass bc, boolean runtime) { BCMethod m = bc.declareMethod("writeReplace", Object.class, null); m.makeProtected(); m.getExceptions(true).addException(ObjectStreamException.class); Code code = m.getCode(true); code.aload().setThis(); code.constant().setValue(!runtime); code.invokestatic().setMethod(Proxies.class, "writeReplace", Object.class, new Class[] { Proxy.class, boolean.class }); code.areturn(); code.calculateMaxLocals(); code.calculateMaxStack(); } /** * 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 (int i = 0; i < cons.length; i++) { params = cons[i].getParameterTypes(); if (params.length != 1) continue; // quit immediately on exact match if (params[0] == cls) return cons[i]; if (params[0].isAssignableFrom(cls) && (matchParam == null || matchParam.isAssignableFrom(params[0]))) { // track most derived collection constructor match = cons[i]; 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>+<br /> * Where the following options are recognized: * <ul> * <li><i>-utils/-u <number></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 * 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(); 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", })); } final ProxyManagerImpl mgr = new ProxyManagerImpl(); Class cls; BCClass bc; for (int i = 0; i < types.size(); i++) { cls = Class.forName((String) types.get(i)); 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 } if (Collection.class.isAssignableFrom(cls)) bc = mgr.generateProxyCollectionBytecode(cls, false); else if (Map.class.isAssignableFrom(cls)) bc = mgr.generateProxyMapBytecode(cls, false); else if (Date.class.isAssignableFrom(cls)) bc = mgr.generateProxyDateBytecode(cls, false); else if (Calendar.class.isAssignableFrom(cls)) bc = mgr.generateProxyCalendarBytecode(cls, false); else { final Class fCls = cls; // TODO Move this to J2DOPrivHelper bc = AccessController .doPrivileged(new PrivilegedAction<BCClass>() { public BCClass run() { return mgr.generateProxyBeanBytecode(fCls, false); } }); } // START - ALLOW PRINT STATEMENTS System.out.println(bc.getName()); // STOP - ALLOW PRINT STATEMENTS AsmAdaptor.write(bc, new File(dir, bc.getClassName() + ".class")); } } }