001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.loader.enhancer;
031:
032: import com.caucho.bytecode.ByteCodeClassMatcher;
033: import com.caucho.bytecode.ByteCodeClassScanner;
034: import com.caucho.bytecode.ByteCodeParser;
035: import com.caucho.bytecode.JClass;
036: import com.caucho.bytecode.JavaClass;
037: import com.caucho.bytecode.JavaClassLoader;
038: import com.caucho.java.WorkDir;
039: import com.caucho.java.gen.GenClass;
040: import com.caucho.java.gen.JavaClassGenerator;
041: import com.caucho.loader.*;
042: import com.caucho.util.CharBuffer;
043: import com.caucho.util.L10N;
044: import com.caucho.util.Log;
045: import com.caucho.vfs.*;
046:
047: import java.io.*;
048: import java.lang.instrument.*;
049: import java.lang.reflect.*;
050: import java.util.ArrayList;
051: import java.security.*;
052: import java.util.logging.Level;
053: import java.util.logging.Logger;
054:
055: /**
056: * Manages the enhancement
057: */
058: public class EnhancerManager implements ClassFileTransformer {
059: private static final L10N L = new L10N(EnhancerManager.class);
060: private static final Logger log = Log.open(EnhancerManager.class);
061:
062: private static EnvironmentLocal<EnhancerManager> _localEnhancer = new EnvironmentLocal<EnhancerManager>();
063:
064: private EnhancerManager _parent;
065: private DynamicClassLoader _loader;
066:
067: private Path _workPath;
068:
069: private JavaClassLoader _jClassLoader = new JavaClassLoader();
070: private JavaClassGenerator _javaGen = new JavaClassGenerator();
071:
072: private ArrayList<ClassEnhancer> _classEnhancerList = new ArrayList<ClassEnhancer>();
073:
074: private EnhancerManager(ClassLoader loader) {
075: for (; loader != null
076: && !(loader instanceof DynamicClassLoader); loader = loader
077: .getParent()) {
078: }
079:
080: _loader = (DynamicClassLoader) loader;
081:
082: if (loader != null)
083: _parent = getLocalEnhancer(loader.getParent());
084:
085: if (_parent != null) {
086: _classEnhancerList.addAll(_parent._classEnhancerList);
087: }
088: }
089:
090: public static EnhancerManager create() {
091: return create(Thread.currentThread().getContextClassLoader());
092: }
093:
094: public static EnhancerManager create(ClassLoader loader) {
095: EnhancerManager enhancer = _localEnhancer.getLevel(loader);
096:
097: if (enhancer == null) {
098: enhancer = new EnhancerManager(loader);
099: _localEnhancer.set(enhancer, loader);
100:
101: for (; loader != null; loader = loader.getParent()) {
102: if (loader instanceof DynamicClassLoader) {
103: ((DynamicClassLoader) loader)
104: .addTransformer(enhancer);
105: break;
106: }
107: }
108: }
109:
110: return enhancer;
111: }
112:
113: public static EnhancerManager getLocalEnhancer(ClassLoader loader) {
114: return _localEnhancer.get(loader);
115: }
116:
117: /**
118: * Returns the JClassLoader.
119: */
120: public JavaClassLoader getJavaClassLoader() {
121: return _jClassLoader;
122: }
123:
124: /**
125: * Gets the work path.
126: */
127: public Path getWorkPath() {
128: if (_workPath != null)
129: return _workPath;
130: else
131: return WorkDir.getLocalWorkDir(_loader);
132: }
133:
134: /**
135: * Sets the work path.
136: */
137: public void setWorkPath(Path workPath) {
138: _workPath = workPath;
139: }
140:
141: /**
142: * Gets the work path.
143: */
144: public final Path getPreWorkPath() {
145: return getWorkPath().lookup("pre-enhance");
146: }
147:
148: /**
149: * Gets the work path.
150: */
151: public final Path getPostWorkPath() {
152: return getWorkPath().lookup("post-enhance");
153: }
154:
155: /**
156: * Adds a class enhancer.
157: */
158: public void addClassEnhancer(ClassEnhancer classEnhancer) {
159: _classEnhancerList.add(classEnhancer);
160: }
161:
162: /**
163: * Adds a class annotation.
164: */
165: public void addClass(ClassEnhancerConfig config) {
166: config.setEnhancerManager(this );
167:
168: _classEnhancerList.add(config);
169: }
170:
171: /**
172: * Returns the enhanced .class or null if no enhancement.
173: */
174: public byte[] transform(ClassLoader loader, String className,
175: Class oldClass, ProtectionDomain domain, byte[] buffer) {
176: if (isClassMatch(className)) {
177: try {
178: ClassLoader tempLoader = ((DynamicClassLoader) loader)
179: .getNewTempClassLoader();
180: DynamicClassLoader workLoader = SimpleLoader.create(
181: tempLoader, getPostWorkPath());
182: workLoader.setServletHack(true);
183: boolean isModified = true;
184:
185: try {
186: Class cl = Class.forName(className
187: .replace('/', '.'), false, workLoader);
188:
189: Method init = cl.getMethod("_caucho_init",
190: new Class[] { Path.class });
191: Method modified = cl.getMethod(
192: "_caucho_is_modified", new Class[0]);
193:
194: init.invoke(null, Vfs.lookup());
195:
196: isModified = (Boolean) modified.invoke(null);
197: } catch (Exception e) {
198: log.log(Level.FINEST, e.toString(), e);
199: } catch (Throwable e) {
200: log.log(Level.FINER, e.toString(), e);
201: }
202:
203: if (!isModified) {
204: try {
205: return load(className);
206: } catch (Exception e) {
207: log.log(Level.FINER, e.toString(), e);
208: }
209: }
210:
211: ByteCodeParser parser = new ByteCodeParser();
212: parser.setClassLoader(_jClassLoader);
213:
214: ByteArrayInputStream is;
215: is = new ByteArrayInputStream(buffer, 0, buffer.length);
216:
217: JavaClass jClass = parser.parse(is);
218:
219: return enhance(jClass);
220: } catch (RuntimeException e) {
221: throw e;
222: } catch (Exception e) {
223: throw new EnhancerRuntimeException(e);
224: }
225: }
226:
227: return null;
228: }
229:
230: /**
231: * Enhances the given class.
232: */
233: public byte[] enhance(JClass jClass) throws ClassNotFoundException {
234: String className = jClass.getName().replace('/', '.');
235: String extClassName = className + "__ResinExt";
236:
237: try {
238: EnhancerPrepare prepare = new EnhancerPrepare();
239: prepare.setWorkPath(getWorkPath());
240: prepare.setClassLoader(_loader);
241:
242: for (ClassEnhancer enhancer : _classEnhancerList) {
243: if (enhancer.shouldEnhance(className)) {
244: prepare.addEnhancer(enhancer);
245: }
246: }
247:
248: //prepare.renameClass(className, extClassName);
249: prepare.renameClass(className, className);
250: } catch (Exception e) {
251: log.log(Level.FINE, e.toString(), e);
252:
253: throw new ClassNotFoundException(e.toString());
254: }
255:
256: boolean hasEnhancer = false;
257: GenClass genClass = new GenClass(extClassName);
258: genClass.setSuperClassName(className);
259: for (ClassEnhancer enhancer : _classEnhancerList) {
260: if (enhancer.shouldEnhance(className)) {
261: try {
262: hasEnhancer = true;
263: enhancer.enhance(genClass, jClass, extClassName);
264: } catch (RuntimeException e) {
265: throw e;
266: } catch (Exception e) {
267: throw new RuntimeException(e);
268: }
269: }
270: }
271: // XXX: class-wide enhancements need to go first
272:
273: try {
274: if (hasEnhancer) {
275: _javaGen.setWorkDir(getPreWorkPath());
276: _javaGen.generate(genClass);
277: _javaGen.compilePendingJava();
278: }
279:
280: EnhancerFixup fixup = new EnhancerFixup();
281: fixup.setJavaClassLoader(_jClassLoader);
282: fixup.setClassLoader(_loader);
283: fixup.setWorkPath(getWorkPath());
284:
285: for (ClassEnhancer enhancer : _classEnhancerList) {
286: if (enhancer.shouldEnhance(className)) {
287: fixup.addEnhancer(enhancer);
288: }
289: }
290:
291: fixup.fixup(className, extClassName);
292:
293: return load(className);
294: } catch (RuntimeException e) {
295: e.printStackTrace();
296:
297: throw e;
298: } catch (Exception e) {
299: e.printStackTrace();
300: log.log(Level.FINE, e.toString(), e);
301:
302: throw new ClassNotFoundException(e.getMessage());
303: }
304:
305: // return null;
306: }
307:
308: private byte[] load(String className) throws IOException {
309: Path path = getPostWorkPath().lookup(
310: className.replace('.', '/') + ".class");
311: byte[] buffer = new byte[(int) path.getLength()];
312:
313: ReadStream is = path.openRead();
314: try {
315: is.readAll(buffer, 0, buffer.length);
316: } finally {
317: is.close();
318: }
319:
320: return buffer;
321: }
322:
323: /**
324: * Returns true for a matching class.
325: */
326: public boolean isClassMatch(String className) {
327: if (className.lastIndexOf('$') >= 0) {
328: int p = className.lastIndexOf('$');
329: char ch = 0;
330:
331: if (p + 1 < className.length())
332: ch = className.charAt(p + 1);
333:
334: if ('0' <= ch && ch <= '9')
335: return false;
336: } else if (className.indexOf('+') > 0
337: || className.indexOf('-') > 0)
338: return false;
339:
340: for (int i = 0; i < _classEnhancerList.size(); i++) {
341: if (_classEnhancerList.get(i).shouldEnhance(className)) {
342: return true;
343: }
344: }
345:
346: return false;
347: }
348: }
|