001: /*
002: * Copyright 2001-2007 Geert Bevin <gbevin[remove] at uwyn dot com>
003: * Distributed under the terms of either:
004: * - the common development and distribution license (CDDL), v1.0; or
005: * - the GNU Lesser General Public License, v2.1 or later
006: * $Id: ClassBytesLoader.java 3811 2007-06-25 15:06:16Z gbevin $
007: */
008: package com.uwyn.rife.tools;
009:
010: import com.uwyn.rife.tools.exceptions.FileUtilsErrorException;
011: import java.io.IOException;
012: import java.lang.reflect.Constructor;
013: import java.lang.reflect.Field;
014: import java.lang.reflect.InvocationTargetException;
015: import java.lang.reflect.Method;
016: import java.net.URL;
017: import java.net.URLClassLoader;
018: import sun.misc.Resource;
019: import sun.misc.URLClassPath;
020:
021: /**
022: * Utility class to load the bytes of class files.
023: * <p>
024: * After instantiating it, the {@link #setupSunByteLoading} method can
025: * optionally be called to let the class detect if it's possible to
026: * interface with a private API that's specific to the Sun JVM. This interface
027: * can improve class byte loading performance by 40%.
028: *
029: * @author Geert Bevin (gbevin[remove] at uwyn dot com)
030: * @version $Revision: 3811 $
031: * @since 1.6
032: */
033: public class ClassBytesLoader {
034: private ClassLoader mClassLoader = null;
035: private Object mSunByteLoading = null;
036: private Method mSunByteLoadingMethod = null;
037:
038: /**
039: * Instantiates a new bytes loader for class files.
040: *
041: * @param classLoader the classloader that should be used to search for the
042: * classes
043: * @since 1.6
044: */
045: public ClassBytesLoader(ClassLoader classLoader) {
046: mClassLoader = classLoader;
047: }
048:
049: /**
050: * Tries to setup an interaction with a private Sun JVM interface that can
051: * speed up class bytes loading by 40%, when available.
052: *
053: * @return {@code true} when the interaction with the private Sun JVM
054: * interface could be setup; or<p>
055: * {@code false} otherwise
056: */
057: public boolean setupSunByteLoading() {
058: // this is an ugly hack to be able to use Sun's implementation of URLClassLoader for
059: // speed increases if it's available, otherwise we'll fallback to a regular method of
060: // reading the class bytes
061: try {
062: Field ucp_field = URLClassLoader.class
063: .getDeclaredField("ucp");
064: ucp_field.setAccessible(true);
065: Object ucp = ucp_field.get(mClassLoader);
066: if (ucp.getClass().getName()
067: .equals("sun.misc.URLClassPath")) {
068: Class urlclasspath_class = mClassLoader
069: .loadClass("sun.misc.URLClassPath");
070: Class byteloading_class = mClassLoader
071: .loadClass(getClass().getName()
072: + "$SunByteLoading");
073: Constructor byteloading_constructor = byteloading_class
074: .getDeclaredConstructor(urlclasspath_class);
075: byteloading_constructor.setAccessible(true);
076: Object byteloading = byteloading_constructor
077: .newInstance(ucp);
078: Method byteloading_method = byteloading_class
079: .getDeclaredMethod("getBytes", String.class);
080: byteloading_method.setAccessible(true);
081:
082: mSunByteLoading = byteloading;
083: mSunByteLoadingMethod = byteloading_method;
084: }
085: } catch (Throwable e) {
086: mSunByteLoading = null;
087: mSunByteLoadingMethod = null;
088: }
089:
090: return isUsingSunByteLoading();
091: }
092:
093: /**
094: * Retrieves a byte array that contains the bytecode for a specific Java
095: * class.
096: *
097: * @param classFileName the file name of the class whose bytes should be
098: * loaded, note that this is not the Java FQN ({@code com.uwyn.rife.Version}),
099: * but the real name of the file resource ({@code com/uwyn/rife/Version.java})
100: * @return an array with the bytes of the class; or<p>
101: * {@code null} if no bytes could be loaded
102: * @throws FileUtilsErrorException if an error occurred during the loading
103: * of the class bytes
104: * @see #getClassBytes(String, URL)
105: * @since 1.6
106: */
107: public byte[] getClassBytes(String classFileName)
108: throws ClassNotFoundException {
109: return getClassBytes(classFileName, null);
110: }
111:
112: /**
113: * Retrieves a byte array that contains the bytecode for a specific Java
114: * class.
115: *
116: * @param classFileName the file name of the class whose bytes should be
117: * loaded, note that this is not the Java FQN ({@code com.uwyn.rife.Version}),
118: * but the real name of the file resource ({@code com/uwyn/rife/Version.java})
119: * @param classResource the resource that can be used to load the class
120: * bytes from if it couldn't be obtained by using the file name, if no
121: * resource is provided and the bytes couldn't be loaded by simply using
122: * the class' file name, a resource will be looked up for the file name
123: * through the class loader that was provided to the constructor
124: * @return an array with the bytes of the class; or<p>
125: * {@code null} if no bytes could be loaded
126: * @throws FileUtilsErrorException if an error occurred during the loading
127: * of the class bytes
128: * @see #getClassBytes(String)
129: * @since 1.6
130: */
131: public byte[] getClassBytes(String classFileName, URL classResource)
132: throws ClassNotFoundException {
133: byte[] raw_bytes = null;
134:
135: // this is a hack to be able to use Sun's implementation of URLClassLoader for
136: // speed increases if it's available, otherwise we'll fallback to a regular method of
137: // reading the class bytes
138: if (classFileName != null && isUsingSunByteLoading()) {
139: try {
140: raw_bytes = (byte[]) mSunByteLoadingMethod.invoke(
141: mSunByteLoading, classFileName);
142: } catch (IllegalAccessException e) {
143: raw_bytes = null;
144: } catch (IllegalArgumentException e) {
145: raw_bytes = null;
146: } catch (InvocationTargetException e) {
147: raw_bytes = null;
148: }
149: }
150:
151: // get the class bytes through a regular method that works on any JVM
152: if (null == raw_bytes) {
153: if (null == classResource && classFileName != null) {
154: classResource = mClassLoader.getResource(classFileName);
155: }
156:
157: if (classResource != null) {
158: try {
159: raw_bytes = FileUtils.readBytes(classResource);
160: } catch (FileUtilsErrorException e) {
161: throw new ClassNotFoundException(
162: "Unexpected error while reading the bytes of the class resource '"
163: + classResource + "'.", e);
164: }
165: }
166: }
167:
168: return raw_bytes;
169: }
170:
171: /**
172: * Indicates whether this class is using the private Sun JVM interface to
173: * speed up class bytes loading.
174: *
175: * @return {@code true} when this class is using the private Sun JVM
176: * interface; or<p>
177: * {@code false} if this is not the case
178: * @since 1.6
179: */
180: public boolean isUsingSunByteLoading() {
181: return mSunByteLoading != null;
182: }
183:
184: @SuppressWarnings("unused")
185: private static class SunByteLoading {
186: URLClassPath mUrlClassPath = null;
187:
188: private SunByteLoading(URLClassPath ucp) {
189: mUrlClassPath = ucp;
190: }
191:
192: private byte[] getBytes(String path) {
193: Resource resource = mUrlClassPath.getResource(path, false);
194: if (resource != null) {
195: try {
196: return resource.getBytes();
197: } catch (IOException e) {
198: return null;
199: }
200: } else {
201: return null;
202: }
203: }
204: }
205: }
|