AbstractClassGenerator.java revision 674060f01e9090cd21b3c5656cc3204912ad17a6
1/* 2 * Copyright 2003,2004 The Apache Software Foundation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package org.mockito.cglib.core; 17 18import java.io.*; 19import java.util.*; 20import java.lang.ref.*; 21 22import org.mockito.asm.ClassReader; 23import org.mockito.asm.ClassVisitor; 24import org.mockito.asm.ClassWriter; 25import org.mockito.asm.Type; 26 27/** 28 * Abstract class for all code-generating CGLIB utilities. 29 * In addition to caching generated classes for performance, it provides hooks for 30 * customizing the <code>ClassLoader</code>, name of the generated class, and transformations 31 * applied before generation. 32 */ 33abstract public class AbstractClassGenerator 34implements ClassGenerator 35{ 36 private static final Object NAME_KEY = new Object(); 37 private static final ThreadLocal CURRENT = new ThreadLocal(); 38 39 private GeneratorStrategy strategy = DefaultGeneratorStrategy.INSTANCE; 40 private NamingPolicy namingPolicy = DefaultNamingPolicy.INSTANCE; 41 private Source source; 42 private ClassLoader classLoader; 43 private String namePrefix; 44 private Object key; 45 private boolean useCache = true; 46 private String className; 47 private boolean attemptLoad; 48 49 protected static class Source { 50 String name; 51 Map cache = new WeakHashMap(); 52 public Source(String name) { 53 this.name = name; 54 } 55 } 56 57 protected AbstractClassGenerator(Source source) { 58 this.source = source; 59 } 60 61 protected void setNamePrefix(String namePrefix) { 62 this.namePrefix = namePrefix; 63 } 64 65 final protected String getClassName() { 66 if (className == null) 67 className = getClassName(getClassLoader()); 68 return className; 69 } 70 71 private String getClassName(final ClassLoader loader) { 72 final Set nameCache = getClassNameCache(loader); 73 return namingPolicy.getClassName(namePrefix, source.name, key, new Predicate() { 74 public boolean evaluate(Object arg) { 75 return nameCache.contains(arg); 76 } 77 }); 78 } 79 80 private Set getClassNameCache(ClassLoader loader) { 81 return (Set)((Map)source.cache.get(loader)).get(NAME_KEY); 82 } 83 84 /** 85 * Set the <code>ClassLoader</code> in which the class will be generated. 86 * Concrete subclasses of <code>AbstractClassGenerator</code> (such as <code>Enhancer</code>) 87 * will try to choose an appropriate default if this is unset. 88 * <p> 89 * Classes are cached per-<code>ClassLoader</code> using a <code>WeakHashMap</code>, to allow 90 * the generated classes to be removed when the associated loader is garbage collected. 91 * @param classLoader the loader to generate the new class with, or null to use the default 92 */ 93 public void setClassLoader(ClassLoader classLoader) { 94 this.classLoader = classLoader; 95 } 96 97 /** 98 * Override the default naming policy. 99 * @see DefaultNamingPolicy 100 * @param namingPolicy the custom policy, or null to use the default 101 */ 102 public void setNamingPolicy(NamingPolicy namingPolicy) { 103 if (namingPolicy == null) 104 namingPolicy = DefaultNamingPolicy.INSTANCE; 105 this.namingPolicy = namingPolicy; 106 } 107 108 /** 109 * @see #setNamingPolicy 110 */ 111 public NamingPolicy getNamingPolicy() { 112 return namingPolicy; 113 } 114 115 /** 116 * Whether use and update the static cache of generated classes 117 * for a class with the same properties. Default is <code>true</code>. 118 */ 119 public void setUseCache(boolean useCache) { 120 this.useCache = useCache; 121 } 122 123 /** 124 * @see #setUseCache 125 */ 126 public boolean getUseCache() { 127 return useCache; 128 } 129 130 /** 131 * If set, CGLIB will attempt to load classes from the specified 132 * <code>ClassLoader</code> before generating them. Because generated 133 * class names are not guaranteed to be unique, the default is <code>false</code>. 134 */ 135 public void setAttemptLoad(boolean attemptLoad) { 136 this.attemptLoad = attemptLoad; 137 } 138 139 public boolean getAttemptLoad() { 140 return attemptLoad; 141 } 142 143 /** 144 * Set the strategy to use to create the bytecode from this generator. 145 * By default an instance of {@see DefaultGeneratorStrategy} is used. 146 */ 147 public void setStrategy(GeneratorStrategy strategy) { 148 if (strategy == null) 149 strategy = DefaultGeneratorStrategy.INSTANCE; 150 this.strategy = strategy; 151 } 152 153 /** 154 * @see #setStrategy 155 */ 156 public GeneratorStrategy getStrategy() { 157 return strategy; 158 } 159 160 /** 161 * Used internally by CGLIB. Returns the <code>AbstractClassGenerator</code> 162 * that is being used to generate a class in the current thread. 163 */ 164 public static AbstractClassGenerator getCurrent() { 165 return (AbstractClassGenerator)CURRENT.get(); 166 } 167 168 public ClassLoader getClassLoader() { 169 ClassLoader t = classLoader; 170 if (t == null) { 171 t = getDefaultClassLoader(); 172 } 173 if (t == null) { 174 t = getClass().getClassLoader(); 175 } 176 if (t == null) { 177 t = Thread.currentThread().getContextClassLoader(); 178 } 179 if (t == null) { 180 throw new IllegalStateException("Cannot determine classloader"); 181 } 182 return t; 183 } 184 185 abstract protected ClassLoader getDefaultClassLoader(); 186 187 protected Object create(Object key) { 188 try { 189 Class gen = null; 190 191 synchronized (source) { 192 ClassLoader loader = getClassLoader(); 193 Map cache2 = null; 194 cache2 = (Map)source.cache.get(loader); 195 if (cache2 == null) { 196 cache2 = new HashMap(); 197 cache2.put(NAME_KEY, new HashSet()); 198 source.cache.put(loader, cache2); 199 } else if (useCache) { 200 Reference ref = (Reference)cache2.get(key); 201 gen = (Class) (( ref == null ) ? null : ref.get()); 202 } 203 if (gen == null) { 204 Object save = CURRENT.get(); 205 CURRENT.set(this); 206 try { 207 this.key = key; 208 209 if (attemptLoad) { 210 try { 211 gen = loader.loadClass(getClassName()); 212 } catch (ClassNotFoundException e) { 213 // ignore 214 } 215 } 216 if (gen == null) { 217 byte[] b = strategy.generate(this); 218 String className = ClassNameReader.getClassName(new ClassReader(b)); 219 getClassNameCache(loader).add(className); 220 gen = ReflectUtils.defineClass(className, b, loader); 221 } 222 223 if (useCache) { 224 cache2.put(key, new WeakReference(gen)); 225 } 226 return firstInstance(gen); 227 } finally { 228 CURRENT.set(save); 229 } 230 } 231 } 232 return firstInstance(gen); 233 } catch (RuntimeException e) { 234 throw e; 235 } catch (Error e) { 236 throw e; 237 } catch (Exception e) { 238 throw new CodeGenerationException(e); 239 } 240 } 241 242 abstract protected Object firstInstance(Class type) throws Exception; 243 abstract protected Object nextInstance(Object instance) throws Exception; 244} 245