001: /*
002: * Copyright 2003,2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package net.sf.cglib.core;
017:
018: import java.io.*;
019: import java.util.*;
020: import java.lang.ref.*;
021: import java.lang.reflect.Method;
022: import java.lang.reflect.InvocationTargetException;
023: import java.security.ProtectionDomain;
024:
025: import org.objectweb.asm.ClassReader;
026: import org.objectweb.asm.ClassVisitor;
027: import org.objectweb.asm.ClassWriter;
028: import org.objectweb.asm.Type;
029:
030: /**
031: * Abstract class for all code-generating CGLIB utilities.
032: * In addition to caching generated classes for performance, it provides hooks for
033: * customizing the <code>ClassLoader</code>, name of the generated class, and transformations
034: * applied before generation.
035: */
036: abstract public class AbstractClassGenerator implements ClassGenerator {
037: private static final Object NAME_KEY = new Object();
038: private static final ThreadLocal CURRENT = new ThreadLocal();
039:
040: private GeneratorStrategy strategy = DefaultGeneratorStrategy.INSTANCE;
041: private NamingPolicy namingPolicy = DefaultNamingPolicy.INSTANCE;
042: private Source source;
043: private ClassLoader classLoader;
044: private String namePrefix;
045: private Object key;
046: private boolean useCache = true;
047: private String className;
048: protected boolean attemptLoad;
049:
050: protected static class Source {
051: String name;
052: Map cache = new WeakHashMap();
053:
054: public Source(String name) {
055: this .name = name;
056: }
057: }
058:
059: protected AbstractClassGenerator(Source source) {
060: this .source = source;
061: }
062:
063: protected void setNamePrefix(String namePrefix) {
064: this .namePrefix = namePrefix;
065: }
066:
067: final protected String getClassName() {
068: if (className == null) {
069: className = getClassName(getClassLoader());
070: }
071: return className;
072: }
073:
074: private String getClassName(final ClassLoader loader) {
075: final Set nameCache = getClassNameCache(loader);
076: return namingPolicy.getClassName(namePrefix, source.name, key,
077: new Predicate() {
078: public boolean evaluate(Object arg) {
079: return nameCache.contains(arg);
080: }
081: });
082: }
083:
084: private Set getClassNameCache(ClassLoader loader) {
085: return (Set) ((Map) source.cache.get(loader)).get(NAME_KEY);
086: }
087:
088: /**
089: * Set the <code>ClassLoader</code> in which the class will be generated.
090: * Concrete subclasses of <code>AbstractClassGenerator</code> (such as <code>Enhancer</code>)
091: * will try to choose an appropriate default if this is unset.
092: * <p/>
093: * Classes are cached per-<code>ClassLoader</code> using a <code>WeakHashMap</code>, to allow
094: * the generated classes to be removed when the associated loader is garbage collected.
095: *
096: * @param classLoader the loader to generate the new class with, or null to use the default
097: */
098: public void setClassLoader(ClassLoader classLoader) {
099: this .classLoader = classLoader;
100: }
101:
102: /**
103: * Override the default naming policy.
104: *
105: * @param namingPolicy the custom policy, or null to use the default
106: * @see DefaultNamingPolicy
107: */
108: public void setNamingPolicy(NamingPolicy namingPolicy) {
109: if (namingPolicy == null) {
110: namingPolicy = DefaultNamingPolicy.INSTANCE;
111: }
112: this .namingPolicy = namingPolicy;
113: }
114:
115: /**
116: * @see #setNamingPolicy
117: */
118: public NamingPolicy getNamingPolicy() {
119: return namingPolicy;
120: }
121:
122: /**
123: * Whether use and update the static cache of generated classes
124: * for a class with the same properties. Default is <code>true</code>.
125: */
126: public void setUseCache(boolean useCache) {
127: this .useCache = useCache;
128: }
129:
130: /**
131: * @see #setUseCache
132: */
133: public boolean getUseCache() {
134: return useCache;
135: }
136:
137: /**
138: * If set, CGLIB will attempt to load classes from the specified
139: * <code>ClassLoader</code> before generating them. Because generated
140: * class names are not guaranteed to be unique, the default is <code>false</code>.
141: */
142: public void setAttemptLoad(boolean attemptLoad) {
143: this .attemptLoad = attemptLoad;
144: }
145:
146: public boolean getAttemptLoad() {
147: return attemptLoad;
148: }
149:
150: /**
151: * Set the strategy to use to create the bytecode from this generator.
152: * By default an instance of {@see DefaultGeneratorStrategy} is used.
153: */
154: public void setStrategy(GeneratorStrategy strategy) {
155: if (strategy == null) {
156: strategy = DefaultGeneratorStrategy.INSTANCE;
157: }
158: this .strategy = strategy;
159: }
160:
161: /**
162: * @see #setStrategy
163: */
164: public GeneratorStrategy getStrategy() {
165: return strategy;
166: }
167:
168: /**
169: * Used internally by CGLIB. Returns the <code>AbstractClassGenerator</code>
170: * that is being used to generate a class in the current thread.
171: */
172: public static AbstractClassGenerator getCurrent() {
173: return (AbstractClassGenerator) CURRENT.get();
174: }
175:
176: public ClassLoader getClassLoader() {
177: ClassLoader t = classLoader;
178: if (t == null) {
179: t = getDefaultClassLoader();
180: }
181: if (t == null) {
182: t = getClass().getClassLoader();
183: }
184: if (t == null) {
185: t = Thread.currentThread().getContextClassLoader();
186: }
187: if (t == null) {
188: throw new IllegalStateException(
189: "Cannot determine classloader");
190: }
191: return t;
192: }
193:
194: abstract protected ClassLoader getDefaultClassLoader();
195:
196: protected Object create(Object key) {
197: try {
198: Object instance = null;
199: synchronized (source) {
200: ClassLoader loader = getClassLoader();
201: Map cache2 = null;
202: cache2 = (Map) source.cache.get(loader);
203: if (cache2 == null) {
204: cache2 = new HashMap();
205: cache2.put(NAME_KEY, new HashSet());
206: source.cache.put(loader, cache2);
207: } else if (useCache) {
208: Reference ref = (Reference) cache2.get(key);
209: instance = (ref == null) ? null : ref.get();
210: }
211: if (instance == null) {
212: Object save = CURRENT.get();
213: CURRENT.set(this );
214: try {
215: this .key = key;
216: Class gen = null;
217: if (attemptLoad) {
218: try {
219: gen = loader.loadClass(getClassName());
220: } catch (ClassNotFoundException e) {
221: // ignore
222: }
223: }
224: if (gen == null) {
225: byte[] b = strategy.generate(this );
226:
227: // --- START - AW additions ---
228: byte[] transformed;
229: Class awPreProcessor = null;
230: try {
231: awPreProcessor = getClassLoader()
232: .loadClass(
233: "org.codehaus.aspectwerkz.hook.impl.ClassPreProcessorHelper");
234: Method defineClass = awPreProcessor
235: .getMethod(
236: "defineClass0Pre",
237: new Class[] {
238: ClassLoader.class,
239: String.class,
240: byte[].class,
241: int.class,
242: int.class,
243: ProtectionDomain.class });
244:
245: // pipe the bytes through the AW weaver
246: transformed = (byte[]) defineClass
247: .invoke(null, new Object[] {
248: loader, className, b,
249: new Integer(0),
250: new Integer(b.length),
251: null });
252: } catch (ClassNotFoundException e) {
253: // AW not on classpath ignore and go on
254: transformed = b;
255: } catch (InvocationTargetException e) {
256: System.err
257: .println("WARNING: AspectWerkz preprocessor found on classpath but can not be used due to: "
258: + e
259: .getTargetException()
260: .toString());
261: // AW not on classpath ignore and go on
262: transformed = b;
263: } catch (Exception e) {
264: System.err
265: .println("WARNING: AspectWerkz preprocessor found on classpath but can not be used due to: "
266: + e.toString());
267: // AW not on classpath ignore and go on
268: transformed = b;
269: }
270: // --- END - AW additions ---
271:
272: String className = ClassNameReader
273: .getClassName(new ClassReader(
274: transformed));
275: getClassNameCache(loader).add(className);
276: gen = ReflectUtils.defineClass(className,
277: transformed, loader);
278: }
279: instance = firstInstance(gen);
280: if (useCache) {
281: cache2
282: .put(key, new SoftReference(
283: instance));
284: }
285: return instance;
286: } finally {
287: CURRENT.set(save);
288: }
289: }
290: }
291: return nextInstance(instance);
292: } catch (RuntimeException e) {
293: throw e;
294: } catch (Error e) {
295: throw e;
296: } catch (Exception e) {
297: throw new CodeGenerationException(e);
298: }
299: }
300:
301: abstract protected Object firstInstance(Class type)
302: throws Exception;
303:
304: abstract protected Object nextInstance(Object instance)
305: throws Exception;
306: }
|