ClassLoader.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  *  Unless required by applicable law or agreed to in writing, software
  12.  *  distributed under the License is distributed on an "AS IS" BASIS,
  13.  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  *  See the License for the specific language governing permissions and
  15.  *  limitations under the License.
  16.  */
  17. package org.apache.bcel.util;

  18. import java.io.ByteArrayInputStream;
  19. import java.io.IOException;
  20. import java.util.Hashtable;

  21. import org.apache.bcel.Const;
  22. import org.apache.bcel.classfile.ClassParser;
  23. import org.apache.bcel.classfile.ConstantClass;
  24. import org.apache.bcel.classfile.ConstantPool;
  25. import org.apache.bcel.classfile.ConstantUtf8;
  26. import org.apache.bcel.classfile.JavaClass;
  27. import org.apache.bcel.classfile.Utility;

  28. /**
  29.  * <p>
  30.  * Drop in replacement for the standard class loader of the JVM. You can use it in conjunction with the JavaWrapper to
  31.  * dynamically modify/create classes as they're requested.
  32.  * </p>
  33.  *
  34.  * <p>
  35.  * This class loader recognizes special requests in a distinct format, i.e., when the name of the requested class
  36.  * contains with "$$BCEL$$" it calls the createClass() method with that name (everything bevor the $$BCEL$$ is
  37.  * considered to be the package name. You can subclass the class loader and override that method. "Normal" classes class
  38.  * can be modified by overriding the modifyClass() method which is called just before defineClass().
  39.  * </p>
  40.  *
  41.  * <p>
  42.  * There may be a number of packages where you have to use the default class loader (which may also be faster). You can
  43.  * define the set of packages where to use the system class loader in the constructor. The default value contains
  44.  * "java.", "sun.", "javax."
  45.  * </p>
  46.  *
  47.  * @see JavaWrapper
  48.  * @see ClassPath
  49.  * @deprecated 6.0 Do not use - does not work
  50.  */
  51. @Deprecated
  52. public class ClassLoader extends java.lang.ClassLoader {

  53.     private static final String BCEL_TOKEN = "$$BCEL$$";

  54.     public static final String[] DEFAULT_IGNORED_PACKAGES = {"java.", "javax.", "sun."};

  55.     private final Hashtable<String, Class<?>> classes = new Hashtable<>();
  56.     // Hashtable is synchronized thus thread-safe
  57.     private final String[] ignoredPackages;
  58.     private Repository repository = SyntheticRepository.getInstance();

  59.     /**
  60.      * Ignored packages are by default ( "java.", "sun.", "javax."), i.e. loaded by system class loader
  61.      */
  62.     public ClassLoader() {
  63.         this(DEFAULT_IGNORED_PACKAGES);
  64.     }

  65.     /**
  66.      * @param deferTo delegate class loader to use for ignored packages
  67.      */
  68.     public ClassLoader(final java.lang.ClassLoader deferTo) {
  69.         super(deferTo);
  70.         this.ignoredPackages = DEFAULT_IGNORED_PACKAGES;
  71.         this.repository = new ClassLoaderRepository(deferTo);
  72.     }

  73.     /**
  74.      * @param ignoredPackages classes contained in these packages will be loaded with the system class loader
  75.      * @param deferTo delegate class loader to use for ignored packages
  76.      */
  77.     public ClassLoader(final java.lang.ClassLoader deferTo, final String[] ignoredPackages) {
  78.         this(ignoredPackages);
  79.         this.repository = new ClassLoaderRepository(deferTo);
  80.     }

  81.     /**
  82.      * @param ignoredPackages classes contained in these packages will be loaded with the system class loader
  83.      */
  84.     public ClassLoader(final String[] ignoredPackages) {
  85.         this.ignoredPackages = ignoredPackages;
  86.     }

  87.     /**
  88.      * Override this method to create you own classes on the fly. The name contains the special token $$BCEL$$. Everything
  89.      * before that token is considered to be a package name. You can encode your own arguments into the subsequent string.
  90.      * You must ensure however not to use any "illegal" characters, i.e., characters that may not appear in a Java class
  91.      * name too
  92.      * <p>
  93.      * The default implementation interprets the string as a encoded compressed Java class, unpacks and decodes it with the
  94.      * Utility.decode() method, and parses the resulting byte array and returns the resulting JavaClass object.
  95.      * </p>
  96.      *
  97.      * @param className compressed byte code with "$$BCEL$$" in it
  98.      */
  99.     protected JavaClass createClass(final String className) {
  100.         final int index = className.indexOf(BCEL_TOKEN);
  101.         final String realName = className.substring(index + BCEL_TOKEN.length());
  102.         JavaClass clazz = null;
  103.         try {
  104.             final byte[] bytes = Utility.decode(realName, true);
  105.             final ClassParser parser = new ClassParser(new ByteArrayInputStream(bytes), "foo");
  106.             clazz = parser.parse();
  107.         } catch (final IOException e) {
  108.             e.printStackTrace();
  109.             return null;
  110.         }
  111.         // Adapt the class name to the passed value
  112.         final ConstantPool cp = clazz.getConstantPool();
  113.         final ConstantClass cl = cp.getConstant(clazz.getClassNameIndex(), Const.CONSTANT_Class, ConstantClass.class);
  114.         final ConstantUtf8 name = cp.getConstantUtf8(cl.getNameIndex());
  115.         name.setBytes(Utility.packageToPath(className));
  116.         return clazz;
  117.     }

  118.     @Override
  119.     protected Class<?> loadClass(final String className, final boolean resolve) throws ClassNotFoundException {
  120.         Class<?> cl = null;
  121.         /*
  122.          * First try: lookup hash table.
  123.          */
  124.         if ((cl = classes.get(className)) == null) {
  125.             /*
  126.              * Second try: Load system class using system class loader. You better don't mess around with them.
  127.              */
  128.             for (final String ignoredPackage : ignoredPackages) {
  129.                 if (className.startsWith(ignoredPackage)) {
  130.                     cl = getParent().loadClass(className);
  131.                     break;
  132.                 }
  133.             }
  134.             if (cl == null) {
  135.                 JavaClass clazz = null;
  136.                 /*
  137.                  * Third try: Special request?
  138.                  */
  139.                 if (className.contains(BCEL_TOKEN)) {
  140.                     clazz = createClass(className);
  141.                 } else { // Fourth try: Load classes via repository
  142.                     if ((clazz = repository.loadClass(className)) == null) {
  143.                         throw new ClassNotFoundException(className);
  144.                     }
  145.                     clazz = modifyClass(clazz);
  146.                 }
  147.                 if (clazz != null) {
  148.                     final byte[] bytes = clazz.getBytes();
  149.                     cl = defineClass(className, bytes, 0, bytes.length);
  150.                 } else {
  151.                     cl = Class.forName(className);
  152.                 }
  153.             }
  154.             if (resolve) {
  155.                 resolveClass(cl);
  156.             }
  157.         }
  158.         classes.put(className, cl);
  159.         return cl;
  160.     }

  161.     /**
  162.      * Override this method if you want to alter a class before it gets actually loaded. Does nothing by default.
  163.      */
  164.     protected JavaClass modifyClass(final JavaClass clazz) {
  165.         return clazz;
  166.     }
  167. }