0001: /*
0002: * Copyright 2001-2007 Geert Bevin <gbevin[remove] at uwyn dot com>
0003: * Distributed under the terms of either:
0004: * - the common development and distribution license (CDDL), v1.0; or
0005: * - the GNU Lesser General Public License, v2.1 or later
0006: * $Id: EngineClassLoader.java 3862 2007-07-13 14:37:11Z gbevin $
0007: */
0008: package com.uwyn.rife.engine;
0009:
0010: import com.tc.object.loaders.NamedClassLoader;
0011: import com.uwyn.rife.engine.instrument.ElementDetector;
0012: import com.uwyn.rife.instrument.ClassBytesProvider;
0013: import com.uwyn.rife.instrument.exceptions.ClassBytesNotFoundException;
0014: import com.uwyn.rife.resources.ModificationTimeClasspath;
0015: import com.uwyn.rife.site.instrument.ConstrainedDetector;
0016: import com.uwyn.rife.tools.ClassBytesLoader;
0017: import com.uwyn.rife.tools.FileUtils;
0018: import com.uwyn.rife.tools.InstrumentationUtils;
0019: import java.io.File;
0020: import java.io.UnsupportedEncodingException;
0021: import java.lang.reflect.Constructor;
0022: import java.lang.reflect.Field;
0023: import java.lang.reflect.InvocationTargetException;
0024: import java.lang.reflect.Method;
0025: import java.net.URL;
0026: import java.net.URLClassLoader;
0027: import java.net.URLDecoder;
0028: import java.util.ArrayList;
0029: import java.util.HashMap;
0030: import java.util.HashSet;
0031: import java.util.List;
0032: import java.util.Set;
0033:
0034: public class EngineClassLoader extends URLClassLoader implements
0035: NamedClassLoader, ClassBytesProvider {
0036: public final static String DEFAULT_IMPLEMENTATIONS_PATH = "implementations/";
0037: public final static String META_DATA_SUFFIX = "MetaData";
0038:
0039: private static HashMap<String, Long> sModificationTimes = new HashMap<String, Long>();
0040:
0041: private static List<String> sRifeWebappPath = null;
0042: private static boolean sRifeWebappPathProcessed = false;
0043:
0044: private Set<String> mModifiedClasses = null;
0045: private boolean mIsParentEngineclassloader = false;
0046: private EngineClassLoader mChild = null;
0047: private ClassLoader mInitiating = null;
0048:
0049: private Method mFindLoadedClassMethod = null;
0050:
0051: private ClassBytesLoader mByteLoader = null;
0052:
0053: private ElementDetector mElementDetector = null;
0054: private ConstrainedDetector mConstrainedDetector = null;
0055:
0056: private Class mMergerClass = null;
0057: private Method mMergerMethod = null;
0058:
0059: private Class mLazyLoadClass = null;
0060: private Method mLazyLoadMethod = null;
0061:
0062: private Object mEngineContinuationConfigInstrument = null;
0063:
0064: private Class mContinuationConfigInstrumentClass = null;
0065:
0066: private Class mResumerClass = null;
0067: private Method mResumerMethod = null;
0068:
0069: private String mClassLoaderName = "RIFE:EngineClassLoader";
0070:
0071: public EngineClassLoader(ClassLoader parent) {
0072: super (new URL[0], parent);
0073:
0074: // check for the presence of Terracotta by loading its ClassProcessorHelper class
0075: try {
0076: Class classprocessor_helper_class = Class
0077: .forName("com.tc.object.bytecode.hook.impl.ClassProcessorHelper");
0078: if (classprocessor_helper_class != null) {
0079: if (parent instanceof NamedClassLoader) {
0080: NamedClassLoader namedParent = (NamedClassLoader) parent;
0081: mClassLoaderName = "Rife:Engine:"
0082: + namedParent.__tc_getClassLoaderName();
0083:
0084: try {
0085: Method method = classprocessor_helper_class
0086: .getDeclaredMethod(
0087: "registerGlobalLoader",
0088: new Class[] { NamedClassLoader.class });
0089: method.invoke(null, new Object[] { this });
0090: } catch (Exception e) {
0091: throw new RuntimeException(
0092: "Unable to register the engine classloader '"
0093: + mClassLoaderName
0094: + "' with Terracotta.", e);
0095: }
0096: }
0097: }
0098: } catch (ClassNotFoundException e) {
0099: // this is OK, Terracotta is not present in the classpath
0100: }
0101:
0102: mIsParentEngineclassloader = parent instanceof EngineClassLoader;
0103: if (mIsParentEngineclassloader) {
0104: mInitiating = ((EngineClassLoader) parent).mInitiating;
0105: } else {
0106: mInitiating = parent;
0107: }
0108:
0109: mByteLoader = new ClassBytesLoader(this );
0110: mByteLoader.setupSunByteLoading();
0111: if (mByteLoader.isUsingSunByteLoading()) {
0112: addClassPathURLs();
0113: }
0114:
0115: mElementDetector = new ElementDetector(this );
0116:
0117: mConstrainedDetector = new ConstrainedDetector(mByteLoader);
0118: }
0119:
0120: public String __tc_getClassLoaderName() {
0121: return mClassLoaderName;
0122: }
0123:
0124: public void __tc_setClassLoaderName(String name) {
0125: throw new UnsupportedOperationException(
0126: "class loader name can not be modified for loader with name: "
0127: + mClassLoaderName);
0128: }
0129:
0130: private void addClassPathURLs() {
0131: try {
0132: Class classpathutils_class = loadClass("com.uwyn.rife.tools.ClasspathUtils");
0133: Method classpathutils_method = classpathutils_class
0134: .getDeclaredMethod("getClassPathAsList",
0135: Class.class);
0136: List<String> classpath = (List<String>) classpathutils_method
0137: .invoke(null, getClass());
0138: if (classpath != null) {
0139: for (String classpath_element : classpath) {
0140: addURL(new URL("file", null, classpath_element));
0141: }
0142: }
0143: } catch (Throwable e) {
0144: throw new RuntimeException(e);
0145: }
0146: }
0147:
0148: private boolean containsModifiedClass(String classname) {
0149: if (null == mModifiedClasses) {
0150: return false;
0151: }
0152:
0153: if (mModifiedClasses.contains(classname)) {
0154: return true;
0155: }
0156:
0157: int innerclass_index = classname.indexOf("$");
0158: if (innerclass_index != -1) {
0159: String containing_classname = classname.substring(0,
0160: innerclass_index);
0161: if (mModifiedClasses.contains(containing_classname)) {
0162: return true;
0163: }
0164: }
0165:
0166: return false;
0167: }
0168:
0169: protected Class loadClass(String classname, boolean resolve)
0170: throws ClassNotFoundException {
0171: return loadClass(classname, resolve, false);
0172: }
0173:
0174: synchronized public void markClassAsModified(String classname) {
0175: if (null == mModifiedClasses) {
0176: mModifiedClasses = new HashSet<String>();
0177: }
0178:
0179: if (mChild != null && mModifiedClasses.contains(classname)) {
0180: mChild.markClassAsModified(classname);
0181: } else {
0182: if (null == mChild) {
0183: mChild = new EngineClassLoader(this );
0184: }
0185: mModifiedClasses.add(classname);
0186: }
0187: }
0188:
0189: public Class loadClass(String classname, boolean resolve,
0190: boolean loadElement) throws ClassNotFoundException {
0191: assert classname != null;
0192:
0193: // System.out.println(">>> "+classname);
0194:
0195: // get the auto reload preferences
0196: boolean auto_reload = doAutoReload();
0197:
0198: // see if this classloader has cached the class with the provided name
0199: Class c = null;
0200: if (auto_reload && containsModifiedClass(classname)) {
0201: return mChild.loadClass(classname, resolve, loadElement);
0202: }
0203: if (null == c) {
0204: c = findLoadedClass(classname);
0205: }
0206:
0207: // if an already loaded version was found, check whether it's outdated or not
0208: if (c != null) {
0209: // if an already loaded version was found, check whether it's outdated or not
0210: // this can only be Element classes since those are the only ones that are
0211: // handled by this classloader
0212: if (auto_reload) {
0213: // if the element was modified, don't use the cached class
0214: // otherwise, just take the previous element class
0215: if (loadElement && isModified(classname)) {
0216: synchronized (this ) {
0217: markClassAsModified(classname);
0218:
0219: Class klass = mChild.loadClass(classname,
0220: resolve, loadElement);
0221: clearSiteCaches(classname);
0222: return klass;
0223: }
0224: }
0225: }
0226: }
0227: // try to obtain the class in another way
0228: else {
0229: // try to load the core system classes first, these are sure to be
0230: // handled by the system classloader
0231: if (null == c) {
0232: if (classname.startsWith("java.")) {
0233: try {
0234: c = findSystemClass(classname);
0235: if (c != null) {
0236: if (resolve) {
0237: resolveClass(c);
0238: }
0239:
0240: // System.out.println("SYSTEM defined "+classname);
0241:
0242: return c;
0243: }
0244: } catch (ClassNotFoundException e) {
0245: c = null;
0246: }
0247: }
0248: }
0249:
0250: ClassLoader parent = getParent();
0251: classname = classname.intern();
0252:
0253: // Get classes already loaded by the servlet container's classloader,
0254: // to avoid loading them again. These will be classes loaded by other
0255: // filters and listeners inside the same web application, initialized
0256: // before RIFE. For RIFE these should only be the
0257: // EngineClassLoader-related classed and those of the continuations engine
0258: try {
0259: if (null == mFindLoadedClassMethod) {
0260: mFindLoadedClassMethod = ClassLoader.class
0261: .getDeclaredMethod(
0262: "findLoadedClass",
0263: new Class[] { java.lang.String.class });
0264: mFindLoadedClassMethod.setAccessible(true);
0265: }
0266:
0267: c = (Class) mFindLoadedClassMethod.invoke(mInitiating,
0268: (Object[]) new String[] { classname });
0269: // if (c != null)
0270: // {
0271: // System.out.println("PARENT defined "+classname+" (already loaded)");
0272: // }
0273: } catch (Throwable e) {
0274: c = null;
0275: }
0276:
0277: // load the class through the initial EngineClassLoader, unless it
0278: // has been modified
0279: if (mIsParentEngineclassloader
0280: && !((EngineClassLoader) parent)
0281: .containsModifiedClass(classname)) {
0282: // if (c != null)
0283: // {
0284: // System.out.println("PARENT loading "+classname);
0285: // }
0286: return ((EngineClassLoader) parent).loadClass(
0287: classname, resolve, loadElement);
0288: }
0289:
0290: // try to obtain the class in a RIFE-specific way
0291: if (null == c
0292: && !classname.startsWith("com.uwyn.rife.asm.")
0293: && !classname.startsWith("com.tc.object.loaders.")) {
0294: boolean webapp_class = false;
0295: boolean rife_class = false;
0296: if (classname == "com.uwyn.rife.engine.EngineClassLoaderRifeWebappPath") {
0297: webapp_class = true;
0298: } else {
0299: // This is not a problem, if several simultaneous threads
0300: // will access it, it will just be retrieved several times.
0301: // The calls will be cached for all subsequent threads
0302: // though.
0303: if (!sRifeWebappPathProcessed) {
0304: try {
0305: Class webapp_path_class = loadClass("com.uwyn.rife.engine.EngineClassLoaderRifeWebappPath");
0306: Field webapp_path_field = webapp_path_class
0307: .getField("RIFE_WEBAPP_PATH");
0308: sRifeWebappPath = (ArrayList<String>) webapp_path_field
0309: .get(null);
0310: sRifeWebappPathProcessed = true;
0311: } catch (Throwable e) {
0312: throw new ClassNotFoundException(classname,
0313: e);
0314: }
0315: }
0316:
0317: // check of the class is part of the web application, or if it should be loaded through
0318: // a parent classloader
0319: if (classname.startsWith("com.uwyn.rife.")) // classes are loaded by the EngineClassLoader
0320: {
0321: webapp_class = true;
0322: rife_class = true;
0323: } else {
0324: webapp_class = isWebAppClass(classname);
0325: }
0326:
0327: // only handle undefined classes, or check existing ones if
0328: // auto-reloading has been activated
0329: if (null == c || auto_reload) {
0330: String packagename = null;
0331: int packagename_index = classname
0332: .lastIndexOf('.');
0333: if (packagename_index != -1) {
0334: packagename = classname.substring(0,
0335: packagename_index);
0336: }
0337:
0338: // first try to find the compiled bytecode, take inner classes
0339: // into account
0340: byte raw_bytes[] = null;
0341: int innerclass_index = classname.indexOf("$");
0342: try {
0343: if (innerclass_index > -1) {
0344: raw_bytes = getClassBytes(
0345: classname.substring(0,
0346: innerclass_index),
0347: auto_reload, loadElement);
0348: } else {
0349: raw_bytes = getClassBytes(classname,
0350: auto_reload, loadElement);
0351: }
0352: } catch (ClassNotFoundException e) {
0353: raw_bytes = null;
0354: }
0355:
0356: if (raw_bytes != null) {
0357: // check if the bytes corresponds to an element class
0358: if (!isElement(classname, raw_bytes,
0359: auto_reload)) {
0360: // If it's not an element and the class hasn't been
0361: // defined yet, define it. If it has been found in the
0362: // parent, use the already existing instance.
0363: // Auto-reloading isn't supported for non-elements
0364: if (null == c) {
0365: if (!webapp_class) {
0366: try {
0367: // System.out.println("PARENT defined "+classname+" (not webapp class)");
0368: if (!mIsParentEngineclassloader) {
0369: c = parent
0370: .loadClass(classname);
0371: }
0372: } catch (ClassNotFoundException e) {
0373: c = null;
0374: }
0375: } else {
0376: // System.out.println("ENGINE defined "+classname);
0377:
0378: if (packagename != null) {
0379: Package pkg = getPackage(packagename);
0380: if (null == pkg) {
0381: definePackage(
0382: packagename,
0383: null, null,
0384: null, null,
0385: null, null,
0386: null);
0387: }
0388: }
0389:
0390: // use the interned classname to get a synchronization lock monitor
0391: // that is specific for the current class that is loaded and
0392: // that will not lock up the classloading of all other classes
0393: // by for instance synchronizing on this classloader
0394: synchronized (classname) {
0395: // make sure that the class has not been defined in the
0396: // meantime, otherwise defining it again will trigger an
0397: // exception;
0398: // reuse the existing class if it has already been defined
0399: c = findLoadedClass(classname);
0400: if (null == c) {
0401: // if we are working with an inner class, get its
0402: // actual raw bytes, since we used the containing
0403: // class's bytes to determine if it's an element
0404: if (innerclass_index > -1) {
0405: raw_bytes = getClassBytes(
0406: classname,
0407: auto_reload,
0408: loadElement);
0409: }
0410:
0411: if (!rife_class) {
0412: // handle meta data sibling classes for non framework classes
0413: Class metadata_class = null;
0414: if (!classname
0415: .endsWith(META_DATA_SUFFIX)) {
0416: try {
0417: metadata_class = loadClass(classname
0418: + META_DATA_SUFFIX);
0419: } catch (ClassNotFoundException e) {
0420: metadata_class = null;
0421: }
0422:
0423: if (metadata_class != null) {
0424: if (null == mMergerClass) {
0425: mMergerClass = loadClass("com.uwyn.rife.site.instrument.MetaDataBytecodeTransformer");
0426: }
0427:
0428: try {
0429: if (null == mMergerMethod) {
0430: mMergerMethod = mMergerClass
0431: .getDeclaredMethod(
0432: "mergeMetaDataIntoBytes",
0433: new Class[] {
0434: byte[].class,
0435: Class.class });
0436: mMergerMethod
0437: .setAccessible(true);
0438: }
0439: raw_bytes = (byte[]) mMergerMethod
0440: .invoke(
0441: null,
0442: new Object[] {
0443: raw_bytes,
0444: metadata_class });
0445: } catch (Exception e) {
0446: throw new ClassNotFoundException(
0447: classname,
0448: e);
0449: }
0450: }
0451: }
0452:
0453: try {
0454: if (isConstrained(
0455: classname,
0456: raw_bytes)) {
0457: // handle lazy loading for constrained non framework classes
0458: if (null == mLazyLoadClass) {
0459: mLazyLoadClass = loadClass("com.uwyn.rife.database.querymanagers.generic.instrument.LazyLoadAccessorsBytecodeTransformer");
0460: }
0461:
0462: if (null == mLazyLoadMethod) {
0463: mLazyLoadMethod = mLazyLoadClass
0464: .getDeclaredMethod(
0465: "addLazyLoadToBytes",
0466: new Class[] { byte[].class });
0467: mLazyLoadMethod
0468: .setAccessible(true);
0469: }
0470: raw_bytes = (byte[]) mLazyLoadMethod
0471: .invoke(
0472: null,
0473: new Object[] { raw_bytes });
0474: }
0475: } catch (Exception e) {
0476: throw new ClassNotFoundException(
0477: classname,
0478: e);
0479: }
0480: }
0481:
0482: // define the class from its raw bytes
0483: InstrumentationUtils
0484: .dumpClassBytes(
0485: "adapted",
0486: classname,
0487: raw_bytes);
0488: c = defineClass(
0489: classname,
0490: raw_bytes,
0491: 0,
0492: raw_bytes.length);
0493:
0494: if (null == c) {
0495: throw new ClassNotFoundException(
0496: classname);
0497: }
0498: }
0499: }
0500: }
0501: }
0502: } else {
0503: // if (null == c)
0504: // {
0505: // System.out.println("ENGINE defined "+classname);
0506: // }
0507: // else
0508: // {
0509: // System.out.println("ENGINE redefined "+classname);
0510: // }
0511:
0512: // if we are working with an inner class, get its
0513: // actual raw bytes, since we used the containing
0514: // class's bytes to determine if it's an element
0515: if (innerclass_index > -1) {
0516: raw_bytes = getClassBytes(
0517: classname, auto_reload,
0518: loadElement);
0519: }
0520:
0521: if (auto_reload) {
0522: // register the modification time of the source file
0523: long modification_time = getClassModificationTime(classname);
0524: sModificationTimes
0525: .put(classname, new Long(
0526: modification_time));
0527: }
0528:
0529: // get an instance of the engine continuations config
0530: if (null == mEngineContinuationConfigInstrument) {
0531: Class config_class = loadClass("com.uwyn.rife.engine.EngineContinuationConfigInstrument");
0532: try {
0533: Constructor config_constructor = config_class
0534: .getConstructor(new Class[0]);
0535: config_constructor
0536: .setAccessible(true);
0537: mEngineContinuationConfigInstrument = config_constructor
0538: .newInstance(new Object[0]);
0539: } catch (Exception e) {
0540: throw new ClassNotFoundException(
0541: classname, e);
0542: }
0543: }
0544:
0545: // get the continuation config instrument class
0546: if (null == mContinuationConfigInstrumentClass) {
0547: mContinuationConfigInstrumentClass = loadClass("com.uwyn.rife.continuations.ContinuationConfigInstrument");
0548: }
0549:
0550: // transform bytes on-the-fly into resumable bytes
0551: byte[] resumable_bytes;
0552: if (null == mResumerClass) {
0553: mResumerClass = loadClass("com.uwyn.rife.continuations.instrument.ContinuationsBytecodeTransformer");
0554: }
0555: try {
0556: if (null == mResumerMethod) {
0557: mResumerMethod = mResumerClass
0558: .getDeclaredMethod(
0559: "transformIntoResumableBytes",
0560: new Class[] {
0561: mContinuationConfigInstrumentClass,
0562: byte[].class,
0563: String.class });
0564: mResumerMethod
0565: .setAccessible(true);
0566: }
0567: resumable_bytes = (byte[]) mResumerMethod
0568: .invoke(
0569: null,
0570: new Object[] {
0571: mEngineContinuationConfigInstrument,
0572: raw_bytes,
0573: classname });
0574: } catch (Exception e) {
0575: throw new ClassNotFoundException(
0576: classname, e);
0577: }
0578:
0579: // define a package if needed
0580: if (packagename != null) {
0581: Package pkg = getPackage(packagename);
0582: if (null == pkg) {
0583: definePackage(packagename,
0584: null, null, null, null,
0585: null, null, null);
0586: }
0587: }
0588:
0589: // intern the classname to get a synchronization lock monitor
0590: // that is specific for the current class that is loaded and
0591: // that will not lock up the classloading of all other classes
0592: // by for instance synchronizing on this classloader
0593: classname = classname.intern();
0594: synchronized (classname) {
0595: // make sure that the class has not been defined in the
0596: // meantime, otherwise defining it again will trigger an
0597: // exception;
0598: // reuse the existing class if it has already been defined
0599: c = findLoadedClass(classname);
0600: if (null == c) {
0601: // get the raw bytes of the class and define it for this classloader
0602: if (null == resumable_bytes) {
0603: InstrumentationUtils
0604: .dumpClassBytes(
0605: "adapted",
0606: classname,
0607: raw_bytes);
0608: c = defineClass(classname,
0609: raw_bytes, 0,
0610: raw_bytes.length);
0611: } else {
0612: InstrumentationUtils
0613: .dumpClassBytes(
0614: "adapted",
0615: classname,
0616: resumable_bytes);
0617: c = defineClass(
0618: classname,
0619: resumable_bytes,
0620: 0,
0621: resumable_bytes.length);
0622: }
0623: }
0624: }
0625: }
0626: }
0627: }
0628: }
0629: }
0630: }
0631:
0632: // the ultimate fallback class loading
0633: if (null == c && !mIsParentEngineclassloader) {
0634: try {
0635: c = getParent().loadClass(classname);
0636: // if (c != null)
0637: // {
0638: // System.out.println("FALLBACK PARENT defined "+classname+" (ultimate falback)");
0639: // }
0640: } catch (ClassNotFoundException e) {
0641: c = null;
0642: }
0643: }
0644: if (null == c) {
0645: try {
0646: c = findSystemClass(classname);
0647: // System.out.println("FALLBACK SYSTEM defined "+classname);
0648: } catch (ClassNotFoundException e) {
0649: // System.out.println("!!!!!! not found "+classname);
0650: throw e;
0651: }
0652: }
0653:
0654: // resolve the class if it's needed
0655: if (resolve) {
0656: resolveClass(c);
0657: }
0658:
0659: assert c != null;
0660:
0661: return c;
0662: }
0663:
0664: private void clearSiteCaches(String classname)
0665: throws ClassNotFoundException {
0666: try {
0667: Class site_class = loadClass("com.uwyn.rife.engine.Site");
0668: Method repinstance_method = site_class.getDeclaredMethod(
0669: "getRepInstance", new Class[0]);
0670: Method clearcaches_method = site_class.getDeclaredMethod(
0671: "clearCaches", new Class[0]);
0672: Object site = repinstance_method
0673: .invoke(null, new Object[0]);
0674: if (site != null) {
0675: clearcaches_method.invoke(site, new Object[0]);
0676: }
0677: } catch (Exception e) {
0678: e.printStackTrace();
0679: throw new ClassNotFoundException(classname, e);
0680: }
0681: }
0682:
0683: private boolean isWebAppClass(String classname) {
0684: try {
0685: String class_file = classname.replace('.', '/') + ".class";
0686: URL resource = getResource(class_file);
0687: if (resource != null) {
0688: // System.out.println("resource_path : " + resource.toExternalForm());
0689: String resource_path = resource.getPath();
0690: String resource_protocol = resource.getProtocol();
0691: // if the resource lies in a WEB-INF dir, it's part
0692: // of a webapp
0693: if (resource_path.indexOf("WEB-INF") != -1) {
0694: return true;
0695: }
0696: // handle Orion's classloader resource protocol
0697: else if (resource_protocol != null
0698: && resource_protocol.equals("classloader")) {
0699: return true;
0700: }
0701: // if a specific rife webapp path collection has been
0702: // given, check against each entry
0703: else if (sRifeWebappPath != null) {
0704: if (resource_path.startsWith("jar:file:")) {
0705: resource_path = resource_path
0706: .substring("jar:file:".length());
0707: }
0708: if (resource_path.startsWith("file:")) {
0709: resource_path = resource_path.substring("file:"
0710: .length());
0711: }
0712: int exclamation_index = resource_path.indexOf("!");
0713: if (exclamation_index != -1) {
0714: resource_path = resource_path.substring(0,
0715: exclamation_index);
0716: }
0717: resource_path = URLDecoder.decode(resource_path,
0718: "ISO-8859-1");
0719: File resource_file = new File(resource_path);
0720: if (resource_file.exists()) {
0721: resource_path = resource_file
0722: .getCanonicalPath();
0723: for (String path : sRifeWebappPath) {
0724: if (resource_path.startsWith(path)) {
0725: return true;
0726: }
0727: }
0728: }
0729: }
0730: }
0731: } catch (Throwable e) {
0732: return false;
0733: }
0734:
0735: return false;
0736: }
0737:
0738: private boolean hasRepClass() {
0739: if (getParent() instanceof EngineClassLoader) {
0740: return ((EngineClassLoader) getParent()).hasRepClass();
0741: }
0742:
0743: try {
0744: return findLoadedClass("com.uwyn.rife.rep.Rep") != null;
0745: }
0746: // fix to make it work with the IBM jvm
0747: catch (ClassCircularityError e) {
0748: return true;
0749: }
0750: }
0751:
0752: private boolean doAutoReload() {
0753: Object value = System.getProperties()
0754: .get("ELEMENT_AUTO_RELOAD");
0755:
0756: if (null == value) {
0757: return true;
0758: }
0759:
0760: if (value instanceof String) {
0761: String string_value = (String) value;
0762: if (string_value.equals("1")
0763: || string_value.equalsIgnoreCase("t")
0764: || string_value.equalsIgnoreCase("true")
0765: || string_value.equalsIgnoreCase("y")
0766: || string_value.equalsIgnoreCase("yes")
0767: || string_value.equalsIgnoreCase("on")) {
0768: return true;
0769: }
0770: }
0771:
0772: return false;
0773: }
0774:
0775: public static String constructSourcePath(String classname) {
0776: String source_location = null;
0777: int innerclass_index = classname.indexOf("$");
0778: if (innerclass_index != -1) {
0779: String containing_classname = classname.substring(0,
0780: innerclass_index);
0781: source_location = containing_classname.replace('.', '/')
0782: + ".java";
0783: } else {
0784: source_location = classname.replace('.', '/') + ".java";
0785: }
0786:
0787: return source_location;
0788: }
0789:
0790: public byte[] getClassBytes(String className,
0791: boolean reloadAutomatically) throws ClassNotFoundException {
0792: return getClassBytes(className, reloadAutomatically, false);
0793: }
0794:
0795: public byte[] getClassBytes(String className, boolean doAutoReload,
0796: boolean performCompilation) throws ClassNotFoundException {
0797: byte[] raw_bytes = null;
0798: String class_filename = className.replace('.', '/') + ".class";
0799: URL class_resource = getResource(class_filename);
0800: try {
0801: // if it couldn't be found or if it's outdated when auto-reload is
0802: // activated, compile the element
0803: if ((null == class_resource || (doAutoReload
0804: && performCompilation && isModified(className)))
0805: && (-1 == className.indexOf('$') || !className
0806: .endsWith("BeanInfo"))) /// don't recompile for BeanInfo classes
0807: {
0808: try {
0809: if (!performCompilation && !hasRepClass()) {
0810: throw new ClassNotFoundException(className);
0811: } else {
0812: String source_location = constructSourcePath(className);
0813: File class_file = compileClass(className,
0814: source_location);
0815: raw_bytes = FileUtils.readBytes(class_file);
0816: }
0817: } catch (Throwable e) {
0818: Class elementcompilation_failed_class = loadClass("com.uwyn.rife.engine.exceptions.ElementCompilationFailedException");
0819: if (e.getClass() == elementcompilation_failed_class) {
0820: throw (RuntimeException) e;
0821: }
0822: if (e instanceof ClassNotFoundException) {
0823: throw (ClassNotFoundException) e;
0824: }
0825:
0826: throw new ClassNotFoundException(className, e);
0827: }
0828: }
0829: // otherwise just get the bytes of the classfile
0830: else {
0831: raw_bytes = mByteLoader.getClassBytes(class_filename,
0832: class_resource);
0833: }
0834: } catch (ClassNotFoundException e) {
0835: throw e;
0836: } catch (Throwable e) {
0837: if (e instanceof RuntimeException) {
0838: throw (RuntimeException) e;
0839: }
0840:
0841: throw new ClassNotFoundException(
0842: "Error while reading the contents of the element class file '"
0843: + className + "'.", e);
0844: }
0845:
0846: InstrumentationUtils.dumpClassBytes("initial", className,
0847: raw_bytes);
0848:
0849: return raw_bytes;
0850: }
0851:
0852: File compileClass(String classname, String sourceLocation)
0853: throws ClassNotFoundException {
0854: assert classname != null;
0855: assert sourceLocation != null;
0856: assert sourceLocation.endsWith(".java");
0857:
0858: // try to find the script
0859: URL source_resource = getSourceResource(sourceLocation, true);
0860: if (null == source_resource) {
0861: throw new ClassNotFoundException(
0862: "Couldn't find the source file '" + sourceLocation
0863: + "'.");
0864: }
0865:
0866: // get the package and the short classname of the element
0867: int classname_index = classname.lastIndexOf(".");
0868: String element_package = null;
0869: String element_classname = null;
0870: if (classname_index != -1) {
0871: element_package = classname.substring(0, classname_index);
0872: element_classname = classname
0873: .substring(classname_index + 1);
0874: } else {
0875: element_classname = classname;
0876: }
0877:
0878: // setup everything to perform the conversion of the element to java sources
0879: // and to compile it into a java class
0880: Object generation_path_object = null;
0881: try {
0882: Class tests_class = loadClass("com.uwyn.rife.config.RifeConfig$Engine");
0883: Method method = tests_class.getMethod(
0884: "getElementGenerationPath", (Class[]) null);
0885: generation_path_object = method.invoke(null,
0886: (Object[]) null);
0887: } catch (Throwable e) {
0888: throw new ClassNotFoundException(
0889: "Couldn't obtain the element bytecode generation path.",
0890: e);
0891: }
0892:
0893: String generation_path = ((String) generation_path_object)
0894: + File.separatorChar;
0895: String package_dir = "";
0896: if (element_package != null) {
0897: package_dir = generation_path
0898: + element_package.replace('.', File.separatorChar)
0899: + File.separator;
0900: } else {
0901: package_dir = generation_path;
0902: }
0903: String filename_java = null;
0904: try {
0905: filename_java = URLDecoder.decode(
0906: source_resource.getPath(), "ISO-8859-1");
0907: } catch (UnsupportedEncodingException e) {
0908: throw new ClassNotFoundException(
0909: "Couldn't decode the resource path '"
0910: + source_resource.getPath() + "'.", e);
0911: }
0912: String filename_class = package_dir + element_classname
0913: + ".class";
0914: File file_packagedir = new File(package_dir);
0915: File file_class = new File(filename_class);
0916:
0917: // prepare the package directory
0918: if (!file_packagedir.exists()) {
0919: if (!file_packagedir.mkdirs()) {
0920: throw new ClassNotFoundException(
0921: "Couldn't create the package directory : '"
0922: + package_dir + "'.");
0923: }
0924: } else if (!file_packagedir.isDirectory()) {
0925: throw new ClassNotFoundException("The package directory '"
0926: + package_dir + "' exists but is not a directory.");
0927: } else if (!file_packagedir.canWrite()) {
0928: throw new ClassNotFoundException("The package directory '"
0929: + package_dir + "' is not writable.");
0930: }
0931:
0932: // check if the class wasn't already compiled due to other classes
0933: // in the same source file
0934: boolean is_already_compiled = false;
0935: long source_modification_time = -1L;
0936: if (file_class.exists()) {
0937: long class_modification_time = file_class.lastModified();
0938: source_modification_time = getSourceModificationTime(source_resource);
0939: if (class_modification_time >= source_modification_time) {
0940: is_already_compiled = true;
0941: }
0942: }
0943:
0944: if (!is_already_compiled) {
0945: try {
0946: Class classloader_classpath_class = loadClass("com.uwyn.rife.engine.EngineClassLoaderClasspath");
0947: Field classloader_classpath_field = classloader_classpath_class
0948: .getField("CLASSPATH");
0949: String classloader_classpath = (String) classloader_classpath_field
0950: .get(null);
0951:
0952: Class tests_class = loadClass("com.uwyn.rife.tools.CompilationUtils");
0953: Method method = tests_class.getMethod("compile",
0954: new Class[] { String.class, File.class,
0955: String.class, String.class });
0956: generation_path_object = method
0957: .invoke(null, new Object[] { filename_java,
0958: file_class, generation_path,
0959: classloader_classpath });
0960: } catch (InvocationTargetException e) {
0961: Class compilation_failed_class = loadClass("com.uwyn.rife.tools.exceptions.CompilationFailedException");
0962:
0963: Throwable target_exception = e.getTargetException();
0964: if (compilation_failed_class == target_exception
0965: .getClass()) {
0966: String sourcefilename = null;
0967: String errors = null;
0968: Throwable cause = null;
0969:
0970: try {
0971: Method sourcefilename_method = compilation_failed_class
0972: .getMethod("getSourceFilename",
0973: (Class[]) null);
0974: sourcefilename = (String) sourcefilename_method
0975: .invoke(target_exception,
0976: (Object[]) null);
0977: Method errors_method = compilation_failed_class
0978: .getMethod("getErrors", (Class[]) null);
0979: errors = (String) errors_method.invoke(
0980: target_exception, (Object[]) null);
0981: Method cause_method = compilation_failed_class
0982: .getMethod("getCause", (Class[]) null);
0983: cause = (Throwable) cause_method.invoke(
0984: target_exception, (Object[]) null);
0985: } catch (Throwable e2) {
0986: throw new ClassNotFoundException(
0987: "Unexpected error while compiling the element source '"
0988: + filename_java + "'+.", e);
0989: }
0990:
0991: Object elementcompilation_failed_instance = null;
0992:
0993: try {
0994: Class elementcompilation_failed_class = loadClass("com.uwyn.rife.engine.exceptions.ElementCompilationFailedException");
0995: Constructor elementcompilation_failed_constructor = elementcompilation_failed_class
0996: .getConstructor(new Class[] {
0997: String.class, String.class,
0998: Throwable.class });
0999: elementcompilation_failed_instance = elementcompilation_failed_constructor
1000: .newInstance(new Object[] {
1001: sourcefilename, errors, cause });
1002: } catch (Throwable e2) {
1003: throw new ClassNotFoundException(
1004: "Unexpected error while compiling the element source '"
1005: + filename_java + "'+.", e);
1006: }
1007:
1008: throw (RuntimeException) elementcompilation_failed_instance;
1009: } else {
1010: throw new ClassNotFoundException(
1011: "Unexpected error while compiling the element source '"
1012: + filename_java + "'.", e);
1013: }
1014: } catch (Throwable e) {
1015: throw new ClassNotFoundException(
1016: "Unexpected error while compiling the element source '"
1017: + filename_java + "'.", e);
1018: }
1019: }
1020:
1021: assert file_class != null;
1022: assert file_class.exists();
1023: assert file_class.canRead();
1024:
1025: return file_class;
1026: }
1027:
1028: private URL getSourceResource(String sourceLocation,
1029: boolean getElement) {
1030: URL source_resource = getResource(sourceLocation);
1031: if (null == source_resource && getElement) {
1032: source_resource = getResource(DEFAULT_IMPLEMENTATIONS_PATH
1033: + sourceLocation);
1034: }
1035: return source_resource;
1036: }
1037:
1038: private boolean isElement(String internedClassname, byte[] bytes,
1039: boolean doAutoReload) throws ClassNotFoundException {
1040: return mElementDetector.detect(internedClassname, bytes,
1041: doAutoReload);
1042: }
1043:
1044: private boolean isConstrained(String internedClassname, byte[] bytes)
1045: throws ClassBytesNotFoundException {
1046: return mConstrainedDetector.isConstrained(internedClassname,
1047: bytes);
1048: }
1049:
1050: private long getClassModificationTime(String classname) {
1051: return getSourceModificationTime(getSourceResource(
1052: constructSourcePath(classname), true));
1053: }
1054:
1055: public static long getSourceModificationTime(URL sourceResource) {
1056: if (null == sourceResource) {
1057: return -1;
1058: }
1059:
1060: try {
1061: return ModificationTimeClasspath
1062: .getModificationTime(sourceResource);
1063: } catch (Throwable e) {
1064: return -1;
1065: }
1066: }
1067:
1068: private boolean isModified(String classname) {
1069: Long last_modification_time = sModificationTimes.get(classname);
1070: if (null == last_modification_time) {
1071: return false;
1072: }
1073:
1074: long current_modification_time = getClassModificationTime(classname);
1075: if (current_modification_time <= last_modification_time
1076: .longValue()) {
1077: return false;
1078: }
1079:
1080: return true;
1081: }
1082: }
|