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}