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.ByteArrayInputStream;
   22   import java.lang.instrument.ClassFileTransformer;
   23   import java.lang.instrument.IllegalClassFormatException;
   24   import java.security.ProtectionDomain;
   25   import java.util.Set;
   26   
   27   import org.apache.openjpa.conf.OpenJPAConfiguration;
   28   import org.apache.openjpa.lib.log.Log;
   29   import org.apache.openjpa.lib.util.Localizer;
   30   import org.apache.openjpa.lib.util.Options;
   31   import org.apache.openjpa.meta.MetaDataRepository;
   32   import org.apache.openjpa.util.GeneralException;
   33   import serp.bytecode.Project;
   34   import serp.bytecode.lowlevel.ConstantPoolTable;
   35   
   36   /**
   37    * Transformer that makes persistent classes implement the
   38    * {@link PersistenceCapable} interface at runtime.
   39    *
   40    * @author Abe White
   41    * @nojavadoc
   42    */
   43   public class PCClassFileTransformer
   44       implements ClassFileTransformer {
   45   
   46       private static final Localizer _loc = Localizer.forPackage
   47           (PCClassFileTransformer.class);
   48   
   49       private final MetaDataRepository _repos;
   50       private final PCEnhancer.Flags _flags;
   51       private final ClassLoader _tmpLoader;
   52       private final Log _log;
   53       private final Set _names;
   54       private boolean _transforming = false;
   55   
   56       /**
   57        * Constructor.
   58        *
   59        * @param repos metadata repository to use internally
   60        * @param opts enhancer configuration options
   61        * @param loader temporary class loader for loading intermediate classes
   62        */
   63       public PCClassFileTransformer(MetaDataRepository repos, Options opts,
   64           ClassLoader loader) {
   65           this(repos, toFlags(opts), loader, opts.removeBooleanProperty
   66               ("scanDevPath", "ScanDevPath", false));
   67       }
   68   
   69       /**
   70        * Create enhancer flags from the given options.
   71        */
   72       private static PCEnhancer.Flags toFlags(Options opts) {
   73           PCEnhancer.Flags flags = new PCEnhancer.Flags();
   74           flags.addDefaultConstructor = opts.removeBooleanProperty
   75               ("addDefaultConstructor", "AddDefaultConstructor",
   76                   flags.addDefaultConstructor);
   77           flags.enforcePropertyRestrictions = opts.removeBooleanProperty
   78               ("enforcePropertyRestrictions", "EnforcePropertyRestrictions",
   79                   flags.enforcePropertyRestrictions);
   80           return flags;
   81       }
   82   
   83       /**
   84        * Constructor.
   85        *
   86        * @param repos metadata repository to use internally
   87        * @param flags enhancer configuration
   88        * @param loader temporary class loader for loading intermediate classes
   89        * @param devscan whether to scan the dev classpath for persistent types
   90        * if none are configured
   91        */
   92       public PCClassFileTransformer(MetaDataRepository repos,
   93           PCEnhancer.Flags flags, ClassLoader tmpLoader, boolean devscan) {
   94           _repos = repos;
   95           _tmpLoader = tmpLoader;
   96   
   97           _log = repos.getConfiguration().
   98               getLog(OpenJPAConfiguration.LOG_ENHANCE);
   99           _flags = flags;
  100   
  101           _names = repos.getPersistentTypeNames(devscan, tmpLoader);
  102           if (_names == null && _log.isInfoEnabled())
  103               _log.info(_loc.get("runtime-enhance-pcclasses"));
  104       }
  105   
  106       public byte[] transform(ClassLoader loader, String className,
  107           Class redef, ProtectionDomain domain, byte[] bytes)
  108           throws IllegalClassFormatException {
  109           if (loader == _tmpLoader)
  110               return null;
  111   
  112           // prevent re-entrant calls, which can occur if the enhanceing
  113           // loader is used to also load OpenJPA libraries; this is to prevent 
  114           // recursive enhancement attempts for internal openjpa libraries
  115           if (_transforming)
  116               return null;
  117   
  118           _transforming = true;
  119   
  120           return transform0(className, redef, bytes);
  121       }
  122   
  123       /**
  124        * We have to split the transform method into two methods to avoid
  125        * ClassCircularityError when executing method using pure-JIT JVMs
  126        * such as JRockit.
  127        */
  128       private byte[] transform0(String className, Class redef, byte[] bytes)
  129           throws IllegalClassFormatException {
  130   
  131           try {
  132               Boolean enhance = needsEnhance(className, redef, bytes);
  133               if (enhance != null && _log.isTraceEnabled())
  134                   _log.trace(_loc.get("needs-runtime-enhance", className,
  135                       enhance));
  136               if (enhance != Boolean.TRUE)
  137                   return null;
  138   
  139               PCEnhancer enhancer = new PCEnhancer(_repos.getConfiguration(),
  140                   new Project().loadClass(new ByteArrayInputStream(bytes),
  141                       _tmpLoader), _repos);
  142               enhancer.setAddDefaultConstructor(_flags.addDefaultConstructor);
  143               enhancer.setEnforcePropertyRestrictions
  144                   (_flags.enforcePropertyRestrictions);
  145   
  146               if (enhancer.run() == PCEnhancer.ENHANCE_NONE)
  147                   return null;
  148               return enhancer.getPCBytecode().toByteArray();
  149           } catch (Throwable t) {
  150               _log.warn(_loc.get("cft-exception-thrown", className), t);
  151               if (t instanceof RuntimeException)
  152                   throw (RuntimeException) t;
  153               if (t instanceof IllegalClassFormatException)
  154                   throw (IllegalClassFormatException) t;
  155               throw new GeneralException(t);
  156           } finally {
  157               _transforming = false;
  158           }
  159       }
  160   
  161       /**
  162        * Return whether the given class needs enhancement.
  163        */
  164       private Boolean needsEnhance(String clsName, Class redef, byte[] bytes) {
  165           if (redef != null) {
  166               Class[] intfs = redef.getInterfaces();
  167               for (int i = 0; i < intfs.length; i++)
  168                   if (PersistenceCapable.class.getName().
  169                       equals(intfs[i].getName()))
  170                       return Boolean.valueOf(!isEnhanced(bytes));
  171               return null;
  172           }
  173   
  174           if (_names != null) {
  175               if (_names.contains(clsName.replace('/', '.')))
  176                   return Boolean.valueOf(!isEnhanced(bytes));
  177               return null;
  178           }
  179   
  180           if (clsName.startsWith("java/") || clsName.startsWith("javax/"))
  181               return null;
  182           if (isEnhanced(bytes))
  183               return Boolean.FALSE;
  184   
  185           try {
  186               Class c = Class.forName(clsName.replace('/', '.'), false,
  187                   _tmpLoader);
  188               if (_repos.getMetaData(c, null, false) != null)
  189                   return Boolean.TRUE;
  190               return null;
  191           } catch (ClassNotFoundException cnfe) {
  192               // cannot load the class: this might mean that it is a proxy
  193               // or otherwise inaccessible class which can't be an entity
  194               return Boolean.FALSE;
  195           } catch (LinkageError cce) {
  196               // this can happen if we are loading classes that this
  197               // class depends on; these will never be enhanced anyway
  198               return Boolean.FALSE;
  199           } catch (RuntimeException re) {
  200               throw re;
  201           } catch (Throwable t) {
  202               throw new GeneralException(t);
  203           }
  204       }
  205   
  206       /**
  207        * Analyze the bytecode to see if the given class definition implements
  208        * {@link PersistenceCapable}.
  209        */
  210       private static boolean isEnhanced(byte[] b) {
  211           ConstantPoolTable table = new ConstantPoolTable(b);
  212           int idx = table.getEndIndex();
  213   
  214           idx += 6; // skip access, cls, super
  215           int ifaces = table.readUnsignedShort(idx);
  216           int clsEntry, utfEntry;
  217           String name;
  218           for (int i = 0; i < ifaces; i++) {
  219               idx += 2;
  220               clsEntry = table.readUnsignedShort(idx);
  221               utfEntry = table.readUnsignedShort(table.get(clsEntry));
  222               name = table.readString(table.get(utfEntry));
  223               if ("org/apache/openjpa/enhance/PersistenceCapable".equals(name))
  224                   return true;
  225           }
  226           return false;
  227       }
  228   }

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