Home » apache-openjpa-1.1.0-source » org.apache.openjpa » util » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one
    3    * or more contributor license agreements.  See the NOTICE file
    4    * distributed with this work for additional information
    5    * regarding copyright ownership.  The ASF licenses this file
    6    * to you under the Apache License, Version 2.0 (the
    7    * "License"); you may not use this file except in compliance
    8    * with the License.  You may obtain a copy of the License at
    9    *
   10    * http://www.apache.org/licenses/LICENSE-2.0
   11    *
   12    * Unless required by applicable law or agreed to in writing,
   13    * software distributed under the License is distributed on an
   14    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   15    * KIND, either express or implied.  See the License for the
   16    * specific language governing permissions and limitations
   17    * under the License.    
   18    */
   19   package org.apache.openjpa.util;
   20   
   21   import java.io.File;
   22   import java.io.IOException;
   23   import java.io.ObjectStreamException;
   24   import java.lang.reflect.Array;
   25   import java.lang.reflect.Constructor;
   26   import java.lang.reflect.Method;
   27   import java.lang.reflect.Modifier;
   28   import java.security.AccessController;
   29   import java.security.PrivilegedAction;
   30   import java.security.PrivilegedActionException;
   31   import java.sql.Timestamp;
   32   import java.util.ArrayList;
   33   import java.util.Arrays;
   34   import java.util.Calendar;
   35   import java.util.Collection;
   36   import java.util.Comparator;
   37   import java.util.Date;
   38   import java.util.GregorianCalendar;
   39   import java.util.HashMap;
   40   import java.util.HashSet;
   41   import java.util.LinkedList;
   42   import java.util.List;
   43   import java.util.Map;
   44   import java.util.Set;
   45   import java.util.SortedMap;
   46   import java.util.SortedSet;
   47   import java.util.TimeZone;
   48   import java.util.TreeMap;
   49   import java.util.TreeSet;
   50   import java.util.Queue;
   51   
   52   import org.apache.commons.lang.StringUtils;
   53   import org.apache.openjpa.kernel.OpenJPAStateManager;
   54   import org.apache.openjpa.lib.util.Files;
   55   import org.apache.openjpa.lib.util.J2DoPrivHelper;
   56   import org.apache.openjpa.lib.util.JavaVersions;
   57   import org.apache.openjpa.lib.util.Localizer;
   58   import org.apache.openjpa.lib.util.Options;
   59   import org.apache.openjpa.lib.util.concurrent.NullSafeConcurrentHashMap;
   60   
   61   import java.util.concurrent.ConcurrentHashMap;
   62   import serp.bytecode.BCClass;
   63   import serp.bytecode.BCField;
   64   import serp.bytecode.BCMethod;
   65   import serp.bytecode.Code;
   66   import serp.bytecode.JumpInstruction;
   67   import serp.bytecode.Project;
   68   import serp.util.Strings;
   69   
   70   /**
   71    * Default implementation of the {@link ProxyManager} interface.
   72    *
   73    * @author Abe White
   74    */
   75   public class ProxyManagerImpl
   76       implements ProxyManager {
   77   
   78       private static final String PROXY_SUFFIX = "$proxy";
   79   
   80       private static final Localizer _loc = Localizer.forPackage
   81           (ProxyManagerImpl.class);
   82   
   83       private static long _proxyId = 0L;
   84       private static final Map _stdCollections = new HashMap();
   85       private static final Map _stdMaps = new HashMap();
   86       static {
   87           _stdCollections.put(Collection.class, ArrayList.class);
   88           _stdCollections.put(Set.class, HashSet.class);
   89           _stdCollections.put(SortedSet.class, TreeSet.class);
   90           _stdCollections.put(List.class, ArrayList.class);
   91           _stdCollections.put(Queue.class, LinkedList.class);
   92           _stdMaps.put(Map.class, HashMap.class);
   93           _stdMaps.put(SortedMap.class, TreeMap.class);
   94       }
   95   
   96       private final Set _unproxyable = new HashSet();
   97       private final Map _proxies = new NullSafeConcurrentHashMap();
   98       private boolean _trackChanges = true;
   99       private boolean _assertType = false;
  100   
  101       public ProxyManagerImpl() {
  102           _unproxyable.add(TimeZone.class.getName());
  103       }
  104   
  105       /**
  106        * Whether proxies produced by this factory will use {@link ChangeTracker}s
  107        * to try to cut down on data store operations at the cost of some extra
  108        * bookkeeping overhead. Defaults to true.
  109        */
  110       public boolean getTrackChanges() {
  111           return _trackChanges;
  112       }
  113   
  114       /**
  115        * Whether proxies produced by this factory will use {@link ChangeTracker}s
  116        * to try to cut down on data store operations at the cost of some extra
  117        * bookkeeping overhead. Defaults to true.
  118        */
  119       public void setTrackChanges(boolean track) {
  120           _trackChanges = track;
  121       }
  122   
  123       /**
  124        * Whether to perform runtime checks to ensure that all elements
  125        * added to collection and map proxies are the proper element/key/value
  126        * type as defined by the metadata. Defaults to false.
  127        */
  128       public boolean getAssertAllowedType() {
  129           return _assertType;
  130       }
  131   
  132       /**
  133        * Whether to perform runtime checks to ensure that all elements
  134        * added to collection and map proxies are the proper element/key/value
  135        * type as defined by the metadata. Defaults to false.
  136        */
  137       public void setAssertAllowedType(boolean assertType) {
  138           _assertType = assertType;
  139       }
  140   
  141       /**
  142        * Return a mutable view of class names we know cannot be proxied  
  143        * correctly by this manager.
  144        */
  145       public Collection getUnproxyable() {
  146           return _unproxyable;
  147       }
  148   
  149       /**
  150        * Provided for auto-configuration.  Add the given semicolon-separated
  151        * class names to the set of class names we know cannot be proxied correctly
  152        * by this manager.
  153        */
  154       public void setUnproxyable(String clsNames) {
  155           if (clsNames != null)
  156               _unproxyable.addAll(Arrays.asList(Strings.split(clsNames, ";", 0)));
  157       }
  158   
  159       public Object copyArray(Object orig) {
  160           if (orig == null)
  161               return null;
  162   
  163           try {
  164               int length = Array.getLength(orig);
  165               Object array = Array.newInstance(orig.getClass().
  166                   getComponentType(), length);
  167   
  168               System.arraycopy(orig, 0, array, 0, length);
  169               return array;
  170           } catch (Exception e) {
  171               throw new UnsupportedException(_loc.get("bad-array",
  172                   e.getMessage()), e);
  173           }
  174       }
  175   
  176       public Collection copyCollection(Collection orig) {
  177           if (orig == null)
  178               return null;
  179           if (orig instanceof Proxy)
  180               return (Collection) ((Proxy) orig).copy(orig);
  181   
  182           ProxyCollection proxy = getFactoryProxyCollection(orig.getClass());
  183           return (Collection) proxy.copy(orig);
  184       }
  185   
  186       public Proxy newCollectionProxy(Class type, Class elementType,
  187           Comparator compare) {
  188           type = toProxyableCollectionType(type);
  189           ProxyCollection proxy = getFactoryProxyCollection(type);
  190           return proxy.newInstance((_assertType) ? elementType : null, compare,
  191               _trackChanges);
  192       }
  193   
  194       public Map copyMap(Map orig) {
  195           if (orig == null)
  196               return null;
  197           if (orig instanceof Proxy)
  198               return (Map) ((Proxy) orig).copy(orig);
  199   
  200           ProxyMap proxy = getFactoryProxyMap(orig.getClass());
  201           return (Map) proxy.copy(orig);
  202       }
  203   
  204       public Proxy newMapProxy(Class type, Class keyType, 
  205           Class elementType, Comparator compare) {
  206           type = toProxyableMapType(type);
  207           ProxyMap proxy = getFactoryProxyMap(type);
  208           return proxy.newInstance((_assertType) ? keyType : null, 
  209               (_assertType) ? elementType : null, compare, _trackChanges);
  210       }
  211   
  212       public Date copyDate(Date orig) {
  213           if (orig == null)
  214               return null;
  215           if (orig instanceof Proxy)
  216               return (Date) ((Proxy) orig).copy(orig);
  217   
  218           ProxyDate proxy = getFactoryProxyDate(orig.getClass());
  219           return (Date) proxy.copy(orig);
  220       }
  221   
  222       public Proxy newDateProxy(Class type) {
  223           ProxyDate proxy = getFactoryProxyDate(type);
  224           return proxy.newInstance();
  225       }
  226   
  227       public Calendar copyCalendar(Calendar orig) {
  228           if (orig == null)
  229               return null;
  230           if (orig instanceof Proxy)
  231               return (Calendar) ((Proxy) orig).copy(orig);
  232   
  233           ProxyCalendar proxy = getFactoryProxyCalendar(orig.getClass());
  234           return (Calendar) proxy.copy(orig);
  235       }
  236   
  237       public Proxy newCalendarProxy(Class type, TimeZone zone) {
  238           if (type == Calendar.class)
  239               type = GregorianCalendar.class;
  240           ProxyCalendar proxy = getFactoryProxyCalendar(type);
  241           ProxyCalendar cal = proxy.newInstance();
  242           if (zone != null)
  243               ((Calendar) cal).setTimeZone(zone);
  244           return cal;
  245       }
  246   
  247       public Object copyCustom(Object orig) {
  248           if (orig == null)
  249               return null;
  250           if (orig instanceof Proxy)
  251               return ((Proxy) orig).copy(orig);
  252           if (ImplHelper.isManageable(orig))
  253               return null;
  254           if (orig instanceof Collection)
  255               return copyCollection((Collection) orig);
  256           if (orig instanceof Map)
  257               return copyMap((Map) orig);
  258           if (orig instanceof Date)
  259               return copyDate((Date) orig);
  260           if (orig instanceof Calendar)
  261               return copyCalendar((Calendar) orig);
  262           ProxyBean proxy = getFactoryProxyBean(orig);
  263           return (proxy == null) ? null : proxy.copy(orig); 
  264       }
  265   
  266       public Proxy newCustomProxy(Object orig) {
  267           if (orig == null)
  268               return null;
  269           if (orig instanceof Proxy)
  270               return (Proxy) orig;
  271           if (ImplHelper.isManageable(orig))
  272               return null;
  273           if (orig instanceof Collection) {
  274               Comparator comp = (orig instanceof SortedSet) 
  275                   ? ((SortedSet) orig).comparator() : null;
  276               Collection c = (Collection) newCollectionProxy(orig.getClass(), 
  277                   null, comp); 
  278               c.addAll((Collection) orig);
  279               return (Proxy) c;
  280           }
  281           if (orig instanceof Map) {
  282               Comparator comp = (orig instanceof SortedMap) 
  283                   ? ((SortedMap) orig).comparator() : null;
  284               Map m = (Map) newMapProxy(orig.getClass(), null, null, comp);
  285               m.putAll((Map) orig);
  286               return (Proxy) m;
  287           }
  288           if (orig instanceof Date) {
  289               Date d = (Date) newDateProxy(orig.getClass());
  290               d.setTime(((Date) orig).getTime());
  291               if (orig instanceof Timestamp)
  292                   ((Timestamp) d).setNanos(((Timestamp) orig).getNanos());
  293               return (Proxy) d;
  294           }
  295           if (orig instanceof Calendar) {
  296               Calendar c = (Calendar) newCalendarProxy(orig.getClass(),
  297                   ((Calendar) orig).getTimeZone());
  298               c.setTimeInMillis(((Calendar) orig).getTimeInMillis());
  299               return (Proxy) c;
  300           }
  301   
  302           ProxyBean proxy = getFactoryProxyBean(orig);
  303           return (proxy == null) ? null : proxy.newInstance(orig);
  304       }
  305   
  306       /**
  307        * Return the concrete type for proxying.
  308        */
  309       protected Class toProxyableCollectionType(Class type) {
  310           if (type.getName().endsWith(PROXY_SUFFIX))
  311               type = type.getSuperclass();
  312           else if (type.isInterface()) {
  313               type = toConcreteType(type, _stdCollections);
  314               if (type == null)
  315                   throw new UnsupportedException(_loc.get("no-proxy-intf", type));
  316           } else if (Modifier.isAbstract(type.getModifiers()))
  317               throw new UnsupportedException(_loc.get("no-proxy-abstract", type));
  318           return type;
  319       }
  320   
  321       /**
  322        * Return the concrete type for proxying.
  323        */
  324       protected Class toProxyableMapType(Class type) {
  325           if (type.getName().endsWith(PROXY_SUFFIX))
  326               type = type.getSuperclass();
  327           else if (type.isInterface()) {
  328               type = toConcreteType(type, _stdMaps);
  329               if (type == null)
  330                   throw new UnsupportedException(_loc.get("no-proxy-intf", type));
  331           } else if (Modifier.isAbstract(type.getModifiers()))
  332               throw new UnsupportedException(_loc.get("no-proxy-abstract", type));
  333           return type;
  334       }
  335   
  336       /**
  337        * Locate a concrete type to proxy for the given collection interface.
  338        */
  339       private static Class toConcreteType(Class intf, Map concretes) {
  340           Class concrete = (Class) concretes.get(intf);
  341           if (concrete != null)
  342               return concrete;
  343           Class[] intfs = intf.getInterfaces();         
  344           for (int i = 0; i < intfs.length; i++) {
  345               concrete = toConcreteType(intfs[i], concretes);
  346               if (concrete != null)
  347                   return concrete;
  348           }
  349           return null; 
  350       }
  351   
  352       /**
  353        * Return the cached factory proxy for the given collection type.
  354        */
  355       private ProxyCollection getFactoryProxyCollection(Class type) {
  356           // we don't lock here; ok if two proxies get generated for same type
  357           ProxyCollection proxy = (ProxyCollection) _proxies.get(type);
  358           if (proxy == null) {
  359               ClassLoader l = GeneratedClasses.getMostDerivedLoader(type,
  360                   ProxyCollection.class);
  361               Class pcls = loadBuildTimeProxy(type, l);
  362               if (pcls == null)
  363                   pcls = GeneratedClasses.loadBCClass(
  364                       generateProxyCollectionBytecode(type, true), l);
  365               proxy = (ProxyCollection) instantiateProxy(pcls, null, null);
  366               _proxies.put(type, proxy);
  367           }
  368           return proxy;
  369       }
  370   
  371       /**
  372        * Return the cached factory proxy for the given map type.
  373        */
  374       private ProxyMap getFactoryProxyMap(Class type) {
  375           // we don't lock here; ok if two proxies get generated for same type
  376           ProxyMap proxy = (ProxyMap) _proxies.get(type);
  377           if (proxy == null) {
  378               ClassLoader l = GeneratedClasses.getMostDerivedLoader(type,
  379                   ProxyMap.class);
  380               Class pcls = loadBuildTimeProxy(type, l);
  381               if (pcls == null)
  382                   pcls = GeneratedClasses.loadBCClass(
  383                       generateProxyMapBytecode(type, true), l);
  384               proxy = (ProxyMap) instantiateProxy(pcls, null, null);
  385               _proxies.put(type, proxy);
  386           }
  387           return proxy;
  388       }
  389   
  390       /**
  391        * Return the cached factory proxy for the given date type.
  392        */
  393       private ProxyDate getFactoryProxyDate(Class type) {
  394           // we don't lock here; ok if two proxies get generated for same type
  395           ProxyDate proxy = (ProxyDate) _proxies.get(type);
  396           if (proxy == null) {
  397               ClassLoader l = GeneratedClasses.getMostDerivedLoader(type,
  398                   ProxyDate.class);
  399               Class pcls = loadBuildTimeProxy(type, l);
  400               if (pcls == null)
  401                   pcls = GeneratedClasses.loadBCClass(
  402                       generateProxyDateBytecode(type, true), l);
  403               proxy = (ProxyDate) instantiateProxy(pcls, null, null);
  404               _proxies.put(type, proxy);
  405           }
  406           return proxy;
  407       }
  408   
  409       /**
  410        * Return the cached factory proxy for the given calendar type.
  411        */
  412       private ProxyCalendar getFactoryProxyCalendar(Class type) {
  413           // we don't lock here; ok if two proxies get generated for same type
  414           ProxyCalendar proxy = (ProxyCalendar) _proxies.get(type);
  415           if (proxy == null) {
  416               ClassLoader l = GeneratedClasses.getMostDerivedLoader(type,
  417                   ProxyCalendar.class);
  418               Class pcls = loadBuildTimeProxy(type, l);
  419               if (pcls == null)
  420                   pcls = GeneratedClasses.loadBCClass(
  421                       generateProxyCalendarBytecode(type, true), l);
  422               proxy = (ProxyCalendar) instantiateProxy(pcls, null, null);
  423               _proxies.put(type, proxy);
  424           }
  425           return proxy;
  426       }
  427   
  428       /**
  429        * Return the cached factory proxy for the given bean type.
  430        */
  431       private ProxyBean getFactoryProxyBean(Object orig) {
  432           final Class type = orig.getClass();
  433           if (isUnproxyable(type))
  434               return null;
  435   
  436           // we don't lock here; ok if two proxies get generated for same type
  437           ProxyBean proxy = (ProxyBean) _proxies.get(type);
  438           if (proxy == null && !_proxies.containsKey(type)) {
  439               ClassLoader l = GeneratedClasses.getMostDerivedLoader(type,
  440                   ProxyBean.class);
  441               Class pcls = loadBuildTimeProxy(type, l);
  442               if (pcls == null) {
  443                   BCClass bc = (BCClass) AccessController
  444                       .doPrivileged(new PrivilegedAction() {
  445                           public Object run() {
  446                               return generateProxyBeanBytecode(type, true);
  447                           }
  448                       });
  449                   if (bc != null)
  450                       pcls = GeneratedClasses.loadBCClass(bc, l);
  451               }
  452               if (pcls != null)
  453                   proxy = (ProxyBean) instantiateProxy(pcls,
  454                       findCopyConstructor(type), new Object[] {orig});
  455               _proxies.put(type, proxy);
  456           }
  457           return proxy;
  458       }
  459   
  460       /**
  461        * Return whether the given type is known to be unproxyable.
  462        */
  463       protected boolean isUnproxyable(Class type) {
  464           for (; type != null && type != Object.class; 
  465               type = type.getSuperclass()) {
  466               if (_unproxyable.contains(type.getName()))
  467                   return true;
  468           }
  469           return false;
  470       }
  471   
  472       /**
  473        * Load the proxy class generated at build time for the given type,
  474        * returning null if none exists.
  475        */
  476       protected Class loadBuildTimeProxy(Class type, ClassLoader loader) {
  477           try {
  478               return Class.forName(getProxyClassName(type, false), true, loader);
  479           } catch (Throwable t) {
  480               return null;
  481           }
  482       }
  483   
  484       /**
  485        * Instantiate the given proxy class.
  486        */
  487       private Proxy instantiateProxy(Class cls, Constructor cons, Object[] args) {
  488           try {
  489               if (cons != null)
  490                   return (Proxy) cls.getConstructor(cons.getParameterTypes()).
  491                       newInstance(args);
  492               return (Proxy) AccessController.doPrivileged(
  493                   J2DoPrivHelper.newInstanceAction(cls));
  494           } catch (InstantiationException ie) {
  495               throw new UnsupportedException(_loc.get("cant-newinstance",
  496                   cls.getSuperclass().getName()));
  497           } catch (PrivilegedActionException pae) {
  498               Exception e = pae.getException();
  499               if (e instanceof InstantiationException)
  500                   throw new UnsupportedException(_loc.get("cant-newinstance",
  501                       cls.getSuperclass().getName()));
  502               else
  503                   throw new GeneralException(cls.getName()).setCause(e);
  504           } catch (Throwable t) {
  505               throw new GeneralException(cls.getName()).setCause(t);
  506           }
  507       }
  508   
  509       /**
  510        * Generate the bytecode for a collection proxy for the given type.
  511        */
  512       protected BCClass generateProxyCollectionBytecode(Class type, 
  513           boolean runtime) {
  514           assertNotFinal(type);
  515           Project project = new Project(); 
  516           BCClass bc = (BCClass) AccessController.doPrivileged(J2DoPrivHelper
  517               .loadProjectClassAction(project, getProxyClassName(type, runtime))); 
  518           bc.setSuperclass(type);
  519           bc.declareInterface(ProxyCollection.class);
  520    
  521           delegateConstructors(bc, type);
  522           addProxyMethods(bc, false);
  523           addProxyCollectionMethods(bc, type);
  524           proxyRecognizedMethods(bc, type, ProxyCollections.class, 
  525               ProxyCollection.class);
  526           proxySetters(bc, type);
  527           addWriteReplaceMethod(bc, runtime);
  528           return bc;
  529       }
  530   
  531       /**
  532        * Return the name of the proxy class to generate for the given type.   
  533        */
  534       private static String getProxyClassName(Class type, boolean runtime) {
  535           String id = (runtime) ? "$" + nextProxyId() : "";
  536           return Strings.getPackageName(ProxyManagerImpl.class) + "." 
  537               + type.getName().replace('.', '$') + id + PROXY_SUFFIX;
  538       }
  539   
  540       /**
  541        * Throw appropriate exception if the given type is final.
  542        */
  543       private static void assertNotFinal(Class type) {
  544           if (Modifier.isFinal(type.getModifiers()))
  545               throw new UnsupportedException(_loc.get("no-proxy-final", type));
  546       }
  547   
  548       /**
  549        * Generate the bytecode for a map proxy for the given type.
  550        */
  551       protected BCClass generateProxyMapBytecode(Class type, boolean runtime) {
  552           assertNotFinal(type);
  553           Project project = new Project(); 
  554           BCClass bc = (BCClass) AccessController.doPrivileged(J2DoPrivHelper
  555               .loadProjectClassAction(project, getProxyClassName(type, runtime))); 
  556           bc.setSuperclass(type);
  557           bc.declareInterface(ProxyMap.class);
  558    
  559           delegateConstructors(bc, type);
  560           addProxyMethods(bc, false);
  561           addProxyMapMethods(bc, type);
  562           proxyRecognizedMethods(bc, type, ProxyMaps.class, ProxyMap.class);
  563           proxySetters(bc, type);
  564           addWriteReplaceMethod(bc, runtime);
  565           return bc;
  566       }
  567   
  568       /**
  569        * Generate the bytecode for a date proxy for the given type.
  570        */
  571       protected BCClass generateProxyDateBytecode(Class type, boolean runtime) {
  572           assertNotFinal(type);
  573           Project project = new Project(); 
  574           BCClass bc = (BCClass) AccessController.doPrivileged(J2DoPrivHelper
  575               .loadProjectClassAction(project, getProxyClassName(type, runtime))); 
  576           bc.setSuperclass(type);
  577           bc.declareInterface(ProxyDate.class);
  578    
  579           delegateConstructors(bc, type);
  580           addProxyMethods(bc, true);
  581           addProxyDateMethods(bc, type);
  582           proxySetters(bc, type);
  583           addWriteReplaceMethod(bc, runtime);
  584           return bc;
  585       }
  586   
  587       /**
  588        * Generate the bytecode for a calendar proxy for the given type.
  589        */
  590       protected BCClass generateProxyCalendarBytecode(Class type, 
  591           boolean runtime) {
  592           assertNotFinal(type);
  593           Project project = new Project(); 
  594           BCClass bc = (BCClass) AccessController.doPrivileged(J2DoPrivHelper
  595               .loadProjectClassAction(project, getProxyClassName(type, runtime))); 
  596           bc.setSuperclass(type);
  597           bc.declareInterface(ProxyCalendar.class);
  598    
  599           delegateConstructors(bc, type);
  600           addProxyMethods(bc, true);
  601           addProxyCalendarMethods(bc, type);
  602           proxySetters(bc, type);
  603           addWriteReplaceMethod(bc, runtime);
  604           return bc;
  605       }
  606   
  607       /**
  608        * Generate the bytecode for a bean proxy for the given type.
  609        */
  610       protected BCClass generateProxyBeanBytecode(Class type, boolean runtime) {
  611           if (Modifier.isFinal(type.getModifiers()))
  612               return null;
  613           if (ImplHelper.isManagedType(null, type))
  614               return null;
  615   
  616           // we can only generate a valid proxy if there is a copy constructor
  617           // or a default constructor
  618           Constructor cons = findCopyConstructor(type);
  619           if (cons == null) {
  620               Constructor[] cs = type.getConstructors();
  621               for (int i = 0; cons == null && i < cs.length; i++)
  622                  if (cs[i].getParameterTypes().length == 0)
  623                       cons = cs[i]; 
  624               if (cons == null)
  625                   return null;
  626           }
  627   
  628           Project project = new Project(); 
  629           BCClass bc = (BCClass) AccessController.doPrivileged(J2DoPrivHelper
  630               .loadProjectClassAction(project, getProxyClassName(type, runtime))); 
  631           bc.setSuperclass(type);
  632           bc.declareInterface(ProxyBean.class);
  633    
  634           delegateConstructors(bc, type);
  635           addProxyMethods(bc, true);
  636           addProxyBeanMethods(bc, type, cons);
  637           if (!proxySetters(bc, type))
  638               return null;
  639           addWriteReplaceMethod(bc, runtime);
  640           return bc;
  641       }
  642   
  643       /**
  644        * Create pass-through constructors to base type.
  645        */
  646       private void delegateConstructors(BCClass bc, Class type) {
  647           Constructor[] cons = type.getConstructors();
  648           Class[] params;
  649           BCMethod m;
  650           Code code;
  651           for (int i = 0; i < cons.length; i++) {
  652               params = cons[i].getParameterTypes();
  653               m = bc.declareMethod("<init>", void.class, params); 
  654               m.makePublic();
  655   
  656               code = m.getCode(true);
  657               code.aload().setThis();
  658               for (int j = 0; j < params.length; j++)
  659                   code.xload().setParam(j).setType(params[j]);
  660               code.invokespecial().setMethod(cons[i]);
  661               code.vreturn();
  662               code.calculateMaxStack();
  663               code.calculateMaxLocals();
  664           }
  665       }
  666   
  667       /**
  668        * Implement the methods in the {@link Proxy} interface, with the exception
  669        * of {@link Proxy#copy}.
  670        *
  671        * @param changeTracker whether to implement a null change tracker; if false
  672        * the change tracker method is left unimplemented
  673        */
  674       private void addProxyMethods(BCClass bc, boolean changeTracker) {
  675           BCField sm = bc.declareField("sm", OpenJPAStateManager.class);
  676           sm.setTransient(true);
  677           BCField field = bc.declareField("field", int.class);
  678           field.setTransient(true);
  679   
  680           BCMethod m = bc.declareMethod("setOwner", void.class, new Class[] {
  681               OpenJPAStateManager.class, int.class });
  682           m.makePublic();
  683           Code code = m.getCode(true);
  684           code.aload().setThis();
  685           code.aload().setParam(0);
  686           code.putfield().setField(sm);
  687           code.aload().setThis();
  688           code.iload().setParam(1);
  689           code.putfield().setField(field);
  690           code.vreturn();
  691           code.calculateMaxStack();
  692           code.calculateMaxLocals();
  693   
  694           m = bc.declareMethod("getOwner", OpenJPAStateManager.class, null);
  695           m.makePublic();
  696           code = m.getCode(true);
  697           code.aload().setThis();
  698           code.getfield().setField(sm);
  699           code.areturn();
  700           code.calculateMaxStack();
  701           code.calculateMaxLocals();
  702   
  703           m = bc.declareMethod("getOwnerField", int.class, null);
  704           m.makePublic();
  705           code = m.getCode(true);
  706           code.aload().setThis();
  707           code.getfield().setField(field);
  708           code.ireturn();
  709           code.calculateMaxStack();
  710           code.calculateMaxLocals();
  711   
  712           /* 
  713            * clone (return detached proxy object)
  714            * Note:  This method is only being provided to satisfy a quirk with
  715            * the IBM JDK -- while comparing Calendar objects, the clone() method
  716            * was invoked.  So, we are now overriding the clone() method so as to
  717            * provide a detached proxy object (null out the StateManager).
  718            */
  719           m = bc.declareMethod("clone", Object.class, null);
  720           m.makePublic();
  721           code = m.getCode(true);
  722           code.aload().setThis();
  723           code.invokespecial().setMethod(bc.getSuperclassType(), "clone",
  724                   Object.class, null);  
  725           code.checkcast().setType(Proxy.class);  
  726           int other = code.getNextLocalsIndex();
  727           code.astore().setLocal(other);
  728           code.aload().setLocal(other);
  729           code.constant().setNull();
  730           code.constant().setValue(0);
  731           code.invokeinterface().setMethod(Proxy.class, "setOwner", void.class,
  732                   new Class[] { OpenJPAStateManager.class, int.class });
  733           code.aload().setLocal(other);
  734           code.areturn();
  735           code.calculateMaxStack();
  736           code.calculateMaxLocals();
  737           
  738           if (changeTracker) {
  739               m = bc.declareMethod("getChangeTracker", ChangeTracker.class, null);
  740               m.makePublic();
  741               code = m.getCode(true);
  742               code.constant().setNull();
  743               code.areturn();
  744               code.calculateMaxStack();
  745               code.calculateMaxLocals();
  746           }
  747       }
  748   
  749       /**
  750        * Implement the methods in the {@link ProxyCollection} interface.
  751        */
  752       private void addProxyCollectionMethods(BCClass bc, Class type) {
  753           // change tracker
  754           BCField changeTracker = bc.declareField("changeTracker", 
  755               CollectionChangeTracker.class);
  756           changeTracker.setTransient(true);
  757           BCMethod m = bc.declareMethod("getChangeTracker", ChangeTracker.class, 
  758               null);
  759           m.makePublic();
  760           Code code = m.getCode(true);
  761           code.aload().setThis();
  762           code.getfield().setField(changeTracker);
  763           code.areturn();
  764           code.calculateMaxStack();
  765           code.calculateMaxLocals();
  766   
  767           // collection copy
  768           Constructor cons = findCopyConstructor(type);
  769           if (cons == null && SortedSet.class.isAssignableFrom(type))
  770               cons = findComparatorConstructor(type);
  771           Class[] params = (cons == null) ? new Class[0] 
  772               : cons.getParameterTypes();
  773   
  774           m = bc.declareMethod("copy", Object.class, new Class[] {Object.class});
  775           m.makePublic();
  776           code = m.getCode(true);
  777   
  778           code.anew().setType(type);
  779           code.dup();
  780           if (params.length == 1) {
  781               code.aload().setParam(0);
  782               if (params[0] == Comparator.class) {
  783                   code.checkcast().setType(SortedSet.class);
  784                   code.invokeinterface().setMethod(SortedSet.class, "comparator", 
  785                       Comparator.class, null);
  786               } else
  787                   code.checkcast().setType(params[0]);
  788           }
  789           code.invokespecial().setMethod(type, "<init>", void.class, params);
  790           if (params.length == 0 || params[0] == Comparator.class) {
  791               code.dup();
  792               code.aload().setParam(0);
  793               code.checkcast().setType(Collection.class);
  794               code.invokevirtual().setMethod(type, "addAll", boolean.class, 
  795                   new Class[] { Collection.class });
  796               code.pop();
  797           }
  798           code.areturn();
  799           code.calculateMaxStack();
  800           code.calculateMaxLocals();
  801   
  802           // element type
  803           BCField elementType = bc.declareField("elementType", Class.class);
  804           elementType.setTransient(true);
  805           m = bc.declareMethod("getElementType", Class.class, null);
  806           m.makePublic();
  807           code = m.getCode(true);
  808           code.aload().setThis();
  809           code.getfield().setField(elementType);
  810           code.areturn();
  811           code.calculateMaxStack();
  812           code.calculateMaxLocals();
  813   
  814           // new instance factory
  815           m = bc.declareMethod("newInstance", ProxyCollection.class, 
  816               new Class[] { Class.class, Comparator.class, boolean.class });
  817           m.makePublic();
  818           code = m.getCode(true);
  819   
  820           code.anew().setType(bc); 
  821           code.dup();
  822           cons = findComparatorConstructor(type);
  823           params = (cons == null) ? new Class[0] : cons.getParameterTypes();
  824           if (params.length == 1)
  825               code.aload().setParam(1);
  826           code.invokespecial().setMethod("<init>", void.class, params);
  827           int ret = code.getNextLocalsIndex();
  828           code.astore().setLocal(ret);
  829   
  830           // set element type
  831           code.aload().setLocal(ret);
  832           code.aload().setParam(0);
  833           code.putfield().setField(elementType);
  834   
  835           // create change tracker and set it
  836           code.iload().setParam(2);
  837           JumpInstruction ifins = code.ifeq();
  838           code.aload().setLocal(ret);
  839           code.anew().setType(CollectionChangeTrackerImpl.class);
  840           code.dup();
  841           code.aload().setLocal(ret);
  842           code.constant().setValue(allowsDuplicates(type));
  843           code.constant().setValue(isOrdered(type));
  844           code.invokespecial().setMethod(CollectionChangeTrackerImpl.class, 
  845               "<init>", void.class, new Class[] { Collection.class, 
  846               boolean.class, boolean.class });
  847           code.putfield().setField(changeTracker);
  848   
  849           ifins.setTarget(code.aload().setLocal(ret));
  850           code.areturn();
  851           code.calculateMaxStack();
  852           code.calculateMaxLocals();
  853       }
  854   
  855       /**
  856        * Return whether the given collection type allows duplicates.
  857        */
  858       protected boolean allowsDuplicates(Class type) {
  859           return !Set.class.isAssignableFrom(type);
  860       }
  861   
  862       /**
  863        * Return whether the given collection type maintains an artificial 
  864        * ordering.
  865        */
  866       protected boolean isOrdered(Class type) {
  867           return List.class.isAssignableFrom(type)
  868               || "java.util.LinkedHashSet".equals(type.getName());
  869       }
  870   
  871       /**
  872        * Implement the methods in the {@link ProxyMap} interface.
  873        */
  874       private void addProxyMapMethods(BCClass bc, Class type) {
  875           // change tracker
  876           BCField changeTracker = bc.declareField("changeTracker", 
  877               MapChangeTracker.class);
  878           changeTracker.setTransient(true);
  879           BCMethod m = bc.declareMethod("getChangeTracker", ChangeTracker.class, 
  880               null);
  881           m.makePublic();
  882           Code code = m.getCode(true);
  883           code.aload().setThis();
  884           code.getfield().setField(changeTracker);
  885           code.areturn();
  886           code.calculateMaxStack();
  887           code.calculateMaxLocals();
  888   
  889           // map copy
  890           Constructor cons = findCopyConstructor(type);
  891           if (cons == null && SortedMap.class.isAssignableFrom(type))
  892               cons = findComparatorConstructor(type);
  893           Class[] params = (cons == null) ? new Class[0] 
  894               : cons.getParameterTypes();
  895   
  896           m = bc.declareMethod("copy", Object.class, new Class[] {Object.class});
  897           m.makePublic();
  898           code = m.getCode(true);
  899   
  900           code.anew().setType(type);
  901           code.dup();
  902           if (params.length == 1) {
  903               code.aload().setParam(0);
  904               if (params[0] == Comparator.class) {
  905                   code.checkcast().setType(SortedMap.class);
  906                   code.invokeinterface().setMethod(SortedMap.class, "comparator", 
  907                       Comparator.class, null);
  908               } else 
  909                   code.checkcast().setType(params[0]);
  910           }
  911           code.invokespecial().setMethod(type, "<init>", void.class, params);
  912           if (params.length == 0 || params[0] == Comparator.class) {
  913               code.dup();
  914               code.aload().setParam(0);
  915               code.checkcast().setType(Map.class);
  916               code.invokevirtual().setMethod(type, "putAll", void.class, 
  917                   new Class[] { Map.class });
  918           }
  919           code.areturn();
  920           code.calculateMaxStack();
  921           code.calculateMaxLocals();
  922   
  923           // key type
  924           BCField keyType = bc.declareField("keyType", Class.class);
  925           keyType.setTransient(true);
  926           m = bc.declareMethod("getKeyType", Class.class, null);
  927           m.makePublic();
  928           code = m.getCode(true);
  929           code.aload().setThis();
  930           code.getfield().setField(keyType);
  931           code.areturn();
  932           code.calculateMaxStack();
  933           code.calculateMaxLocals();
  934   
  935           // value type
  936           BCField valueType = bc.declareField("valueType", Class.class);
  937           valueType.setTransient(true);
  938           m = bc.declareMethod("getValueType", Class.class, null);
  939           m.makePublic();
  940           code = m.getCode(true);
  941           code.aload().setThis();
  942           code.getfield().setField(valueType);
  943           code.areturn();
  944           code.calculateMaxStack();
  945           code.calculateMaxLocals();
  946   
  947           // new instance factory
  948           m = bc.declareMethod("newInstance", ProxyMap.class, 
  949               new Class[] { Class.class, Class.class, Comparator.class, 
  950               boolean.class });
  951           m.makePublic();
  952           code = m.getCode(true);
  953   
  954           code.anew().setType(bc); 
  955           code.dup();
  956           cons = findComparatorConstructor(type);
  957           params = (cons == null) ? new Class[0] : cons.getParameterTypes();
  958           if (params.length == 1)
  959               code.aload().setParam(2);
  960           code.invokespecial().setMethod("<init>", void.class, params);
  961           int ret = code.getNextLocalsIndex();
  962           code.astore().setLocal(ret);
  963   
  964           // set key and value types
  965           code.aload().setLocal(ret);
  966           code.aload().setParam(0);
  967           code.putfield().setField(keyType);
  968           code.aload().setLocal(ret);
  969           code.aload().setParam(1);
  970           code.putfield().setField(valueType);
  971   
  972           // create change tracker and set it
  973           code.iload().setParam(3);
  974           JumpInstruction ifins = code.ifeq();
  975           code.aload().setLocal(ret);
  976           code.anew().setType(MapChangeTrackerImpl.class);
  977           code.dup();
  978           code.aload().setLocal(ret);
  979           code.invokespecial().setMethod(MapChangeTrackerImpl.class, 
  980               "<init>", void.class, new Class[] { Map.class });
  981           code.putfield().setField(changeTracker);
  982   
  983           ifins.setTarget(code.aload().setLocal(ret));
  984           code.areturn();
  985           code.calculateMaxStack();
  986           code.calculateMaxLocals();
  987       }
  988   
  989       /**
  990        * Implement the methods in the {@link ProxyDate} interface.
  991        */
  992       private void addProxyDateMethods(BCClass bc, Class type) {
  993           boolean hasDefaultCons = bc.getDeclaredMethod("<init>", 
  994               (Class[]) null) != null;
  995           boolean hasMillisCons = bc.getDeclaredMethod("<init>", 
  996               new Class[] { long.class }) != null;
  997           if (!hasDefaultCons && !hasMillisCons)
  998               throw new UnsupportedException(_loc.get("no-date-cons", type));
  999   
 1000           // add a default constructor that delegates to the millis constructor
 1001           BCMethod m;
 1002           Code code;
 1003           if (!hasDefaultCons) {
 1004               m = bc.declareMethod("<init>", void.class, null);
 1005               m.makePublic();
 1006               code = m.getCode(true);
 1007               code.aload().setThis();
 1008               code.invokestatic().setMethod(System.class, "currentTimeMillis",
 1009                   long.class, null);
 1010               code.invokespecial().setMethod(type, "<init>", void.class,
 1011                   new Class[] { long.class });
 1012               code.vreturn();
 1013               code.calculateMaxStack();           
 1014               code.calculateMaxLocals();
 1015           }
 1016   
 1017           // date copy
 1018           Constructor cons = findCopyConstructor(type);
 1019           Class[] params;
 1020           if (cons != null)
 1021               params = cons.getParameterTypes();
 1022           else if (hasMillisCons)
 1023               params = new Class[] { long.class };
 1024           else
 1025               params = new Class[0];
 1026   
 1027           m = bc.declareMethod("copy", Object.class, new Class[] {Object.class});
 1028           m.makePublic();
 1029           code = m.getCode(true);
 1030   
 1031           code.anew().setType(type);
 1032           code.dup();
 1033           if (params.length == 1) {
 1034               if (params[0] == long.class) {
 1035                   code.aload().setParam(0);
 1036                   code.checkcast().setType(Date.class);
 1037                   code.invokevirtual().setMethod(Date.class, "getTime", 
 1038                       long.class, null);
 1039               } else {
 1040                   code.aload().setParam(0);
 1041                   code.checkcast().setType(params[0]);
 1042               }
 1043           }
 1044           code.invokespecial().setMethod(type, "<init>", void.class, params);
 1045           if (params.length == 0) {
 1046               code.dup();
 1047               code.aload().setParam(0);
 1048               code.checkcast().setType(Date.class);
 1049               code.invokevirtual().setMethod(Date.class, "getTime", long.class, 
 1050                   null);
 1051               code.invokevirtual().setMethod(type, "setTime", void.class,
 1052                   new Class[] { long.class });
 1053           }
 1054           if ((params.length == 0 || params[0] == long.class) 
 1055               && Timestamp.class.isAssignableFrom(type)) {
 1056               code.dup();
 1057               code.aload().setParam(0);
 1058               code.checkcast().setType(Timestamp.class);
 1059               code.invokevirtual().setMethod(Timestamp.class, "getNanos", 
 1060                   int.class, null);
 1061               code.invokevirtual().setMethod(type, "setNanos", void.class,
 1062                   new Class[] { int.class });
 1063           }
 1064           code.areturn();
 1065           code.calculateMaxStack();
 1066           code.calculateMaxLocals();
 1067   
 1068           // new instance factory
 1069           m = bc.declareMethod("newInstance", ProxyDate.class, null); 
 1070           m.makePublic();
 1071           code = m.getCode(true);
 1072           code.anew().setType(bc); 
 1073           code.dup();
 1074           code.invokespecial().setMethod("<init>", void.class, null);
 1075           code.areturn();
 1076           code.calculateMaxStack();
 1077           code.calculateMaxLocals();
 1078       }
 1079   
 1080       /**
 1081        * Implement the methods in the {@link ProxyCalendar} interface.
 1082        */
 1083       private void addProxyCalendarMethods(BCClass bc, Class type) {
 1084           // calendar copy
 1085           Constructor cons = findCopyConstructor(type);
 1086           Class[] params = (cons == null) ? new Class[0] 
 1087               : cons.getParameterTypes();
 1088   
 1089           BCMethod m = bc.declareMethod("copy", Object.class, 
 1090               new Class[] {Object.class});
 1091           m.makePublic();
 1092           Code code = m.getCode(true);
 1093   
 1094           code.anew().setType(type);
 1095           code.dup();
 1096           if (params.length == 1) {
 1097               code.aload().setParam(0);
 1098               code.checkcast().setType(params[0]);
 1099           }
 1100           code.invokespecial().setMethod(type, "<init>", void.class, params);
 1101           if (params.length == 0) {
 1102               code.dup();
 1103               code.aload().setParam(0);
 1104               code.checkcast().setType(Calendar.class);
 1105               code.invokevirtual().setMethod(Calendar.class, "getTimeInMillis", 
 1106                   long.class, null);
 1107               code.invokevirtual().setMethod(type, "setTimeInMillis", void.class,
 1108                   new Class[] { long.class });
 1109   
 1110               code.dup();
 1111               code.aload().setParam(0);
 1112               code.checkcast().setType(Calendar.class);
 1113               code.invokevirtual().setMethod(Calendar.class, "isLenient", 
 1114                   boolean.class, null);
 1115               code.invokevirtual().setMethod(type, "setLenient", void.class,
 1116                   new Class[] { boolean.class });
 1117   
 1118               code.dup();
 1119               code.aload().setParam(0);
 1120               code.checkcast().setType(Calendar.class);
 1121               code.invokevirtual().setMethod(Calendar.class, "getFirstDayOfWeek", 
 1122                   int.class, null);
 1123               code.invokevirtual().setMethod(type, "setFirstDayOfWeek", 
 1124                   void.class, new Class[] { int.class });
 1125   
 1126               code.dup();
 1127               code.aload().setParam(0);
 1128               code.checkcast().setType(Calendar.class);
 1129               code.invokevirtual().setMethod(Calendar.class, 
 1130                   "getMinimalDaysInFirstWeek", int.class, null);
 1131               code.invokevirtual().setMethod(type, "setMinimalDaysInFirstWeek", 
 1132                   void.class, new Class[] { int.class });
 1133   
 1134               code.dup();
 1135               code.aload().setParam(0);
 1136               code.checkcast().setType(Calendar.class);
 1137               code.invokevirtual().setMethod(Calendar.class, "getTimeZone", 
 1138                   TimeZone.class, null);
 1139               code.invokevirtual().setMethod(type, "setTimeZone", void.class,
 1140                   new Class[] { TimeZone.class });
 1141           }
 1142           code.areturn();
 1143           code.calculateMaxStack();
 1144           code.calculateMaxLocals();
 1145   
 1146           // new instance factory
 1147           m = bc.declareMethod("newInstance", ProxyCalendar.class, null); 
 1148           m.makePublic();
 1149           code = m.getCode(true);
 1150           code.anew().setType(bc); 
 1151           code.dup();
 1152           code.invokespecial().setMethod("<init>", void.class, null);
 1153           code.areturn();
 1154           code.calculateMaxStack();
 1155           code.calculateMaxLocals();
 1156   
 1157           // proxy the protected computeFields method b/c it is called on
 1158           // mutate, and some setters are final and therefore not proxyable
 1159           m = bc.declareMethod("computeFields", void.class, null);
 1160           m.makeProtected();
 1161           code = m.getCode(true);
 1162           code.aload().setThis();
 1163           code.constant().setValue(true);
 1164           code.invokestatic().setMethod(Proxies.class, "dirty", void.class,
 1165               new Class[] { Proxy.class, boolean.class });
 1166           code.aload().setThis();
 1167           code.invokespecial().setMethod(type, "computeFields", void.class, null);
 1168           code.vreturn();
 1169           code.calculateMaxStack();
 1170           code.calculateMaxLocals();
 1171       }
 1172   
 1173       /**
 1174        * Implement the methods in the {@link ProxyBean} interface.
 1175        */
 1176       private void addProxyBeanMethods(BCClass bc, Class type, Constructor cons) {
 1177           // bean copy
 1178           BCMethod m = bc.declareMethod("copy", Object.class, 
 1179               new Class[] { Object.class });
 1180           m.makePublic();
 1181           Code code = m.getCode(true);
 1182   
 1183           code.anew().setType(type);
 1184           code.dup();
 1185           Class[] params = cons.getParameterTypes();
 1186           if (params.length == 1) {
 1187               code.aload().setParam(0);
 1188               code.checkcast().setType(params[0]);
 1189           }
 1190           code.invokespecial().setMethod(cons);
 1191           if (params.length == 0)
 1192               copyProperties(type, code);
 1193           code.areturn();
 1194           code.calculateMaxStack();
 1195           code.calculateMaxLocals();
 1196   
 1197           // new instance factory
 1198           m = bc.declareMethod("newInstance", ProxyBean.class, 
 1199               new Class[] { Object.class }); 
 1200           m.makePublic();
 1201           code = m.getCode(true);
 1202           code.anew().setType(bc); 
 1203           code.dup();
 1204           if (params.length == 1) {
 1205               code.aload().setParam(0);
 1206               code.checkcast().setType(params[0]);
 1207           }
 1208           code.invokespecial().setMethod("<init>", void.class, params);
 1209           if (params.length == 0)
 1210               copyProperties(type, code);
 1211           code.areturn();
 1212           code.calculateMaxStack();
 1213           code.calculateMaxLocals();
 1214       }
 1215   
 1216       /**
 1217        * Copy bean properties.  Called with the copy object on the stack.  Must
 1218        * return with the copy object on the stack.
 1219        */
 1220       private void copyProperties(Class type, Code code) {
 1221           int copy = code.getNextLocalsIndex();
 1222           code.astore().setLocal(copy);
 1223           
 1224           Method[] meths = type.getMethods();
 1225           Method getter;
 1226           int mods;
 1227           for (int i = 0; i < meths.length; i++) {
 1228               mods = meths[i].getModifiers(); 
 1229               if (!Modifier.isPublic(mods) || Modifier.isStatic(mods))
 1230                   continue;
 1231               if (!startsWith(meths[i].getName(), "set")
 1232                   || meths[i].getParameterTypes().length != 1)
 1233                   continue;
 1234               getter = findGetter(type, meths[i]);
 1235               if (getter == null)
 1236                   continue;
 1237   
 1238               // copy.setXXX(orig.getXXX());
 1239               code.aload().setLocal(copy);
 1240               code.aload().setParam(0);
 1241               code.checkcast().setType(type);
 1242               code.invokevirtual().setMethod(getter);
 1243               code.invokevirtual().setMethod(meths[i]);
 1244           }
 1245           code.aload().setLocal(copy);
 1246       }
 1247   
 1248       /**
 1249        * Proxy recognized methods to invoke helpers in given helper class.
 1250        */
 1251       private void proxyRecognizedMethods(BCClass bc, Class type, Class helper,
 1252           Class proxyType) {
 1253           Method[] meths = type.getMethods();
 1254           Class[] params;
 1255           Class[] afterParams;
 1256           Method match;
 1257           Method after;
 1258           for (int i = 0; i < meths.length; i++) {
 1259               params = toHelperParameters(meths[i].getParameterTypes(), 
 1260                   proxyType);
 1261   
 1262               // first check for overriding method 
 1263               try {
 1264                   match = helper.getMethod(meths[i].getName(), params); 
 1265                   proxyOverrideMethod(bc, meths[i], match, params);
 1266                   continue;
 1267               } catch (NoSuchMethodException nsme) {
 1268               } catch (Exception e) {
 1269                   throw new GeneralException(e);
 1270               }
 1271   
 1272               // check for before and after methods, either of which may not
 1273               // exist
 1274               match = null;
 1275               try {
 1276                   match = helper.getMethod("before" 
 1277                       + StringUtils.capitalize(meths[i].getName()), params);
 1278               } catch (NoSuchMethodException nsme) {
 1279               } catch (Exception e) {
 1280                   throw new GeneralException(e);
 1281               }
 1282               after = null;
 1283               afterParams = null;
 1284               try {
 1285                   afterParams = toHelperAfterParameters(params, 
 1286                       meths[i].getReturnType(), (match == null) 
 1287                       ? void.class : match.getReturnType());
 1288                   after = helper.getMethod("after" 
 1289                       + StringUtils.capitalize(meths[i].getName()), afterParams);
 1290               } catch (NoSuchMethodException nsme) {
 1291               } catch (Exception e) {
 1292                   throw new GeneralException(e);
 1293               }
 1294               if (match != null || after != null)
 1295                   proxyBeforeAfterMethod(bc, type, meths[i], match, params, after,
 1296                       afterParams);
 1297           }
 1298       }
 1299   
 1300       /**
 1301        * Return the parameter types to the corresponding helper class method.
 1302        */ 
 1303       private static Class[] toHelperParameters(Class[] cls, Class helper) {
 1304           Class[] params = new Class[cls.length + 1];
 1305           params[0] = helper;
 1306           System.arraycopy(cls, 0, params, 1, cls.length); 
 1307           return params;
 1308       }
 1309   
 1310       /**
 1311        * Return the parameter types to the corresponding helper class "after"     
 1312        * method.
 1313        */
 1314       private static Class[] toHelperAfterParameters(Class[] cls, Class ret,
 1315           Class beforeRet) {
 1316           if (ret == void.class && beforeRet == void.class)
 1317               return cls;
 1318           int len = cls.length;
 1319           if (ret != void.class)
 1320               len++;
 1321           if (beforeRet != void.class)
 1322               len++;
 1323           Class[] params = new Class[len];
 1324           System.arraycopy(cls, 0, params, 0, cls.length);
 1325           int pos = cls.length;
 1326           if (ret != void.class)
 1327               params[pos++] = ret;
 1328           if (beforeRet != void.class)
 1329               params[pos++] = beforeRet;
 1330           return params;
 1331       }
 1332   
 1333       /**
 1334        * Proxy setter methods of the given type.
 1335        * 
 1336        * @return true if we find any setters, false otherwise
 1337        */
 1338       private boolean proxySetters(BCClass bc, Class type) {
 1339           Method[] meths = type.getMethods();
 1340           int setters = 0;
 1341           for (int i = 0; i < meths.length; i++) {
 1342               if (isSetter(meths[i]) && !Modifier.isFinal(meths[i].getModifiers())
 1343                   && bc.getDeclaredMethod(meths[i].getName(),
 1344                   meths[i].getParameterTypes()) == null) {
 1345                   setters++;
 1346                   proxySetter(bc, type, meths[i]);
 1347               }
 1348           } 
 1349           return setters > 0;
 1350       }
 1351   
 1352       /**
 1353        * Proxy the given method with one that overrides it by calling into the
 1354        * given helper.
 1355        */
 1356       private void proxyOverrideMethod(BCClass bc, Method meth, 
 1357           Method helper, Class[] params) {
 1358           BCMethod m = bc.declareMethod(meth.getName(), meth.getReturnType(),
 1359               meth.getParameterTypes());
 1360           m.makePublic();
 1361           Code code = m.getCode(true);
 1362   
 1363           code.aload().setThis();
 1364           for (int i = 1; i < params.length; i++)
 1365               code.xload().setParam(i - 1).setType(params[i]);
 1366           code.invokestatic().setMethod(helper);
 1367           code.xreturn().setType(meth.getReturnType());
 1368   
 1369           code.calculateMaxStack();
 1370           code.calculateMaxLocals();
 1371       }
 1372   
 1373       /**
 1374        * Proxy the given method with one that overrides it by calling into the
 1375        * given helper.
 1376        */
 1377       private void proxyBeforeAfterMethod(BCClass bc, Class type, Method meth, 
 1378           Method before, Class[] params, Method after, Class[] afterParams) {
 1379           BCMethod m = bc.declareMethod(meth.getName(), meth.getReturnType(),
 1380               meth.getParameterTypes());
 1381           m.makePublic();
 1382           Code code = m.getCode(true);
 1383   
 1384           // invoke before
 1385           int beforeRet = -1;
 1386           if (before != null) {
 1387               code.aload().setThis();
 1388               for (int i = 1; i < params.length; i++)
 1389                   code.xload().setParam(i - 1).setType(params[i]);
 1390               code.invokestatic().setMethod(before);
 1391               if (after != null && before.getReturnType() != void.class) {
 1392                   beforeRet = code.getNextLocalsIndex();
 1393                   code.xstore().setLocal(beforeRet).
 1394                       setType(before.getReturnType());
 1395               }
 1396           }
 1397   
 1398           // invoke super
 1399           code.aload().setThis();
 1400           for (int i = 1; i < params.length; i++)
 1401               code.xload().setParam(i - 1).setType(params[i]);
 1402           code.invokespecial().setMethod(type, meth.getName(), 
 1403               meth.getReturnType(), meth.getParameterTypes());
 1404   
 1405           // invoke after 
 1406           if (after != null) {
 1407               int ret = -1;
 1408               if (meth.getReturnType() != void.class) {
 1409                   ret = code.getNextLocalsIndex();
 1410                   code.xstore().setLocal(ret).setType(meth.getReturnType());
 1411               }
 1412               code.aload().setThis();
 1413               for (int i = 1; i < params.length; i++)
 1414                   code.xload().setParam(i - 1).setType(params[i]);
 1415               if (ret != -1)
 1416                   code.xload().setLocal(ret).setType(meth.getReturnType());
 1417               if (beforeRet != -1)
 1418                   code.xload().setLocal(beforeRet).
 1419                       setType(before.getReturnType());
 1420               code.invokestatic().setMethod(after);
 1421           }
 1422           code.xreturn().setType(meth.getReturnType());
 1423   
 1424           code.calculateMaxStack();
 1425           code.calculateMaxLocals();
 1426       }
 1427   
 1428       /**
 1429        * Return whether the given method is a setter.
 1430        */
 1431       protected boolean isSetter(Method meth) {
 1432           return startsWith(meth.getName(), "set")
 1433               || startsWith(meth.getName(), "add")
 1434               || startsWith(meth.getName(), "remove")
 1435               || startsWith(meth.getName(), "insert")
 1436               || startsWith(meth.getName(), "clear")
 1437               || startsWith(meth.getName(), "roll"); // used by Calendar
 1438       }
 1439   
 1440       /**
 1441        * Return the getter corresponding to the given setter, or null.
 1442        */
 1443       protected Method findGetter(Class type, Method setter) {
 1444           String name = setter.getName().substring(3);
 1445           Class param = setter.getParameterTypes()[0];
 1446           Method getter;
 1447           try {
 1448               getter = type.getMethod("get" + name, (Class[]) null);   
 1449               if (getter.getReturnType().isAssignableFrom(param)
 1450                   || param.isAssignableFrom(getter.getReturnType()))
 1451                   return getter;
 1452           } catch (NoSuchMethodException nsme) {
 1453           } catch (Exception e) {
 1454               throw new GeneralException(e);
 1455           }
 1456   
 1457           if (param == boolean.class || param == Boolean.class) {
 1458               try {
 1459                   getter = type.getMethod("is" + name, (Class[]) null);   
 1460                   if (getter.getReturnType().isAssignableFrom(param)
 1461                       || param.isAssignableFrom(getter.getReturnType()))
 1462                       return getter;
 1463               } catch (NoSuchMethodException nsme) {
 1464               } catch (Exception e) {
 1465                   throw new GeneralException(e);
 1466               }
 1467           }
 1468           return null;
 1469       }
 1470   
 1471       /**
 1472        * Return whether the target string stars with the given token.
 1473        */
 1474       private static boolean startsWith(String str, String token) {
 1475           return str.startsWith(token)
 1476               && (str.length() == token.length() 
 1477               || Character.isUpperCase(str.charAt(token.length())));
 1478       }
 1479   
 1480       /**
 1481        * Proxy the given setter method to dirty the proxy owner.
 1482        */
 1483       private void proxySetter(BCClass bc, Class type, Method meth) {
 1484           Class[] params = meth.getParameterTypes();
 1485           Class ret = meth.getReturnType();
 1486           BCMethod m = bc.declareMethod(meth.getName(), ret, params);
 1487           m.makePublic();
 1488           Code code = m.getCode(true);
 1489           code.aload().setThis();
 1490           code.constant().setValue(true);
 1491           code.invokestatic().setMethod(Proxies.class, "dirty", void.class,
 1492               new Class[] { Proxy.class, boolean.class });
 1493           code.aload().setThis();
 1494           for (int i = 0; i < params.length; i++)
 1495               code.xload().setParam(i).setType(params[i]);
 1496           code.invokespecial().setMethod(type, meth.getName(), ret, params);
 1497           code.xreturn().setType(ret);
 1498           code.calculateMaxStack();
 1499           code.calculateMaxLocals();
 1500       }
 1501   
 1502       /**
 1503        * Add a writeReplace implementation that serializes to a non-proxy type
 1504        * unless detached and this is a build-time generated class.
 1505        */
 1506       private void addWriteReplaceMethod(BCClass bc, boolean runtime) {
 1507           BCMethod m = bc.declareMethod("writeReplace", Object.class, null);
 1508           m.makeProtected();
 1509           m.getExceptions(true).addException(ObjectStreamException.class);
 1510           Code code = m.getCode(true);
 1511           code.aload().setThis();
 1512           code.constant().setValue(!runtime);
 1513           code.invokestatic().setMethod(Proxies.class, "writeReplace", 
 1514               Object.class, new Class[] { Proxy.class, boolean.class });
 1515           code.areturn();
 1516           code.calculateMaxLocals();
 1517           code.calculateMaxStack();
 1518       }
 1519   
 1520       /**
 1521        * Create a unique id to avoid proxy class name conflicts.
 1522        */
 1523       private static synchronized long nextProxyId() {
 1524           return _proxyId++;
 1525       }
 1526   
 1527       /**
 1528        * Find an appropriate copy constructor for the given type, or return null
 1529        * if none.
 1530        */
 1531       protected Constructor findCopyConstructor(Class cls) {
 1532           Constructor[] cons = cls.getConstructors();
 1533           Constructor match = null;
 1534           Class matchParam = null;
 1535           Class[] params;
 1536           for (int i = 0; i < cons.length; i++) {
 1537               params = cons[i].getParameterTypes();
 1538               if (params.length != 1)
 1539                   continue;
 1540   
 1541               // quit immediately on exact match
 1542               if (params[0] == cls)
 1543                   return cons[i];
 1544   
 1545               if (params[0].isAssignableFrom(cls) && (matchParam == null
 1546                   || matchParam.isAssignableFrom(params[0]))) {
 1547                    // track most derived collection constructor
 1548                   match = cons[i];
 1549                   matchParam = params[0];
 1550               }
 1551           }
 1552           return match; 
 1553       }
 1554   
 1555       /**
 1556        * Return the constructor that takes a comparator for the given type, or
 1557        * null if none.
 1558        */
 1559       private static Constructor findComparatorConstructor(Class cls) {
 1560           try {
 1561               return cls.getConstructor(new Class[] { Comparator.class });
 1562           } catch (NoSuchMethodException nsme) {
 1563               return null;
 1564           } catch (Exception e) {
 1565               throw new GeneralException(e);
 1566           }
 1567       }
 1568   
 1569       /**
 1570        * Usage: java org.apache.openjpa.util.proxy.ProxyManagerImpl [option]*
 1571        * &lt;class name&gt;+<br />
 1572        * Where the following options are recognized:
 1573        * <ul> 
 1574        * <li><i>-utils/-u &lt;number&gt;</i>: Generate proxies for the standard
 1575        * java.util collection, map, date, and calendar classes of the given Java
 1576        * version.  Use 4 for Java 1.4, 5 for Java 5, etc.</li>
 1577        * </ul>
 1578        *
 1579        * The main method generates .class files for the proxies to the classes    
 1580        * given on the command line.  It writes the generated classes to beside the
 1581        * ProxyManagerImpl.class file if possible; otherwise it writes to the 
 1582        * current directory.  The proxy manager looks for these classes 
 1583        * before generating its own proxies at runtime.
 1584        */
 1585       public static void main(String[] args) 
 1586           throws ClassNotFoundException, IOException {
 1587           File dir = Files.getClassFile(ProxyManagerImpl.class);
 1588           dir = (dir == null) ? new File((String) AccessController.doPrivileged(
 1589               J2DoPrivHelper.getPropertyAction("user.dir")))
 1590               : dir.getParentFile();
 1591   
 1592           Options opts = new Options();
 1593           args = opts.setFromCmdLine(args);
 1594   
 1595           List types = new ArrayList();
 1596           types.addAll(Arrays.asList(args));
 1597           int utils = opts.removeIntProperty("utils", "u", 0);
 1598           if (utils >= 4) {
 1599               types.addAll(Arrays.asList(new String[] {
 1600                   java.sql.Date.class.getName(),
 1601                   java.sql.Time.class.getName(),
 1602                   java.sql.Timestamp.class.getName(),
 1603                   java.util.ArrayList.class.getName(),
 1604                   java.util.Date.class.getName(),
 1605                   java.util.GregorianCalendar.class.getName(),
 1606                   java.util.HashMap.class.getName(),
 1607                   java.util.HashSet.class.getName(),
 1608                   java.util.Hashtable.class.getName(),
 1609                   java.util.LinkedList.class.getName(),
 1610                   java.util.Properties.class.getName(),
 1611                   java.util.TreeMap.class.getName(),
 1612                   java.util.TreeSet.class.getName(),
 1613                   java.util.Vector.class.getName(),
 1614               })); 
 1615           }
 1616           if (utils >= 5) {
 1617               types.addAll(Arrays.asList(new String[] {
 1618                   "java.util.EnumMap",
 1619                   "java.util.IdentityHashMap",
 1620                   "java.util.LinkedHashMap",
 1621                   "java.util.LinkedHashSet",
 1622                   "java.util.PriorityQueue",
 1623               })); 
 1624           }
 1625   
 1626           final ProxyManagerImpl mgr = new ProxyManagerImpl();
 1627           Class cls;
 1628           BCClass bc;
 1629           for (int i = 0; i < types.size(); i++) {
 1630               cls = Class.forName((String) types.get(i));
 1631               try {
 1632                   if (Class.forName(getProxyClassName(cls, false), true,
 1633                       GeneratedClasses.getMostDerivedLoader(cls, Proxy.class))
 1634                       != null)
 1635                       continue;
 1636               } catch (Throwable t) {
 1637                   // expected if the class hasn't been generated
 1638               }
 1639   
 1640               if (Collection.class.isAssignableFrom(cls))
 1641                   bc = mgr.generateProxyCollectionBytecode(cls, false);         
 1642               else if (Map.class.isAssignableFrom(cls))
 1643                   bc = mgr.generateProxyMapBytecode(cls, false);         
 1644               else if (Date.class.isAssignableFrom(cls))
 1645                   bc = mgr.generateProxyDateBytecode(cls, false);
 1646               else if (Calendar.class.isAssignableFrom(cls))
 1647                   bc = mgr.generateProxyCalendarBytecode(cls, false);
 1648               else {
 1649                   final Class fCls = cls;
 1650                   bc = (BCClass) AccessController
 1651                       .doPrivileged(new PrivilegedAction() {
 1652                           public Object run() {
 1653                               return mgr.generateProxyBeanBytecode(fCls, false);
 1654                           }
 1655                       });
 1656               }
 1657   
 1658               System.out.println(bc.getName());
 1659               bc.write(new File(dir, bc.getClassName() + ".class"));
 1660           }
 1661       }
 1662   }

Save This Page
Home » apache-openjpa-1.1.0-source » org.apache.openjpa » util » [javadoc | source]