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 }