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 }