001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package org.openimaj.aop;
031
032import java.io.ByteArrayInputStream;
033import java.lang.instrument.ClassFileTransformer;
034import java.lang.instrument.IllegalClassFormatException;
035import java.security.ProtectionDomain;
036import java.util.ArrayList;
037import java.util.List;
038
039import org.apache.logging.log4j.Logger;
040import org.apache.logging.log4j.LogManager;
041
042import javassist.CannotCompileException;
043import javassist.ClassPool;
044import javassist.CtClass;
045import javassist.NotFoundException;
046import javassist.Translator;
047
048/**
049 * A {@link ClassFileTransformer} that applies one or more
050 * {@link ClassTransformer}s to a class before it is loaded.
051 *
052 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
053 */
054public class MultiTransformClassFileTransformer implements ClassFileTransformer, Translator {
055        private static Logger logger = LogManager.getLogger(MultiTransformClassFileTransformer.class);
056
057        private ClassPool classPool;
058        private List<ClassTransformer> transformers = new ArrayList<ClassTransformer>();
059
060        /**
061         * Construct with the given {@link ClassTransformer}s.
062         *
063         * @param t1
064         *            the first transformer
065         * @param transformers
066         *            any additional transformers
067         */
068        public MultiTransformClassFileTransformer(ClassTransformer t1, ClassTransformer... transformers) {
069                this.transformers.add(t1);
070
071                for (final ClassTransformer ct : transformers)
072                        this.transformers.add(ct);
073        }
074
075        @Override
076        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
077                        ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException
078        {
079                return transform(className, classfileBuffer);
080        }
081
082        /**
083         * Transform the given class.
084         *
085         * @param className
086         *            the name of the class
087         * @param classfileBuffer
088         *            the class bytes
089         * @return the transformed bytes
090         */
091        public byte[] transform(String className, byte[] classfileBuffer) {
092                if (classPool == null)
093                        classPool = ClassPool.getDefault();
094
095                try {
096                        final CtClass ctclz = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
097
098                        transform(className, ctclz);
099
100                        return ctclz.toBytecode();
101                } catch (final Exception e) {
102                        e.printStackTrace();
103                        logger.error("Error transforming class " + className);
104                        return classfileBuffer;
105                }
106        }
107
108        private void transform(String className, CtClass ctclz) throws Exception {
109                for (final ClassTransformer ct : transformers)
110                        ct.transform(className, ctclz);
111        }
112
113        @Override
114        public void start(ClassPool pool) throws NotFoundException, CannotCompileException {
115                this.classPool = pool;
116        }
117
118        @Override
119        public void onLoad(ClassPool pool, String classname) throws NotFoundException, CannotCompileException {
120                if (classname.endsWith(".package-info"))
121                        return;
122
123                final CtClass clz = pool.get(classname);
124
125                try {
126                        transform(classname, clz);
127                } catch (final Exception e) {
128                        throw new CannotCompileException(e);
129                }
130        }
131}