View Javadoc

1   /*
2    * Copyright  2000-2004 The Apache Software Foundation
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License"); 
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License. 
15   *
16   */
17  package org.apache.bcel.util;
18  
19  import java.io.ByteArrayInputStream;
20  import java.util.Hashtable;
21  import org.apache.bcel.Constants;
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  /***
30   * <p>Drop in replacement for the standard class loader of the JVM. You can use it
31   * in conjunction with the JavaWrapper to dynamically modify/create classes
32   * as they're requested.</p>
33   *
34   * <p>This class loader recognizes special requests in a distinct
35   * format, i.e., when the name of the requested class contains with
36   * "$$BCEL$$" it calls the createClass() method with that name
37   * (everything bevor the $$BCEL$$ is considered to be the package
38   * name. You can subclass the class loader and override that
39   * method. "Normal" classes class can be modified by overriding the
40   * modifyClass() method which is called just before defineClass().</p>
41   *
42   * <p>There may be a number of packages where you have to use the
43   * default class loader (which may also be faster). You can define the
44   * set of packages where to use the system class loader in the
45   * constructor. The default value contains "java.", "sun.",
46   * "javax."</p>
47   *
48   * @version $Id: ClassLoader.java 386056 2006-03-15 11:31:56Z tcurdt $
49   * @author  <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
50   * @see JavaWrapper
51   * @see ClassPath
52   */
53  public class ClassLoader extends java.lang.ClassLoader {
54  
55      public static final String[] DEFAULT_IGNORED_PACKAGES = {
56              "java.", "javax.", "sun."
57      };
58      private Hashtable classes = new Hashtable(); // Hashtable is synchronized thus thread-safe
59      privateong> String[] ignored_packages;
60      private Repository repository = SyntheticRepository.getInstance();
61  
62  
63      /*** Ignored packages are by default ( "java.", "sun.",
64       * "javax."), i.e. loaded by system class loader
65       */
66      public ClassLoader() {
67          this(DEFAULT_IGNORED_PACKAGES);
68      }
69  
70  
71      /*** @param deferTo delegate class loader to use for ignored packages
72       */
73      public ClassLoader(java.lang.ClassLoader deferTo) {
74          super(deferTo);
75          this.ignored_packages = DEFAULT_IGNORED_PACKAGES;
76          this.repository = new ClassLoaderRepository(deferTo);
77      }
78  
79  
80      /*** @param ignored_packages classes contained in these packages will be loaded
81       * with the system class loader
82       */
83      publicClassLoader(String[] ignored_packages) {/package-summary.html">ong> ClassLoader(String[] ignored_packages) {
84          this.ignored_packages = ignored_packages;
85      }
86  
87  
88      /*** @param ignored_packages classes contained in these packages will be loaded
89       * with the system class loader
90       * @param deferTo delegate class loader to use for ignored packages
91       */
92      publicClassLoader(java/lang/ClassLoader deferTo, String[] ignored_packages) {/package-summary.html">ong> ClassLoader(java.lang.ClassLoader deferTo, String[] ignored_packages) {
93          this(ignored_packages);
94          this.repository = new ClassLoaderRepository(deferTo);
95      }
96  
97  
98      protected Class loadClass( String class_name, boolean resolve ) throws ClassNotFoundException {
99          Class cl = null;
100         /* First try: lookup hash table.
101          */
102         if ((cl = (Class) classes.get(class_name)) == null) {
103             /* Second try: Load system class using system class loader. You better
104              * don't mess around with them.
105              */
106             for (int i = 0; i < ignored_packages.length; i++) {
107                 rong>if (class_name.startsWith(ignored_packages[i])) {
108                     cl = getParent().loadClass(class_name);
109                     break;
110                 }
111             }
112             if (cl == null) {
113                 JavaClass clazz = null;
114                 /* Third try: Special request?
115                  */
116                 if (class_name.indexOf("$$BCEL$$") >= 0) {
117                     clazz = createClass(class_name);
118                 } else { // Fourth try: Load classes via repository
119                     if ((clazz = repository.loadClass(class_name)) != null) {
120                         clazz = modifyClass(clazz);
121                     } else {
122                         throw new ClassNotFoundException(class_name);
123                     }
124                 }
125                 if (clazz != null) {
126                     byte[] bytes = clazz.getBytes();
127                     cl = defineClass(class_name, bytes, 0, bytes.length);
128                 } else {
129                     cl = Class.forName(class_name);
130                 }
131             }
132             if (resolve) {
133                 resolveClass(cl);
134             }
135         }
136         classes.put(class_name, cl);
137         return cl;
138     }
139 
140 
141     /*** Override this method if you want to alter a class before it gets actually
142      * loaded. Does nothing by default.
143      */
144     protected JavaClass modifyClass( JavaClass clazz ) {
145         return clazz;
146     }
147 
148 
149     /*** 
150      * Override this method to create you own classes on the fly. The
151      * name contains the special token $$BCEL$$. Everything before that
152      * token is consddered to be a package name. You can encode you own
153      * arguments into the subsequent string. You must regard however not
154      * to use any "illegal" characters, i.e., characters that may not
155      * appear in a Java class name too<br>
156      *
157      * The default implementation interprets the string as a encoded compressed
158      * Java class, unpacks and decodes it with the Utility.decode() method, and
159      * parses the resulting byte array and returns the resulting JavaClass object.
160      *
161      * @param class_name compressed byte code with "$$BCEL$$" in it
162      */
163     protected JavaClass createClass( String class_name ) {
164         int index = class_name.indexOf("$$BCEL$$");
165         String real_name = class_name.substring(index + 8);
166         JavaClass clazz = null;
167         try {
168             byte[] bytes = Utility.decode(real_name, true);
169             ClassParser parser = new ClassParser(new ByteArrayInputStream(bytes), "foo");
170             clazz = parser.parse();
171         } catch (Throwable e) {
172             e.printStackTrace();
173             return null;
174         }
175         // Adapt the class name to the passed value
176         ConstantPool cp = clazz.getConstantPool();
177         ConstantClass cl = (ConstantClass) cp.getConstant(clazz.getClassNameIndex(),
178                 Constants.CONSTANT_Class);
179         ConstantUtf8 name = (ConstantUtf8) cp.getConstant(cl.getNameIndex(),
180                 Constants.CONSTANT_Utf8);
181         name.setBytes(class_name.replace('.', '/'));
182         return clazz;
183     }
184 }