Created
September 29, 2014 20:18
-
-
Save damc-dev/4be0097ed91ca23b315a to your computer and use it in GitHub Desktop.
Byteman Custom Script Reloading
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* JBoss, Home of Professional Open Source | |
* Copyright 2008-10 Red Hat and individual contributors | |
* by the @authors tag. See the copyright.txt in the distribution for a | |
* full listing of individual contributors. | |
* | |
* This is free software; you can redistribute it and/or modify it | |
* under the terms of the GNU Lesser General Public License as | |
* published by the Free Software Foundation; either version 2.1 of | |
* the License, or (at your option) any later version. | |
* | |
* This software is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
* Lesser General Public License for more details. | |
* | |
* You should have received a copy of the GNU Lesser General Public | |
* License along with this software; if not, write to the Free | |
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |
* 02110-1301 USA, or see the FSF site: http://www.fsf.org. | |
* | |
* @authors Andrew Dinn | |
*/ | |
package com.example.proj.agent; | |
import java.io.InputStream; | |
import java.lang.instrument.Instrumentation; | |
import java.lang.instrument.ClassFileTransformer; | |
import java.lang.reflect.Constructor; | |
import java.lang.reflect.Method; | |
import java.util.jar.JarFile; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.io.File; | |
import java.io.IOException; | |
import java.io.FileInputStream; | |
import java.net.Socket; | |
/** | |
* agent class supplied at JVM startup to install byteman package bytecode transformer | |
*/ | |
public class AgentMain { | |
public static boolean firstTime = true; | |
public final static String BYTEMAN_PREFIX = "org.jboss.byteman."; | |
public static void premain(String args, Instrumentation inst) | |
throws Exception | |
{ | |
// guard against the agent being loaded twice | |
synchronized (AgentMain.class) { | |
if (firstTime) { | |
firstTime = false; | |
} else { | |
throw new Exception("Main : attempting to load Byteman agent more than once"); | |
} | |
} | |
boolean allowRedefine = false; | |
boolean installPolicy = false; | |
if (args != null) { | |
// args are supplied eparated by ',' characters | |
String[] argsArray = args.split(","); | |
// we accept extra jar files to be added to the boot/sys classpaths | |
// script files to be scanned for rules | |
// listener flag which implies use of a retransformer | |
for (String arg : argsArray) { | |
if (arg.startsWith(BOOT_PREFIX)) { | |
bootJarPaths.add(arg.substring(BOOT_PREFIX.length(), arg.length())); | |
} else if (arg.startsWith(SYS_PREFIX)) { | |
sysJarPaths.add(arg.substring(SYS_PREFIX.length(), arg.length())); | |
} else if (arg.startsWith(ADDRESS_PREFIX)) { | |
hostname = arg.substring(ADDRESS_PREFIX.length(), arg.length()); | |
// setting host name forces listener on | |
allowRedefine = true; | |
} else if (arg.startsWith(PORT_PREFIX)) { | |
try { | |
port = Integer.valueOf(arg.substring(PORT_PREFIX.length(), arg.length())); | |
if (port <= 0) { | |
System.err.println("Invalid port specified [" + port + "]"); | |
port = null; | |
} else { | |
// setting port forces listener on | |
allowRedefine = true; | |
} | |
} catch (Exception e) { | |
System.err.println("Invalid port specified [" + arg + "]. Cause: " + e); | |
} | |
} else if (arg.startsWith(SCRIPT_PREFIX)) { | |
scriptPaths.add(arg.substring(SCRIPT_PREFIX.length(), arg.length())); | |
} else if (arg.startsWith(RESOURCE_SCRIPT_PREFIX)) { | |
resourcescriptPaths.add(arg.substring(RESOURCE_SCRIPT_PREFIX.length(), arg.length())); | |
} else if (arg.startsWith(LISTENER_PREFIX)) { | |
String value = arg.substring(LISTENER_PREFIX.length(), arg.length()); | |
allowRedefine = Boolean.parseBoolean(value); | |
// clearing listener when port or host is set should be flagged | |
if (!allowRedefine && (hostname != null || port != null)) { | |
System.err.println("listener disabled with host/port set"); | |
} | |
} else if (arg.startsWith(REDEFINE_PREFIX)) { | |
// this is only for backwards compatibility -- it is the same as listener | |
String value = arg.substring(REDEFINE_PREFIX.length(), arg.length()); | |
allowRedefine = Boolean.parseBoolean(value); | |
// clearing listener when port or host is set should be flagged | |
if (!allowRedefine && (hostname != null || port != null)) { | |
System.err.println("listener disabled with host/port set"); | |
} | |
} else if (arg.startsWith(PROP_PREFIX)) { | |
// this can be used to set byteman properties | |
String prop = arg.substring(PROP_PREFIX.length(), arg.length()); | |
String value=""; | |
if (prop.startsWith(BYTEMAN_PREFIX)) { | |
int index = prop.indexOf('='); | |
if (index > 0) { | |
// need to split off the value | |
if (index == prop.length() - 1) | |
{ | |
// value is empty so just drop the = | |
prop = prop.substring(0, index); | |
} else { | |
value = prop.substring(index + 1); | |
prop = prop.substring(0, index); | |
} | |
} | |
System.out.println("Setting " + prop + "=" + value); | |
System.setProperty(prop, value); | |
} else { | |
System.err.println("Invalid property : " + prop); | |
} | |
} else if (arg.startsWith(POLICY_PREFIX)) { | |
String value = arg.substring(POLICY_PREFIX.length(), arg.length()); | |
installPolicy = Boolean.parseBoolean(value); | |
} else { | |
System.err.println("org.jboss.byteman.agent.Main:\n" + | |
" illegal agent argument : " + arg + "\n" + | |
" valid arguments are boot:<path-to-jar>, sys:<path-to-jar>, script:<path-to-script> or listener:<true-or-false>"); | |
} | |
} | |
} | |
// add any boot jars to the boot class path | |
for (String bootJarPath : bootJarPaths) { | |
try { | |
JarFile jarfile = new JarFile(new File(bootJarPath)); | |
inst.appendToBootstrapClassLoaderSearch(jarfile); | |
} catch (IOException ioe) { | |
System.err.println("org.jboss.byteman.agent.Main: unable to open boot jar file : " + bootJarPath); | |
throw ioe; | |
} | |
} | |
// add any sys jars to the system class path | |
for (String sysJarPath : sysJarPaths) { | |
try { | |
JarFile jarfile = new JarFile(new File(sysJarPath)); | |
inst.appendToSystemClassLoaderSearch(jarfile); | |
} catch (IOException ioe) { | |
System.err.println("org.jboss.byteman.agent.Main: unable to open system jar file : " + sysJarPath); | |
throw ioe; | |
} | |
} | |
// create a socket so we can be sure it is loaded before the transformer gets cerated. otherwise | |
// we seem to hit a deadlock when trying to instrument socket | |
Socket dummy = new Socket(); | |
// look up rules in any script files | |
for (String scriptPath : scriptPaths) { | |
try { | |
FileInputStream fis = new FileInputStream(scriptPath); | |
byte[] bytes = new byte[fis.available()]; | |
fis.read(bytes); | |
String ruleScript = new String(bytes); | |
scripts.add(ruleScript); | |
} catch (IOException ioe) { | |
System.err.println("org.jboss.byteman.agent.Main: unable to read rule script file : " + scriptPath); | |
throw ioe; | |
} | |
} | |
// look up rules in any resource script files | |
for (String scriptPath : resourcescriptPaths) { | |
try { | |
InputStream is = ClassLoader.getSystemResourceAsStream(scriptPath); | |
if (is == null) { | |
throw new Exception("org.jboss.byteman.agent.Main: could not read rule script resource file : " + scriptPath); | |
} | |
byte[] bytes = new byte[is.available()]; | |
is.read(bytes); | |
String ruleScript = new String(bytes); | |
scripts.add(ruleScript); | |
// merge the resource and file script paths into one list | |
scriptPaths.add(scriptPath); | |
} catch (IOException ioe) { | |
System.err.println("org.jboss.byteman.agent.Main: error reading rule script resource file : " + scriptPath); | |
throw ioe; | |
} | |
} | |
// install an instance of Transformer to instrument the bytecode | |
// n.b. this is done with boxing gloves on using explicit class loading and method invocation | |
// via reflection for a GOOD reason. This class (Main) gets laoded by the System class loader. | |
// If we refer to Transformer by name then it also gets loaded va the System class loader. | |
// But if we want to transform a bootstrap class we need Transformer (et al) to be visible | |
// from the bootstrap class loader. That will not happen until after this method has called | |
// inst.appendToBootstrapClassLoaderSearch (see above) to add the byteman jar to the path. | |
// Directly referring to Transformer will giveus two versions of Transformer et al. Not only | |
// does that cause us class mismathc problem it also means that a new done here will not install | |
// the new instance in the static field fo the oneloaded in the bootstrap loader. If instead we | |
// use boxing gloves then the byteman code wil get loaded in the bootstrap loader and its constructor | |
// will be called. | |
// | |
// Of course, if the user does not supply boot:byteman.jar as a -javaagent option then class references | |
// resolve against the system loader and injection into bootstrap classes fails. But that's still ok | |
// because the byteman classes are still only foudn in one place. | |
boolean isRedefine = inst.isRedefineClassesSupported(); | |
ClassFileTransformer transformer; | |
ClassLoader loader = ClassLoader.getSystemClassLoader(); | |
Class transformerClazz; | |
if (allowRedefine && isRedefine) { | |
transformerClazz = loader.loadClass("com.example.proj.agent.AgentRetransformer"); | |
//transformer = new Retransformer(inst, scriptPaths, scripts, true); | |
Constructor constructor = transformerClazz.getConstructor(Instrumentation.class, List.class, List.class, boolean.class); | |
transformer = (ClassFileTransformer)constructor.newInstance(new Object[] { inst, scriptPaths, scripts, isRedefine}); | |
} else { | |
transformerClazz = loader.loadClass("org.jboss.byteman.agent.Transformer"); | |
//transformer = new Transformer(inst, scriptPaths, scripts, isRedefine); | |
Constructor constructor = transformerClazz.getConstructor(Instrumentation.class, List.class, List.class, boolean.class); | |
transformer = (ClassFileTransformer)constructor.newInstance(new Object[] { inst, scriptPaths, scripts, isRedefine}); | |
} | |
inst.addTransformer(transformer, true); | |
if (allowRedefine && isRedefine) { | |
Method method = transformerClazz.getMethod("addTransformListener"); | |
method.invoke(transformer); | |
} | |
if (installPolicy) { | |
Method method = transformerClazz.getMethod("installPolicy"); | |
method.invoke(transformer); | |
} | |
if (isRedefine) { | |
Method method = transformerClazz.getMethod("installBootScripts"); | |
method.invoke(transformer); | |
} | |
} | |
public static void agentmain(String args, Instrumentation inst) throws Exception | |
{ | |
premain(args, inst); | |
} | |
/** | |
* prefix used to specify port argument for agent | |
*/ | |
private static final String PORT_PREFIX = "port:"; | |
/** | |
* prefix used to specify bind address argument for agent | |
*/ | |
private static final String ADDRESS_PREFIX = "address:"; | |
/** | |
* prefix used to specify boot jar argument for agent | |
*/ | |
private static final String BOOT_PREFIX = "boot:"; | |
/** | |
* prefix used to specify system jar argument for agent | |
*/ | |
private static final String SYS_PREFIX = "sys:"; | |
/** | |
* prefix used to request installation of an access-all-areas security | |
* policy at install time for agent code | |
*/ | |
private static final String POLICY_PREFIX = "policy:"; | |
/** | |
* prefix used to specify file script argument for agent | |
*/ | |
private static final String SCRIPT_PREFIX = "script:"; | |
/** | |
* prefix used to specify resource script argument for agent | |
*/ | |
private static final String RESOURCE_SCRIPT_PREFIX = "resourcescript:"; | |
/** | |
* prefix used to specify transformer type argument for agent | |
*/ | |
private static final String LISTENER_PREFIX = "listener:"; | |
/** | |
* for backwards compatibiltiy | |
*/ | |
private static final String REDEFINE_PREFIX = "redefine:"; | |
/** | |
* prefix used to specify system properties to be set before starting the agent | |
*/ | |
private static final String PROP_PREFIX = "prop:"; | |
/** | |
* list of paths to extra bootstrap jars supplied on command line | |
*/ | |
private static List<String> bootJarPaths = new ArrayList<String>(); | |
/** | |
* list of paths to extra system jars supplied on command line | |
*/ | |
private static List<String> sysJarPaths = new ArrayList<String>(); | |
/** | |
* list of paths to script files supplied on command line | |
*/ | |
private static List<String> scriptPaths = new ArrayList<String>(); | |
/** | |
* list of paths to resource script files supplied on command line | |
*/ | |
private static List<String> resourcescriptPaths = new ArrayList<String>(); | |
/** | |
* list of scripts read from script files | |
*/ | |
private static List<String> scripts = new ArrayList<String>(); | |
/** | |
* The hostname to bind the listener to, supplied on the command line (optional argument) | |
*/ | |
private static String hostname = null; | |
/** | |
* The port that the listener will listen to, supplied on the command line (optional argument) | |
*/ | |
private static Integer port = null; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.proj.agent; | |
import java.io.PrintWriter; | |
import java.lang.instrument.Instrumentation; | |
import java.util.HashSet; | |
import java.util.Iterator; | |
import java.util.LinkedList; | |
import java.util.List; | |
import java.util.Set; | |
import java.util.jar.JarFile; | |
import org.jboss.byteman.agent.RuleScript; | |
import org.jboss.byteman.agent.ScriptRepository; | |
import org.jboss.byteman.agent.Transform; | |
import org.jboss.byteman.agent.Transformer; | |
public class AgentRetransformer extends Transformer { | |
private Set<String> sysJars = new HashSet<String>(); // jar files that were loaded in the sys CL | |
private Set<String> bootJars = new HashSet<String>(); // jar files that were loaded in the boot CL | |
/** | |
* constructor allowing this transformer to be provided with access to the JVM's instrumentation | |
* implementation | |
* | |
* @param inst the instrumentation object used to interface to the JVM | |
*/ | |
public AgentRetransformer(Instrumentation inst, List<String> scriptPaths, List<String> scriptTexts, boolean isRedefine) | |
throws Exception | |
{ | |
super(inst, scriptPaths, scriptTexts, isRedefine); | |
//addTransformListener(hostname, port); | |
} | |
public void installScript(List<String> scriptTexts, List<String> scriptNames, PrintWriter out) throws Exception | |
{ | |
int length = scriptTexts.size(); | |
List<RuleScript> toBeAdded = new LinkedList<RuleScript>(); | |
List<RuleScript> toBeRemoved = new LinkedList<RuleScript>(); | |
for (int i = 0; i < length ; i++) { | |
String scriptText = scriptTexts.get(i); | |
String scriptName = scriptNames.get(i); | |
List<RuleScript> ruleScripts = scriptRepository.processScripts(scriptText, scriptName); | |
toBeAdded.addAll(ruleScripts); | |
} | |
for (RuleScript ruleScript : toBeAdded) { | |
String name = ruleScript.getName(); | |
String className = ruleScript.getTargetClass(); | |
String baseName = null; | |
int lastDotIdx = className.lastIndexOf('.'); | |
if (lastDotIdx >= 0) { | |
baseName = className.substring(lastDotIdx + 1); | |
} | |
RuleScript previous; | |
previous = scriptRepository.addScript(ruleScript); | |
if (previous != null) { | |
out.println("redefine rule " + name); | |
toBeRemoved.add(previous); | |
} else { | |
out.println("install rule " + name); | |
} | |
} | |
// ok, now that we have updated the indexes we need to find all classes which match the scripts and | |
// retransform them | |
// list all class names for the to be aded and to be removed scripts | |
List<String> deletedClassNames = new LinkedList<String>(); | |
for (RuleScript ruleScript : toBeRemoved) { | |
List<Transform> transforms = ruleScript.getTransformed(); | |
for (Transform transform : transforms) { | |
String className = transform.getInternalClassName(); | |
if (!deletedClassNames.contains(className)) { | |
deletedClassNames.add(className); | |
} | |
} | |
} | |
// for added scripts we have to transform anything which might be a match | |
ScriptRepository tmpRepository = new ScriptRepository(skipOverrideRules()); | |
for (RuleScript ruleScript : toBeAdded) { | |
tmpRepository.addScript(ruleScript); | |
} | |
// now look for loaded classes whose names are in the deleted list or which match added rules | |
List<Class<?>> transformed = new LinkedList<Class<?>>(); | |
for (Class clazz : inst.getAllLoadedClasses()) { | |
if (isSkipClass(clazz)) { | |
continue; | |
} | |
if (deletedClassNames.contains(clazz.getName())) { | |
transformed.add(clazz); | |
} else if (tmpRepository.matchClass(clazz)) { | |
transformed.add(clazz); | |
} | |
} | |
// retransform all classes whose rules have changed | |
if (!transformed.isEmpty()) { | |
Class<?>[] transformedArray = new Class<?>[transformed.size()]; | |
transformed.toArray(transformedArray); | |
if (Transformer.isVerbose()) { | |
for (int i = 0; i < transformed.size(); i++) { | |
System.out.println("retransforming " + transformedArray[i].getName()); | |
} | |
} | |
synchronized(this) { | |
try { | |
inst.retransformClasses(transformedArray); | |
} catch(VerifyError ve) { | |
System.out.println("Retransformer : VerifyError during retransformation : some rules may not have been correctly injected or uninjected!"); | |
ve.printStackTrace(); | |
out.println("VerifyError during retransformation : some rules may not have been correctly injected or uninjected!"); | |
ve.printStackTrace(out); | |
} | |
} | |
} | |
// now we can safely purge keys for all deleted scripts | |
for (RuleScript ruleScript : toBeRemoved) { | |
ruleScript.purge(); | |
} | |
} | |
protected void collectAffectedNames(List<RuleScript> ruleScripts, List<String> classList, List<String> interfaceList, | |
List<String> superClassList, List<String> superInterfaceList) | |
{ | |
for (RuleScript ruleScript : ruleScripts) { | |
String targetClassName = ruleScript.getTargetClass(); | |
boolean isOverride = ruleScript.isOverride(); | |
if (ruleScript.isInterface()) { | |
if (!interfaceList.contains(targetClassName)) { | |
interfaceList.add(targetClassName); | |
if (isOverride) { | |
superInterfaceList.add(targetClassName); | |
} | |
} | |
} else { | |
if (!classList.contains(targetClassName)) { | |
classList.add(targetClassName); | |
if (isOverride) { | |
superClassList.add(targetClassName); | |
} | |
} | |
} | |
} | |
} | |
protected void listScripts(PrintWriter out) throws Exception | |
{ | |
Iterator<RuleScript> iterator = scriptRepository.currentRules().iterator(); | |
if (!iterator.hasNext()) { | |
out.println("no rules installed"); | |
} else { | |
while (iterator.hasNext()) { | |
RuleScript ruleScript = iterator.next(); | |
ruleScript.writeTo(out); | |
synchronized (ruleScript) { | |
List<Transform> transformed = ruleScript.getTransformed(); | |
if (transformed != null) { | |
Iterator<Transform> iter = transformed.iterator(); | |
while (iter.hasNext()) { | |
Transform transform = iter.next(); | |
transform.writeTo(out); | |
} | |
} | |
} | |
} | |
} | |
} | |
public void addTransformListener() | |
{ | |
ScriptReload.initialize(this); | |
} | |
public void removeScripts(List<String> scriptTexts, PrintWriter out) throws Exception | |
{ | |
List<RuleScript> toBeRemoved; | |
if (scriptTexts != null) { | |
toBeRemoved = new LinkedList<RuleScript>(); | |
int length = scriptTexts.size(); | |
for (int i = 0; i < length ; i++) { | |
String scriptText = scriptTexts.get(i); | |
String[] lines = scriptText.split("\n"); | |
for (int j = 0; j < lines.length; j++) { | |
String line = lines[j].trim(); | |
if (line.startsWith("RULE ")) { | |
String name = line.substring(5).trim(); | |
RuleScript ruleScript = scriptRepository.scriptForRuleName(name); | |
if (ruleScript == null) { | |
out.print("ERROR failed to find loaded rule with name "); | |
out.println(name); | |
} else if (toBeRemoved.contains(ruleScript)) { | |
out.print("WARNING duplicate occurence for rule name "); | |
out.println(name); | |
} else { | |
toBeRemoved.add(ruleScript); | |
} | |
} | |
} | |
} | |
} else { | |
toBeRemoved = scriptRepository.currentRules(); | |
} | |
if (toBeRemoved.isEmpty()) { | |
out.println("ERROR No rule scripts to remove"); | |
return; | |
} | |
for (RuleScript ruleScript : toBeRemoved) { | |
if (scriptRepository.removeScript(ruleScript) != ruleScript) { | |
out.println("ERROR remove failed to find script " + ruleScript.getName()); | |
} | |
} | |
// ok, now that we have updated the maps and deleted the scripts | |
// we need to find all classes which were transformed by | |
// the scripts and retransform them | |
// now look for loaded classes whose names are in the list | |
List<Class<?>> transformed = new LinkedList<Class<?>>(); | |
List<String> deletedClassNames = new LinkedList<String>(); | |
for (RuleScript ruleScript : toBeRemoved) { | |
List<Transform> transforms = ruleScript.getTransformed(); | |
if (transforms != null) { | |
for (Transform transform : transforms) { | |
String className = transform.getInternalClassName(); | |
if (!deletedClassNames.contains(className)) { | |
deletedClassNames.add(className); | |
} | |
} | |
} | |
} | |
for (Class clazz : inst.getAllLoadedClasses()) { | |
if (isSkipClass(clazz)) { | |
continue; | |
} | |
if (deletedClassNames.contains(clazz.getName())) { | |
transformed.add(clazz); | |
} | |
} | |
// retransform all classes affected by the change | |
if (!transformed.isEmpty()) { | |
Class<?>[] transformedArray = new Class<?>[transformed.size()]; | |
transformed.toArray(transformedArray); | |
if (Transformer.isVerbose()) { | |
for (int i = 0; i < transformed.size(); i++) { | |
System.out.println("retransforming " + transformedArray[i].getName()); | |
} | |
} | |
try { | |
inst.retransformClasses(transformedArray); | |
} catch(VerifyError ve) { | |
System.out.println("Retransformer : VerifyError during retransformation : some rules may not have been correctly uninjected!"); | |
ve.printStackTrace(); | |
out.println("VerifyError during retransformation : some rules may not have been correctly uninjected!"); | |
ve.printStackTrace(out); | |
} | |
} | |
// now we can safely purge keys for all the deleted scripts -- we need to do this | |
// after the retransform because the latter removes the trigger code which uses | |
// the rule key | |
for (RuleScript ruleScript : toBeRemoved) { | |
ruleScript.purge(); | |
out.println("uninstall RULE " + ruleScript.getName()); | |
} | |
} | |
public void appendJarFile(PrintWriter out, JarFile jarfile, boolean isBoot) throws Exception | |
{ | |
if (isBoot) { | |
inst.appendToBootstrapClassLoaderSearch(jarfile); | |
bootJars.add(jarfile.getName()); | |
out.println("append boot jar " + jarfile.getName()); | |
} else { | |
inst.appendToSystemClassLoaderSearch(jarfile); | |
sysJars.add(jarfile.getName()); | |
out.println("append sys jar " + jarfile.getName()); | |
} | |
} | |
/** | |
* Returns jars that this retransformer was asked to | |
* {@link #appendJarFile(PrintWriter, JarFile, boolean) add} to the boot classloader. | |
* | |
* Note that the returned set will not include those jars that were added to the | |
* instrumentor object at startup via the -javaagent command line argument. | |
* | |
* @return set of jar pathnames for all jars loaded in the boot classloader | |
*/ | |
public Set<String> getLoadedBootJars() { | |
return new HashSet<String>(bootJars); // returns a copy | |
} | |
/** | |
* Returns jars that this retransformer was asked to | |
* {@link #appendJarFile(PrintWriter, JarFile, boolean) add} to the system classloader. | |
* | |
* Note that the returned set will not include those jars that were added to the | |
* instrumentor object at startup via the -javaagent command line argument. | |
* | |
* @return set of jar pathnames for all jars loaded in the system classloader | |
*/ | |
public Set<String> getLoadedSystemJars() { | |
return new HashSet<String>(sysJars); // returns a copy | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.proj.agent; | |
import java.io.BufferedReader; | |
import java.io.File; | |
import java.io.IOException; | |
import java.io.PrintStream; | |
import java.io.PrintWriter; | |
import java.util.Arrays; | |
import java.util.LinkedList; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Properties; | |
import java.util.Set; | |
import java.util.jar.JarFile; | |
import org.jboss.byteman.agent.Transformer; | |
public class ScriptReload extends Thread { | |
private static ScriptReload scriptReload = null; | |
private AgentRetransformer retransformer; | |
private static List<String> scriptTexts; | |
private static List<String> scriptNames; | |
ScriptReload(AgentRetransformer agentRetransformer) { | |
this.retransformer = agentRetransformer; | |
setDaemon(true); | |
} | |
public void run() { | |
scriptTexts = Arrays.asList("RULE jabber\nCLASS java.lang.Thread\nMETHOD start()\nIF true\nDO traceln(\"*** start for thread: \"+ $0.getName())\nENDRULE\n"); | |
scriptNames = Arrays.asList("newRules.btm"); | |
PrintStream stdout = System.out; | |
System.setOut(stdout); | |
PrintWriter printWriter = new PrintWriter(stdout); | |
while (true) { | |
try { | |
Thread.sleep(5000); | |
/* | |
* Get new script however you want | |
*/ | |
System.out.printf("Installing Scripts %s\n", scriptNames.toString()); | |
retransformer.installScript(scriptTexts, scriptNames, printWriter); | |
} catch(InterruptedException e) { | |
e.printStackTrace(); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
} | |
public static synchronized boolean initialize(AgentRetransformer agentRetransformer) { | |
if(scriptReload == null) { | |
scriptReload = new ScriptReload(agentRetransformer); | |
scriptReload.start(); | |
} | |
return true; | |
} | |
private void getVersion(BufferedReader in, PrintWriter out) { | |
String version = this.getClass().getPackage().getImplementationVersion(); | |
if (version == null) { | |
version = "0"; | |
} | |
out.println(version); | |
out.println("OK"); | |
out.flush(); | |
} | |
private void loadScripts(BufferedReader in, PrintWriter out) throws IOException | |
{ | |
handleScripts(in, out, false); | |
} | |
private void loadJars(BufferedReader in, PrintWriter out, boolean isBoot) throws IOException | |
{ | |
final String endMarker = (isBoot) ? "ENDBOOT" : "ENDSYS"; | |
String line = in.readLine().trim(); | |
while (line != null && !line.equals(endMarker)) { | |
try { | |
JarFile jarfile = new JarFile(new File(line)); | |
retransformer.appendJarFile(out, jarfile, isBoot); | |
} catch (Exception e) { | |
out.append("EXCEPTION "); | |
out.append("Unable to add jar file " + line + "\n"); | |
out.append(e.toString()); | |
out.append("\n"); | |
e.printStackTrace(out); | |
} | |
line = in.readLine().trim(); | |
} | |
if (line == null || !line.equals(endMarker)) { | |
out.append("ERROR\n"); | |
out.append("Unexpected end of line reading " + ((isBoot) ? "boot" : "system") + " jars\n"); | |
} | |
out.println("OK"); | |
out.flush(); | |
} | |
private void deleteScripts(BufferedReader in, PrintWriter out) throws IOException | |
{ | |
handleScripts(in, out, true); | |
} | |
private void handleScripts(BufferedReader in, PrintWriter out, boolean doDelete) throws IOException | |
{ | |
List<String> scripts = new LinkedList<String>(); | |
List<String> scriptNames = new LinkedList<String>(); | |
String line = in.readLine().trim(); | |
String scriptName = "<unknown>"; | |
while (line.startsWith("SCRIPT ")) { | |
StringBuffer stringBuffer = new StringBuffer(); | |
scriptName = line.substring("SCRIPT ".length()); | |
line = in.readLine(); | |
while (line != null && !line.equals("ENDSCRIPT")) { | |
stringBuffer.append(line); | |
stringBuffer.append('\n'); | |
line = in.readLine(); | |
} | |
if (line == null || !line.equals("ENDSCRIPT")) { | |
out.append("ERROR\n"); | |
out.append("Unexpected end of line reading script " + scriptName + "\n"); | |
out.append("OK"); | |
out.flush(); | |
return; | |
} | |
String script = stringBuffer.toString(); | |
scripts.add(script); | |
scriptNames.add(scriptName); | |
line = in.readLine(); | |
} | |
if ((doDelete && !line.equals("ENDDELETE")) || | |
(!doDelete && !line.equals("ENDLOAD"))) { | |
out.append("ERROR "); | |
out.append("Unexpected end of line reading script " + scriptName + "\n"); | |
out.println("OK"); | |
out.flush(); | |
return; | |
} | |
try { | |
if (doDelete) { | |
retransformer.removeScripts(scripts, out); | |
} else { | |
retransformer.installScript(scripts, scriptNames, out); | |
} | |
} catch (Exception e) { | |
out.append("EXCEPTION "); | |
out.append(e.toString()); | |
out.append('\n'); | |
e.printStackTrace(out); | |
} | |
out.println("OK"); | |
out.flush(); | |
} | |
private void purgeScripts(BufferedReader in, PrintWriter out) throws Exception | |
{ | |
retransformer.removeScripts(null, out); | |
out.println("OK"); | |
out.flush(); | |
} | |
private void listScripts(BufferedReader in, PrintWriter out) throws Exception | |
{ | |
retransformer.listScripts(out); | |
out.println("OK"); | |
out.flush(); | |
} | |
private void listBootJars(BufferedReader in, PrintWriter out) throws Exception | |
{ | |
Set<String> jars = retransformer.getLoadedBootJars(); | |
for (String jar : jars) { | |
out.println(new File(jar).getAbsolutePath()); | |
} | |
out.println("OK"); | |
out.flush(); | |
} | |
private void listSystemJars(BufferedReader in, PrintWriter out) throws Exception | |
{ | |
Set<String> jars = retransformer.getLoadedSystemJars(); | |
for (String jar : jars) { | |
out.println(new File(jar).getAbsolutePath()); | |
} | |
out.println("OK"); | |
out.flush(); | |
} | |
private void listSystemProperties(BufferedReader in, PrintWriter out) throws Exception | |
{ | |
Properties sysProps = System.getProperties(); | |
boolean strictMode = false; | |
if (Boolean.parseBoolean(sysProps.getProperty(Transformer.SYSPROPS_STRICT_MODE, "true"))) { | |
strictMode = true; | |
} | |
for (Map.Entry<Object, Object> entry : sysProps.entrySet()) { | |
String name = entry.getKey().toString(); | |
if (!strictMode || name.startsWith("org.jboss.byteman.")) { | |
String value = entry.getValue().toString(); | |
out.println(name + "=" + value.replace("\n", "\\n").replace("\r", "\\r")); | |
} | |
} | |
out.println("OK"); | |
out.flush(); | |
} | |
private void setSystemProperties(BufferedReader in, PrintWriter out) throws Exception | |
{ | |
boolean strictMode = false; | |
if (Boolean.parseBoolean(System.getProperty(Transformer.SYSPROPS_STRICT_MODE, "true"))) { | |
strictMode = true; | |
} | |
final String endMarker = "ENDSETSYSPROPS"; | |
String line = in.readLine().trim(); | |
while (line != null && !line.equals(endMarker)) { | |
try { | |
String[] nameValuePair = line.split("=", 2); | |
if (nameValuePair.length != 2 ) { | |
throw new Exception("missing '='"); | |
} | |
String name = nameValuePair[0]; | |
String value = nameValuePair[1]; | |
if (strictMode && !name.startsWith("org.jboss.byteman.")) { | |
throw new Exception("strict mode is enabled, cannot set non-byteman system property"); | |
} | |
if (name.equals(Transformer.SYSPROPS_STRICT_MODE) && !value.equals("true")) { | |
// nice try | |
throw new Exception("cannot turn off strict mode"); | |
} | |
// everything looks good and we are allowed to set the system property now | |
if (value.length() > 0) { | |
// "some.sys.prop=" means the client wants to delete the system property | |
System.setProperty(name, value); | |
out.append("Set system property [" + name + "] to value [" + value + "]\n"); | |
} else { | |
System.clearProperty(name); | |
out.append("Deleted system property [" + name + "]\n"); | |
} | |
// ok, now tell the transformer a property has changed | |
retransformer.updateConfiguration(name); | |
} catch (Exception e) { | |
out.append("EXCEPTION "); | |
out.append("Unable to set system property [" + line + "]\n"); | |
out.append(e.toString()); | |
out.append("\n"); | |
e.printStackTrace(out); | |
} | |
line = in.readLine().trim(); | |
} | |
if (line == null || !line.equals(endMarker)) { | |
out.append("ERROR\n"); | |
out.append("Unexpected end of line reading system properties\n"); | |
} | |
out.println("OK"); | |
out.flush(); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment