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 * https://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 017package org.springframework.cglib.core; 018 019import java.lang.ref.WeakReference; 020import java.security.ProtectionDomain; 021import java.util.HashSet; 022import java.util.Map; 023import java.util.Set; 024import java.util.WeakHashMap; 025 026import org.springframework.asm.ClassReader; 027import org.springframework.cglib.core.internal.Function; 028import org.springframework.cglib.core.internal.LoadingCache; 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@SuppressWarnings({"rawtypes", "unchecked"}) 037abstract public class AbstractClassGenerator<T> implements ClassGenerator { 038 039 private static final ThreadLocal CURRENT = new ThreadLocal(); 040 041 private static volatile Map<ClassLoader, ClassLoaderData> CACHE = new WeakHashMap<ClassLoader, ClassLoaderData>(); 042 043 private static final boolean DEFAULT_USE_CACHE = 044 Boolean.parseBoolean(System.getProperty("cglib.useCache", "true")); 045 046 047 private GeneratorStrategy strategy = DefaultGeneratorStrategy.INSTANCE; 048 049 private NamingPolicy namingPolicy = DefaultNamingPolicy.INSTANCE; 050 051 private Source source; 052 053 private ClassLoader classLoader; 054 055 private Class contextClass; 056 057 private String namePrefix; 058 059 private Object key; 060 061 private boolean useCache = DEFAULT_USE_CACHE; 062 063 private String className; 064 065 private boolean attemptLoad; 066 067 068 protected static class ClassLoaderData { 069 070 private final Set<String> reservedClassNames = new HashSet<String>(); 071 072 /** 073 * {@link AbstractClassGenerator} here holds "cache key" (e.g. {@link org.springframework.cglib.proxy.Enhancer} 074 * configuration), and the value is the generated class plus some additional values 075 * (see {@link #unwrapCachedValue(Object)}. 076 * <p>The generated classes can be reused as long as their classloader is reachable.</p> 077 * <p>Note: the only way to access a class is to find it through generatedClasses cache, thus 078 * the key should not expire as long as the class itself is alive (its classloader is alive).</p> 079 */ 080 private final LoadingCache<AbstractClassGenerator, Object, Object> generatedClasses; 081 082 /** 083 * Note: ClassLoaderData object is stored as a value of {@code WeakHashMap<ClassLoader, ...>} thus 084 * this classLoader reference should be weak otherwise it would make classLoader strongly reachable 085 * and alive forever. 086 * Reference queue is not required since the cleanup is handled by {@link WeakHashMap}. 087 */ 088 private final WeakReference<ClassLoader> classLoader; 089 090 private final Predicate uniqueNamePredicate = new Predicate() { 091 public boolean evaluate(Object name) { 092 return reservedClassNames.contains(name); 093 } 094 }; 095 096 private static final Function<AbstractClassGenerator, Object> GET_KEY = new Function<AbstractClassGenerator, Object>() { 097 public Object apply(AbstractClassGenerator gen) { 098 return gen.key; 099 } 100 }; 101 102 public ClassLoaderData(ClassLoader classLoader) { 103 if (classLoader == null) { 104 throw new IllegalArgumentException("classLoader == null is not yet supported"); 105 } 106 this.classLoader = new WeakReference<ClassLoader>(classLoader); 107 Function<AbstractClassGenerator, Object> load = 108 new Function<AbstractClassGenerator, Object>() { 109 public Object apply(AbstractClassGenerator gen) { 110 Class klass = gen.generate(ClassLoaderData.this); 111 return gen.wrapCachedClass(klass); 112 } 113 }; 114 generatedClasses = new LoadingCache<AbstractClassGenerator, Object, Object>(GET_KEY, load); 115 } 116 117 public ClassLoader getClassLoader() { 118 return classLoader.get(); 119 } 120 121 public void reserveName(String name) { 122 reservedClassNames.add(name); 123 } 124 125 public Predicate getUniqueNamePredicate() { 126 return uniqueNamePredicate; 127 } 128 129 public Object get(AbstractClassGenerator gen, boolean useCache) { 130 if (!useCache) { 131 return gen.generate(ClassLoaderData.this); 132 } 133 else { 134 Object cachedValue = generatedClasses.get(gen); 135 return gen.unwrapCachedValue(cachedValue); 136 } 137 } 138 } 139 140 141 protected T wrapCachedClass(Class klass) { 142 return (T) new WeakReference(klass); 143 } 144 145 protected Object unwrapCachedValue(T cached) { 146 return ((WeakReference) cached).get(); 147 } 148 149 150 protected static class Source { 151 152 String name; 153 154 public Source(String name) { 155 this.name = name; 156 } 157 } 158 159 160 protected AbstractClassGenerator(Source source) { 161 this.source = source; 162 } 163 164 protected void setNamePrefix(String namePrefix) { 165 this.namePrefix = namePrefix; 166 } 167 168 final protected String getClassName() { 169 return className; 170 } 171 172 private void setClassName(String className) { 173 this.className = className; 174 } 175 176 private String generateClassName(Predicate nameTestPredicate) { 177 return namingPolicy.getClassName(namePrefix, source.name, key, nameTestPredicate); 178 } 179 180 /** 181 * Set the <code>ClassLoader</code> in which the class will be generated. 182 * Concrete subclasses of <code>AbstractClassGenerator</code> (such as <code>Enhancer</code>) 183 * will try to choose an appropriate default if this is unset. 184 * <p> 185 * Classes are cached per-<code>ClassLoader</code> using a <code>WeakHashMap</code>, to allow 186 * the generated classes to be removed when the associated loader is garbage collected. 187 * @param classLoader the loader to generate the new class with, or null to use the default 188 */ 189 public void setClassLoader(ClassLoader classLoader) { 190 this.classLoader = classLoader; 191 } 192 193 // SPRING PATCH BEGIN 194 public void setContextClass(Class contextClass) { 195 this.contextClass = contextClass; 196 } 197 // SPRING PATCH END 198 199 /** 200 * Override the default naming policy. 201 * @param namingPolicy the custom policy, or null to use the default 202 * @see DefaultNamingPolicy 203 */ 204 public void setNamingPolicy(NamingPolicy namingPolicy) { 205 if (namingPolicy == null) 206 namingPolicy = DefaultNamingPolicy.INSTANCE; 207 this.namingPolicy = namingPolicy; 208 } 209 210 /** 211 * @see #setNamingPolicy 212 */ 213 public NamingPolicy getNamingPolicy() { 214 return namingPolicy; 215 } 216 217 /** 218 * Whether use and update the static cache of generated classes 219 * for a class with the same properties. Default is <code>true</code>. 220 */ 221 public void setUseCache(boolean useCache) { 222 this.useCache = useCache; 223 } 224 225 /** 226 * @see #setUseCache 227 */ 228 public boolean getUseCache() { 229 return useCache; 230 } 231 232 /** 233 * If set, CGLIB will attempt to load classes from the specified 234 * <code>ClassLoader</code> before generating them. Because generated 235 * class names are not guaranteed to be unique, the default is <code>false</code>. 236 */ 237 public void setAttemptLoad(boolean attemptLoad) { 238 this.attemptLoad = attemptLoad; 239 } 240 241 public boolean getAttemptLoad() { 242 return attemptLoad; 243 } 244 245 /** 246 * Set the strategy to use to create the bytecode from this generator. 247 * By default an instance of {@link DefaultGeneratorStrategy} is used. 248 */ 249 public void setStrategy(GeneratorStrategy strategy) { 250 if (strategy == null) 251 strategy = DefaultGeneratorStrategy.INSTANCE; 252 this.strategy = strategy; 253 } 254 255 /** 256 * @see #setStrategy 257 */ 258 public GeneratorStrategy getStrategy() { 259 return strategy; 260 } 261 262 /** 263 * Used internally by CGLIB. Returns the <code>AbstractClassGenerator</code> 264 * that is being used to generate a class in the current thread. 265 */ 266 public static AbstractClassGenerator getCurrent() { 267 return (AbstractClassGenerator) CURRENT.get(); 268 } 269 270 public ClassLoader getClassLoader() { 271 ClassLoader t = classLoader; 272 if (t == null) { 273 t = getDefaultClassLoader(); 274 } 275 if (t == null) { 276 t = getClass().getClassLoader(); 277 } 278 if (t == null) { 279 t = Thread.currentThread().getContextClassLoader(); 280 } 281 if (t == null) { 282 throw new IllegalStateException("Cannot determine classloader"); 283 } 284 return t; 285 } 286 287 abstract protected ClassLoader getDefaultClassLoader(); 288 289 /** 290 * Returns the protection domain to use when defining the class. 291 * <p> 292 * Default implementation returns <code>null</code> for using a default protection domain. Sub-classes may 293 * override to use a more specific protection domain. 294 * </p> 295 * @return the protection domain (<code>null</code> for using a default) 296 */ 297 protected ProtectionDomain getProtectionDomain() { 298 return null; 299 } 300 301 protected Object create(Object key) { 302 try { 303 ClassLoader loader = getClassLoader(); 304 Map<ClassLoader, ClassLoaderData> cache = CACHE; 305 ClassLoaderData data = cache.get(loader); 306 if (data == null) { 307 synchronized (AbstractClassGenerator.class) { 308 cache = CACHE; 309 data = cache.get(loader); 310 if (data == null) { 311 Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache); 312 data = new ClassLoaderData(loader); 313 newCache.put(loader, data); 314 CACHE = newCache; 315 } 316 } 317 } 318 this.key = key; 319 Object obj = data.get(this, getUseCache()); 320 if (obj instanceof Class) { 321 return firstInstanclass="sourceLineNo">322 } 323 return nextInstance(obj); 324 } 325 catch (RuntimeException | Error ex) { 326 throw ex; 327 } 328 catch (Exception ex) { 329 throw new CodeGenerationException(ex); 330 } 331 } 332 333 protected Class generate(ClassLoaderData data) { 334 Class gen; 335 Object save = CURRENT.get(); 336 CURRENT.set(this); 337 try { 338 ClassLoader classLoader = data.getClassLoader(); 339 if (classLoader == null) { 340 throw new IllegalStateException("ClassLoader is null while trying to define class " + 341 getClassName() + ". It seems that the loader has been expired from a weak reference somehow. " + 342 "Please file an issue at cglib's issue tracker."); 343 } 344 synchronized (classLoader) { 345 String name = generateClassName(data.getUniqueNamePredicate()); 346 data.reserveName(name); 347 this.setClassName(name); 348 } 349 if (attemptLoad) { 350 try { 351 gen = classLoader.loadClass(getClassName()); 352 return gen; 353 } 354 catch (ClassNotFoundException e) { 355 // ignore 356 } 357 } 358 byte[] b = strategy.generate(this); 359 String className = ClassNameReader.getClassName(new ClassReader(b)); 360 ProtectionDomain protectionDomain = getProtectionDomain(); 361 synchronized (classLoader) { // just in case 362 // SPRING PATCH BEGIN 363 gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain, contextClass); 364 // SPRING PATCH END 365 } 366 return gen; 367 } 368 catch (RuntimeException | Error ex) { 369 throw ex; 370 } 371 catch (Exception ex) { 372 throw new CodeGenerationException(ex); 373 } 374 finally { 375 CURRENT.set(save); 376 } 377 } 378 379 abstract protected Object firstInstance(Class type) throws Exception; 380 381 abstract protected Object nextInstance(Object instance) throws Exception; 382 383}