001    package org.shiftone.jrat.inject.bytecode;
002    
003    
004    import org.shiftone.jrat.core.JRatException;
005    import org.shiftone.jrat.core.shutdown.ShutdownListener;
006    import org.shiftone.jrat.inject.bytecode.asm.AsmInjectorStrategy;
007    import org.shiftone.jrat.util.AtomicLong;
008    import org.shiftone.jrat.util.io.IOUtil;
009    import org.shiftone.jrat.util.log.Logger;
010    
011    import java.io.InputStream;
012    
013    
014    /**
015     * This class is the application's interface to the bytecode injection package.
016     * Application code should not use the InjectorStrategy classes directly. <p/>
017     * Node: setting the system property <b>jrat.transformer.strategy</b> will
018     * change the InjectorStrategy. Avalible options are <b>bcel</b> and
019     * <b>javassist</b>
020     *
021     * @author jeff@shiftone.org (Jeff Drost)
022     */
023    public class Transformer implements ShutdownListener, TransformerMBean {
024    
025        private static final Logger LOG = Logger.getLogger(Transformer.class);
026        private static final String UNKNOWN_SOURCE = "[unknown source]";
027        private InjectorStrategy injectorStrategy;
028        private AtomicLong transformedClassCount = new AtomicLong();
029        private AtomicLong totalInputBytes = new AtomicLong();
030        private AtomicLong totalOutputBytes = new AtomicLong();
031        private AtomicLong totalTransformTime = new AtomicLong();
032    
033        public Transformer(InjectorStrategy injectorStrategy) {
034            this.injectorStrategy = injectorStrategy;
035        }
036    
037    
038        public Transformer() {
039            this.injectorStrategy = new AsmInjectorStrategy();
040        }
041    
042    
043        public byte[] inject(byte[] inputClassData, TransformerOptions options) {
044            return inject(inputClassData, UNKNOWN_SOURCE, options);
045        }
046    
047    
048        public byte[] inject(InputStream inputClassData, TransformerOptions options) {
049            return inject(inputClassData, UNKNOWN_SOURCE, options);
050        }
051    
052    
053        public byte[] inject(byte[] input, String sourceName, TransformerOptions options) {
054    
055            try {
056                long start = System.currentTimeMillis();
057                byte[] output = injectorStrategy.inject(input, options);
058    
059                totalTransformTime.addAndGet(System.currentTimeMillis() - start);
060                totalInputBytes.addAndGet(input.length);
061                totalOutputBytes.addAndGet(output.length);
062                transformedClassCount.incrementAndGet();
063    
064                return output;
065            }
066            catch (Exception e) {
067                throw new JRatException("error injecting : " + sourceName, e);
068            }
069        }
070    
071    
072        public long getTransformedClassCount() {
073            return transformedClassCount.get();
074        }
075    
076    
077        public double getAverageTransformTimeMs() {
078    
079            return (transformedClassCount.get() == 0)
080                    ? 0.0
081                    : (double) totalTransformTime.get() / (double) transformedClassCount.get();
082        }
083    
084    
085        public double getAverageBloatPercent() {
086            return (double) ((totalOutputBytes.get() * 100.0) / totalInputBytes.get()) - 100.0;
087        }
088    
089    
090        public String getInjectorStrategyText() {
091            return injectorStrategy.toString();
092        }
093    
094    
095        public byte[] inject(InputStream inputClassData, String sourceName, TransformerOptions options) {
096    
097            try {
098                byte[] inputClassDataBytes = IOUtil.readAndClose(inputClassData);
099    
100                return inject(inputClassDataBytes, sourceName, options);
101            }
102            catch (Exception e) {
103                throw new JRatException("error injecting stream : " + sourceName, e);
104            }
105        }
106    
107    
108        public void shutdown() {
109            LOG.info("transformed " + transformedClassCount + " classe(s)");
110        }
111    
112    
113        public String toString() {
114            return "Transformer[" + injectorStrategy + "]";
115        }
116    }