Home » apache-openjpa-1.1.0-source » org.apache.openjpa » enhance » [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.enhance;
   20   
   21   import java.io.File;
   22   import java.io.IOException;
   23   import java.util.ArrayList;
   24   import java.util.Collection;
   25   import java.util.Collections;
   26   import java.util.HashMap;
   27   import java.util.HashSet;
   28   import java.util.Iterator;
   29   import java.util.List;
   30   import java.util.Map;
   31   import java.util.Set;
   32   
   33   import org.apache.openjpa.conf.OpenJPAConfiguration;
   34   import org.apache.openjpa.lib.log.Log;
   35   import org.apache.openjpa.lib.util.BytecodeWriter;
   36   import org.apache.openjpa.lib.util.JavaVersions;
   37   import org.apache.openjpa.lib.util.Localizer;
   38   import org.apache.openjpa.lib.util.Files;
   39   import org.apache.openjpa.lib.util.Localizer.Message;
   40   import org.apache.openjpa.meta.ClassMetaData;
   41   import org.apache.openjpa.meta.FieldMetaData;
   42   import org.apache.openjpa.meta.JavaTypes;
   43   import org.apache.openjpa.util.GeneratedClasses;
   44   import org.apache.openjpa.util.ImplHelper;
   45   import org.apache.openjpa.util.InternalException;
   46   import org.apache.openjpa.util.UserException;
   47   import serp.bytecode.BCClass;
   48   
   49   /**
   50    * Redefines the method bodies of existing unenhanced classes to make them
   51    * notify state managers of mutations.
   52    *
   53    * @since 1.0.0
   54    */
   55   public class ManagedClassSubclasser {
   56       private static final Localizer _loc = Localizer.forPackage(
   57           ManagedClassSubclasser.class);
   58   
   59       /**
   60        * For each element in <code>classes</code>, creates and registers a
   61        * new subclass that implements {@link PersistenceCapable}, and prepares
   62        * OpenJPA to handle new instances of the unenhanced type. If this is
   63        * invoked in a Java 6 environment, this method will redefine the methods
   64        * for each class in the argument list such that field accesses are
   65        * intercepted in-line. If invoked in a Java 5 environment, this
   66        * redefinition is not possible; in these contexts, when using field
   67        * access, OpenJPA will need to do state comparisons to detect any change
   68        * to any instance at any time, and when using property access, OpenJPA
   69        * will need to do state comparisons to detect changes to newly inserted
   70        * instances after a flush has been called.
   71        *
   72        * @return the new subclasses, or <code>null</code> if <code>classes</code>
   73        * is <code>null</code>.
   74        * @throws UserException if <code>conf</code> requires build-time
   75        * enhancement and <code>classes</code> includes unenhanced types.
   76        *
   77        * @since 1.0.0
   78        */
   79       public static List<Class> prepareUnenhancedClasses(
   80           final OpenJPAConfiguration conf,
   81           final Collection<? extends Class> classes,
   82           final ClassLoader envLoader) {
   83           if (classes == null)
   84               return null;
   85           if (classes.size() == 0)
   86               return Collections.EMPTY_LIST;
   87   
   88           Log log = conf.getLog(OpenJPAConfiguration.LOG_ENHANCE);
   89           if (conf.getRuntimeUnenhancedClassesConstant()
   90               != RuntimeUnenhancedClasssesModes.SUPPORTED) {
   91               Collection unenhanced = new ArrayList();
   92               for (Class cls : classes)
   93                   if (!PersistenceCapable.class.isAssignableFrom(cls))
   94                       unenhanced.add(cls);
   95               if (unenhanced.size() > 0) {
   96                   Message msg = _loc.get("runtime-optimization-disabled",
   97                       unenhanced);
   98                   if (conf.getRuntimeUnenhancedClassesConstant()
   99                       == RuntimeUnenhancedClasssesModes.WARN)
  100                       log.warn(msg);
  101                   else
  102                       throw new UserException(msg);
  103               }
  104               return null;
  105           }
  106   
  107           boolean redefine = ClassRedefiner.canRedefineClasses();
  108           if (redefine)
  109               log.info(_loc.get("enhance-and-subclass-and-redef-start",
  110                   classes));
  111           else
  112               log.info(_loc.get("enhance-and-subclass-no-redef-start",
  113                   classes));
  114   
  115           final Map<Class, byte[]> map = new HashMap<Class, byte[]>();
  116           final List subs = new ArrayList(classes.size());
  117           final List ints = new ArrayList(classes.size());
  118           Set<Class> unspecified = null;
  119           for (Iterator iter = classes.iterator(); iter.hasNext(); ) {
  120               final Class cls = (Class) iter.next();
  121               final PCEnhancer enhancer = new PCEnhancer(conf, cls); 
  122   
  123               enhancer.setBytecodeWriter(new BytecodeWriter() {
  124                   public void write(BCClass bc) throws IOException {
  125                       ManagedClassSubclasser.write(bc, enhancer, map,
  126                           cls, subs, ints);
  127                   }
  128               });
  129               if (redefine)
  130                   enhancer.setRedefine(true);
  131               enhancer.setCreateSubclass(true);
  132               enhancer.setAddDefaultConstructor(true);
  133   
  134               // set this before enhancement as well as after since enhancement
  135               // uses a different metadata repository, and the metadata config
  136               // matters in the enhancement contract. Don't do any warning here,
  137               // since we'll issue warnings when we do the final metadata
  138               // reconfiguration at the end of this method.
  139               configureMetaData(enhancer.getMetaData(), conf, redefine, false);
  140   
  141               unspecified = collectRelatedUnspecifiedTypes(enhancer.getMetaData(),
  142                   classes, unspecified);
  143   
  144               int runResult = enhancer.run();
  145               if (runResult == PCEnhancer.ENHANCE_PC) {
  146                   try {
  147                       enhancer.record();
  148                   } catch (IOException e) {
  149                       // our impl of BytecodeWriter doesn't throw IOException
  150                       throw new InternalException(e);
  151                   }
  152               }
  153           }
  154   
  155           if (unspecified != null && !unspecified.isEmpty())
  156               throw new UserException(_loc.get("unspecified-unenhanced-types",
  157                   classes, unspecified));
  158   
  159           ClassRedefiner.redefineClasses(conf, map);
  160           for (Class cls : map.keySet()) {
  161               setIntercepting(conf, envLoader, cls);
  162               configureMetaData(conf, envLoader, cls, redefine);
  163           }
  164           for (Class cls : (Collection<Class>) subs)
  165               configureMetaData(conf, envLoader, cls, redefine);
  166           for (Class cls : (Collection<Class>) ints)
  167               setIntercepting(conf, envLoader, cls);
  168   
  169           return subs;
  170       }
  171   
  172       private static Set<Class> collectRelatedUnspecifiedTypes(ClassMetaData meta,
  173           Collection<? extends Class> classes, Set<Class> unspecified) {
  174           unspecified = collectUnspecifiedType(meta.getPCSuperclass(), classes,
  175               unspecified);
  176   
  177           for (FieldMetaData fmd : meta.getFields()) {
  178               if (fmd.isTransient())
  179                   continue;
  180               if (fmd.isTypePC())
  181                   unspecified = collectUnspecifiedType(fmd.getType(), classes,
  182                       unspecified);
  183               if (fmd.getElement() != null && fmd.getElement().isTypePC())
  184                   unspecified = collectUnspecifiedType(fmd.getElement().getType(),
  185                       classes, unspecified);
  186               if (fmd.getKey() != null && fmd.getKey().isTypePC())
  187                   unspecified = collectUnspecifiedType(fmd.getKey().getType(),
  188                       classes, unspecified);
  189               if (fmd.getValue() != null && fmd.getValue().isTypePC())
  190                   unspecified = collectUnspecifiedType(fmd.getValue().getType(),
  191                       classes, unspecified);
  192           }
  193           return unspecified;
  194       }
  195   
  196       private static Set<Class> collectUnspecifiedType(Class cls,
  197           Collection<? extends Class> classes, Set<Class> unspecified) {
  198           if (cls != null && !classes.contains(cls)
  199               && !ImplHelper.isManagedType(null, cls)) {
  200               if (unspecified == null)
  201                   unspecified = new HashSet<Class>();
  202               unspecified.add(cls);
  203           }
  204           return unspecified;
  205       }
  206   
  207       private static void configureMetaData(OpenJPAConfiguration conf,
  208           ClassLoader envLoader, Class cls, boolean redefineAvailable) {
  209           ClassMetaData meta = conf.getMetaDataRepositoryInstance()
  210               .getMetaData(cls, envLoader, true);
  211           configureMetaData(meta, conf, redefineAvailable, true);
  212       }
  213   
  214       private static void configureMetaData(ClassMetaData meta,
  215           OpenJPAConfiguration conf, boolean redefineAvailable, boolean warn) {
  216   
  217           setDetachedState(meta);
  218   
  219           if (warn && meta.getAccessType() == ClassMetaData.ACCESS_FIELD
  220               && !redefineAvailable) {
  221               // only warn about declared fields; superclass fields will be
  222               // warned about when the superclass is handled
  223               for (FieldMetaData fmd : meta.getDeclaredFields()) {
  224                   switch (fmd.getTypeCode()) {
  225                       case JavaTypes.COLLECTION:
  226                       case JavaTypes.MAP:
  227                           // we can lazily load these, since we own the
  228                           // relationship container
  229                           break;
  230                       default:
  231                           if (!fmd.isInDefaultFetchGroup()
  232                               && !(fmd.isVersion() || fmd.isPrimaryKey())) {
  233                               Log log = conf.getLog(
  234                                   OpenJPAConfiguration.LOG_ENHANCE);
  235                               log.warn(_loc.get("subclasser-fetch-group-override",
  236                                   meta.getDescribedType().getName(),
  237                                   fmd.getName()));
  238                               fmd.setInDefaultFetchGroup(true);
  239                           }
  240                   }
  241               }
  242           }
  243       }
  244   
  245       private static void write(BCClass bc, PCEnhancer enhancer,
  246           Map<Class, byte[]> map, Class cls, List subs, List ints)
  247           throws IOException {
  248   
  249           if (bc == enhancer.getManagedTypeBytecode()) {
  250               // if it was already defined, don't put it in the map,
  251               // but do set the metadata accordingly.
  252               if (enhancer.isAlreadyRedefined())
  253                   ints.add(bc.getType());
  254               else if (JavaVersions.VERSION >= 5) {
  255                   map.put(bc.getType(), bc.toByteArray());
  256                   debugBytecodes(bc);
  257               }
  258           } else {
  259               if (!enhancer.isAlreadySubclassed()) {
  260                   debugBytecodes(bc);
  261                   
  262                   // this is the new subclass
  263                   ClassLoader loader = GeneratedClasses.getMostDerivedLoader(
  264                       cls, PersistenceCapable.class);
  265                   subs.add(GeneratedClasses.loadBCClass(bc, loader));
  266               }
  267           }
  268       }
  269   
  270       private static void debugBytecodes(BCClass bc) throws IOException {
  271           // Write the bytecodes to disk for debugging purposes.
  272           if ("true".equals(System.getProperty(
  273               ManagedClassSubclasser.class.getName() + ".dumpBytecodes")))
  274           {
  275               File tmp = new File(System.getProperty("java.io.tmpdir"));
  276               File dir = new File(tmp, "openjpa");
  277               dir = new File(dir, "pcsubclasses");
  278               dir.mkdirs();
  279               dir = Files.getPackageFile(dir, bc.getPackageName(), true);
  280               File f = new File(dir, bc.getClassName() + ".class");
  281               System.err.println("Writing to " + f);
  282               bc.write(f);
  283           }
  284       }
  285   
  286       private static void setIntercepting(OpenJPAConfiguration conf,
  287           ClassLoader envLoader, Class cls) {
  288           ClassMetaData meta = conf.getMetaDataRepositoryInstance()
  289               .getMetaData(cls, envLoader, true);
  290           meta.setIntercepting(true);
  291       }
  292   
  293       /**
  294        * If the metadata is configured to use a synthetic
  295        * detached state, reset it to not use a detached
  296        * state field, since we can't add fields when redefining.
  297        */
  298       private static void setDetachedState(ClassMetaData meta) {
  299           if (ClassMetaData.SYNTHETIC.equals(meta.getDetachedState()))
  300               meta.setDetachedState(null);
  301       }
  302   }

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