View Javadoc

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  
18  package org.apache.commons.jci.compilers;
19  
20  import java.io.File;
21  import java.io.FileInputStream;
22  import java.io.FileOutputStream;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.net.MalformedURLException;
26  import java.net.URL;
27  import java.net.URLClassLoader;
28  import java.util.HashMap;
29  import java.util.Locale;
30  import java.util.Map;
31  
32  import org.objectweb.asm.ClassReader;
33  import org.objectweb.asm.ClassWriter;
34  import org.objectweb.asm.util.CheckClassAdapter;
35  import org.vafer.dependency.asm.RenamingVisitor;
36  import org.vafer.dependency.resources.ResourceRenamer;
37  
38  
39  /**
40   * This classloader injects the FileIn/OutputStream wrappers
41   * into javac. Access to the store/reader is done via ThreadLocals.
42   * It also tries to find the javac class on the system and expands
43   * the classpath accordingly.
44   * 
45   * @author tcurdt
46   */
47  public final class JavacClassLoader extends URLClassLoader {
48  
49      private final Map loaded = new HashMap();
50  
51      public JavacClassLoader( final ClassLoader pParent ) {
52          super(getToolsJar(), pParent);
53      }
54  
55      private static URL[] getToolsJar() {
56          try {
57              Class.forName("com.sun.tools.javac.Main");
58  
59              // found - no addtional classpath entry required
60              return new URL[0];
61  
62          } catch (Exception e) {
63          }
64  
65          // no compiler in current classpath, let's try to find the tools.jar
66  
67          String javaHome = System.getProperty("java.home");
68          if (javaHome.toLowerCase(Locale.US).endsWith(File.separator + "jre")) {
69              javaHome = javaHome.substring(0, javaHome.length()-4);
70          }
71  
72          final File toolsJar = new File(javaHome + "/lib/tools.jar");
73  
74          if (toolsJar.exists()) {
75              try {
76                  return new URL[] { toolsJar.toURL() };
77              } catch (MalformedURLException e) {
78              }
79          }
80  
81          final StringBuffer sb = new StringBuffer();
82          sb.append("Could not find javac compiler class (should be in the tools.jar/classes.jar in your JRE/JDK). ");
83          sb.append("os.name").append('=').append(System.getProperty("os.name")).append(", ");
84          sb.append("os.version").append('=').append(System.getProperty("os.version")).append(", ");
85          sb.append("java.class.path").append('=').append(System.getProperty("java.class.path"));
86  
87          throw new RuntimeException(sb.toString());
88      }
89  
90      protected Class findClass( final String name ) throws ClassNotFoundException {
91  
92          if (name.startsWith("java.")) {
93              return super.findClass(name);
94          }
95  
96          try {
97  
98              final Class clazz = (Class) loaded.get(name);
99              if (clazz != null) {
100                 return clazz;
101             }
102 
103             final byte[] classBytes;
104 
105             if (name.startsWith("com.sun.tools.javac.")) {
106                 final InputStream classStream = getResourceAsStream(name.replace('.', '/') + ".class");
107 
108                 final ClassWriter renamedCw = new ClassWriter(true, false);
109                 new ClassReader(classStream).accept(new RenamingVisitor(new CheckClassAdapter(renamedCw), new ResourceRenamer() {
110                     public String getNewNameFor(final String pOldName) {
111                         if (pOldName.startsWith(FileOutputStream.class.getName())) {
112                             return FileOutputStreamProxy.class.getName();
113                         }
114                         if (pOldName.startsWith(FileInputStream.class.getName())) {
115                             return FileInputStreamProxy.class.getName();
116                         }
117                         return pOldName;
118                     }
119                 }), false);
120 
121                 classBytes = renamedCw.toByteArray();
122 
123             } else {
124                 return super.findClass(name);
125             }
126 
127             final Class newClazz = defineClass(name, classBytes, 0, classBytes.length);
128             loaded.put(name, newClazz);
129             return newClazz;
130         } catch (IOException e) {
131             throw new ClassNotFoundException("", e);
132         }
133     }
134 
135     protected synchronized Class loadClass( final String classname, final boolean resolve ) throws ClassNotFoundException {
136 
137         Class theClass = findLoadedClass(classname);
138         if (theClass != null) {
139             return theClass;
140         }
141 
142         try {
143             theClass = findClass(classname);
144         } catch (ClassNotFoundException cnfe) {
145             theClass = getParent().loadClass(classname);
146         }
147 
148         if (resolve) {
149             resolveClass(theClass);
150         }
151 
152         return theClass;
153     }
154 }