/******************************************************************************* * Copyright (c) 2010 Eric Bodden. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Eric Bodden - initial API and implementation ******************************************************************************/ package de.bodden.tamiflex.playout; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.instrument.Instrumentation; import java.lang.instrument.UnmodifiableClassException; import java.net.Socket; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.List; import java.util.Properties; import java.util.jar.JarFile; import de.bodden.tamiflex.normalizer.Hasher; import de.bodden.tamiflex.playout.rt.ReflLogger; import de.bodden.tamiflex.playout.rt.ShutdownStatus; public class Agent { public final static String PKGNAME = Agent.class.getPackage().getName().replace('.', '/'); private static final boolean CAN_RETRANSFORM = true; private static ClassDumper classDumper; private static boolean dontDump = false; private static boolean dontNormalize = false; private static boolean count = false; private static boolean useDeclaredTypes; private static boolean verbose = false; private static String outPath = "out"; private static String transformations = ""; private static Socket socket; public static void premain(String agentArgs, Instrumentation inst) throws IOException, ClassNotFoundException, UnmodifiableClassException, URISyntaxException, InterruptedException { if(!inst.isRetransformClassesSupported()) { throw new RuntimeException("retransformation not supported"); } System.out.println("============================================================"); System.out.println("TamiFlex Play-Out Agent Version "+Agent.class.getPackage().getImplementationVersion()); loadProperties(); appendRtJarToBootClassPath(inst); ReflLogger.setMustCount(count); ReflLogger.setuseDeclaredTypes(useDeclaredTypes); if(dontNormalize) Hasher.dontNormalize(); String hostAndPort = System.getenv("TAMIFLEX_ECLIPSE"); if(hostAndPort!=null && !hostAndPort.isEmpty()) { //online mode, open Socket String[] split = hostAndPort.split(":"); if(split.length!=2) { System.err.println("Illegal argument: "+agentArgs); System.err.println("Expected format: hostname:socket"); System.exit(1); } String host = split[0]; int port = Integer.parseInt(split[1]); socket = new Socket(host, port); ReflLogger.setSocket(socket); } if(outPath==null||outPath.isEmpty()) { System.err.println("No outDir given!"); } File outDir = new File(outPath); if(outDir.exists()) { if(!outDir.isDirectory()) { System.err.println(outDir+ "is not a directory"); System.exit(1); } } else { boolean res = outDir.mkdirs(); if(!res) { System.err.println("Cannot create directory "+outDir); System.exit(1); } } final File logFile = new File(outDir,"refl.log"); dumpLoadedClasses(inst,outDir,dontDump,verbose); ReflLogger.setLogFile(logFile); if(!transformations.isEmpty()) instrumentClassesForLogging(inst); inst.addTransformer(classDumper, CAN_RETRANSFORM); final boolean verboseOutput = verbose; Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { ShutdownStatus.hasShutDown = true; if(socket!=null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } classDumper.writeClassesToDisk(); ReflLogger.writeLogfileToDisk(verboseOutput,classDumper.newClasses); String agentJarDir = agentJarFilePath.substring(0, agentJarFilePath.lastIndexOf('/')); String version = Agent.class.getPackage().getImplementationVersion(); String dbJarPath = agentJarDir+'/'+"dbdumper-"+version+".jar"; try { File jarfile = new File(new URI(dbJarPath)); if(jarfile.exists()) { System.out.println("Database JAR file found. Will attempt to dump log file to database."); DBDumper.dumpFileToDatabase(jarfile,logFile); } } catch (URISyntaxException e) { e.printStackTrace(); } } }); System.out.println("============================================================"); } private static void loadProperties() { String propFileName = "poa.properties"; String userPropFilePath = System.getProperty("user.home")+File.separator+".tamiflex"+File.separator+propFileName; copyPropFileIfMissing(userPropFilePath); String[] paths = { propFileName, userPropFilePath }; InputStream is = null; File foundFile= null; for (String path : paths) { File file = new File(path); if(file.exists() && file.canRead()) { try { is = new FileInputStream(file); foundFile = file; break; } catch (FileNotFoundException e) { e.printStackTrace(); } } } if(is==null) throw new InternalError("No properties files found!"); Properties props = new Properties(); try { props.load(is); if(!props.containsKey("quiet") || !props.get("quiet").equals("true")) { String path = (foundFile!=null) ? foundFile.getAbsolutePath() : "<JAR FILE>!/"+propFileName; System.out.println("Loaded properties from "+path); } if(props.containsKey("count") && props.get("count").equals("true")) count = true; if(props.containsKey("dontDumpClasses") && props.get("dontDumpClasses").equals("true")) dontDump = true; if(props.containsKey("dontNormalize") && props.get("dontNormalize").equals("true")) dontNormalize = true; if(props.containsKey("verbose") && props.get("verbose").equals("true")) verbose = true; if(props.containsKey("useDeclaredTypes") && props.get("useDeclaredTypes").equals("true")) useDeclaredTypes = true; if(props.containsKey("outDir")) outPath = (String) props.get("outDir"); if(props.containsKey("transformations")) transformations = (String) props.get("transformations"); } catch (IOException e) { throw new InternalError("Error loading default properties file: "+e.getMessage()); } } private static void copyPropFileIfMissing(String userPropFilePath) { File f = new File(userPropFilePath); if(!f.exists()) { File dir = f.getParentFile(); if(!dir.exists()) dir.mkdirs(); try { FileOutputStream fos = new FileOutputStream(f); InputStream is = Agent.class.getClassLoader().getResourceAsStream(f.getName()); if(is==null) { fos.close(); throw new InternalError("No default properties file found in agent JAR file!"); } int i; while((i=is.read())!=-1) { fos.write(i); } fos.close(); is.close(); } catch (Exception e) { e.printStackTrace(); } } } private static void dumpLoadedClasses(Instrumentation inst, File outDir, boolean dontReallyDump, boolean verbose) throws UnmodifiableClassException { classDumper = new ClassDumper(outDir,dontReallyDump,verbose); inst.addTransformer(classDumper, CAN_RETRANSFORM); //dump all classes that are already loaded for (Class<?> c : inst.getAllLoadedClasses()) { if(inst.isModifiableClass(c)) { inst.retransformClasses(c); } else { if(!c.isPrimitive() && !c.isArray() && (c.getPackage()==null || !c.getPackage().getName().startsWith("java.lang"))){ System.err.println("WARNING: Cannot dump class "+c.getName()); } } } inst.removeTransformer(classDumper); } private static void instrumentClassesForLogging(Instrumentation inst) throws UnmodifiableClassException { ReflectionMonitor reflMonitor = new ReflectionMonitor(transformations, verbose); inst.addTransformer(reflMonitor, CAN_RETRANSFORM); List<Class<?>> affectedClasses = reflMonitor.getAffectedClasses(); inst.retransformClasses(affectedClasses.toArray(new Class<?>[affectedClasses.size()])); inst.removeTransformer(reflMonitor); } private static void appendRtJarToBootClassPath(Instrumentation inst) throws URISyntaxException, IOException { URL locationOfAgent = Agent.class.getResource("/de/bodden/tamiflex/playout/rt/ReflLogger.class"); if(locationOfAgent==null) { System.err.println("Support library for reflection log not found on classpath."); System.exit(1); } agentJarFilePath = locationOfAgent.getPath().substring(0, locationOfAgent.getPath().indexOf("!")); URI uri = new URI(agentJarFilePath); JarFile jarFile = new JarFile(new File(uri)); inst.appendToBootstrapClassLoaderSearch(jarFile); } public static void main(String[] args) { usage(); } private static void usage() { System.out.println("============================================================"); System.out.println("TamiFlex Play-Out Agent Version "+Agent.class.getPackage().getImplementationVersion()); System.out.println(DISCLAIMER); System.out.println("============================================================"); System.exit(1); } private final static String DISCLAIMER= "\n\nCopyright (c) 2010-2011 Eric Bodden and others.\n" + "\n" + "DISCLAIMER: USE OF THIS SOFTWARE IS AT OWN RISK.\n" + "\n" + "All rights reserved. This program and the accompanying materials\n" + "are made available under the terms of the Eclipse Public License v1.0\n" + "which accompanies this distribution, and is available at\n" + "http://www.eclipse.org/legal/epl-v10.html"; private static String agentJarFilePath; }