1 package org.codehaus.mojo.apt;
2
3 /*
4 * The MIT License
5 *
6 * Copyright 2006-2008 The Codehaus.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy of
9 * this software and associated documentation files (the "Software"), to deal in
10 * the Software without restriction, including without limitation the rights to
11 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 * of the Software, and to permit persons to whom the Software is furnished to do
13 * so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in all
16 * copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * SOFTWARE.
25 */
26
27 import java.io.File;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.net.MalformedURLException;
31 import java.net.URL;
32 import java.net.URLClassLoader;
33 import java.util.HashMap;
34 import java.util.Map;
35
36 import org.objectweb.asm.ClassReader;
37 import org.objectweb.asm.ClassVisitor;
38 import org.objectweb.asm.ClassWriter;
39 import org.objectweb.asm.commons.Remapper;
40 import org.objectweb.asm.commons.RemappingClassAdapter;
41
42 /**
43 * Loads the APT processor class and enables it for embedding. Inspired by the JavacClassLoader from Apache Commons JCI
44 * and the Maven Shade Plugin. When loading APT and "com.sun.*" classes in general, we will prefer the plugin class
45 * loader as this could know about a system-scope dependency (on {@code tools.jar}) from the POM. If the plugin class
46 * loader fails, we will fallback to the {@code tools.jar} of the current JDK.
47 *
48 * @author Benjamin Bentmann
49 */
50 class AptClassLoader
51 extends URLClassLoader
52 {
53
54 public static final ClassLoader INSTANCE = new AptClassLoader();
55
56 private Map<String, Class<?>> loadedClasses = new HashMap<String, Class<?>>();
57
58 private AptClassLoader()
59 {
60 super( getClassPath(), null );
61 }
62
63 static File getToolsJar()
64 {
65 return new File( System.getProperty( "java.home" ), "../lib/tools.jar" );
66 }
67
68 private static URL[] getClassPath()
69 {
70 File toolsJar = getToolsJar();
71 if ( toolsJar.isFile() )
72 {
73 try
74 {
75 return new URL[] { toolsJar.toURI().toURL() };
76 }
77 catch ( MalformedURLException e )
78 {
79 // cannot happen
80 }
81 }
82 // no need to panic, could still be on the bootstrap class path or provided via plugin dependency
83 return new URL[0];
84 }
85
86 @Override
87 protected synchronized Class<?> loadClass( String name, boolean resolve )
88 throws ClassNotFoundException
89 {
90 if ( name.startsWith( "com.sun.tools.apt." ) )
91 {
92 // APT impl uses the URLClassLoader to load factories and such, so inject our tweaked class loader
93
94 Class<?> loadedClass = loadedClasses.get( name );
95
96 if ( loadedClass == null )
97 {
98 try
99 {
100 String classFile = name.replace( '.', '/' ) + ".class";
101
102 InputStream classStream = getClass().getClassLoader().getResourceAsStream( classFile );
103
104 if ( classStream == null )
105 {
106 classStream = getResourceAsStream( classFile );
107 }
108
109 ClassReader classReader = new ClassReader( classStream );
110
111 ClassWriter classWriter = new ClassWriter( classReader, 0 );
112
113 ClassVisitor classVisitor = new RemappingClassAdapter( classWriter, new ClassRemapper() );
114
115 classReader.accept( classVisitor, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES );
116
117 byte[] classBytes = classWriter.toByteArray();
118
119 loadedClass = defineClass( name, classBytes, 0, classBytes.length );
120
121 loadedClasses.put( name, loadedClass );
122 }
123 catch ( IOException e )
124 {
125 throw new ClassNotFoundException( name, e );
126 }
127 }
128
129 if ( resolve )
130 {
131 resolveClass( loadedClass );
132 }
133
134 return loadedClass;
135 }
136 else if ( EmbeddedURLClassLoader.class.getName().equals( name ) )
137 {
138 // make sure APT can load the tweaked class loader we injected into its code
139
140 return getClass().getClassLoader().loadClass( name );
141 }
142 else if ( name.startsWith( "com.sun." ) )
143 {
144 // APT API
145
146 try
147 {
148 // try plugin class loader first to give priority to explicit plugin dependency
149 return getClass().getClassLoader().loadClass( name );
150 }
151 catch ( ClassNotFoundException e )
152 {
153 // now try auto-detected tools.jar of current JDK below
154 }
155 }
156
157 return super.loadClass( name, resolve );
158 }
159
160 static class ClassRemapper
161 extends Remapper
162 {
163
164 private final String from = URLClassLoader.class.getName().replace( '.', '/' );
165
166 private final String to = EmbeddedURLClassLoader.class.getName().replace( '.', '/' );
167
168 @Override
169 public String map( String type )
170 {
171 if ( from.equals( type ) )
172 {
173 return to;
174 }
175 return type;
176 }
177
178 }
179
180 }