[jboss-svn-commits] JBL Code SVN: r29835 - in labs/jbosstm/workspace/adinn/byteman/trunk: src/org/jboss/byteman/agent and 1 other directories.
jboss-svn-commits at lists.jboss.org
jboss-svn-commits at lists.jboss.org
Tue Oct 27 08:57:11 EDT 2009
Author: mazz
Date: 2009-10-27 08:57:10 -0400 (Tue, 27 Oct 2009)
New Revision: 29835
Modified:
labs/jbosstm/workspace/adinn/byteman/trunk/build.xml
labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Main.java
labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Retransformer.java
labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/TransformListener.java
labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/submit/Submit.java
Log:
This fixes BYTEMAN-54, BYTEMAN-55, BYTEMAN-56, BYTEMAN-57. Submit is now able to be embedded in a Java app. It still has main so it can be used as a cmdline utility. The build now produces byteman-submit.jar so it can be used as a cross-platform cmdline utility without the need for launcher scripts
Modified: labs/jbosstm/workspace/adinn/byteman/trunk/build.xml
===================================================================
--- labs/jbosstm/workspace/adinn/byteman/trunk/build.xml 2009-10-27 12:54:13 UTC (rev 29834)
+++ labs/jbosstm/workspace/adinn/byteman/trunk/build.xml 2009-10-27 12:57:10 UTC (rev 29835)
@@ -56,6 +56,7 @@
<property name="ext.javacup.jars" value="javacup.jar"/>
<property name="ext.javacup.rt.jars" value="javacuprt.jar"/>
<property name="byteman.jar" value="byteman.jar"/>
+ <property name="byteman-submit.jar" value="byteman-submit.jar"/>
<property name="byteman.bin.scripts" value="bytemancheck.sh submit.sh bmjava.sh"/>
<property name="byteman.doc.files" value="ProgrammersGuide.pdf"/>
<property name="manifest.file" value="${dd.dir}/META-INF/MANIFEST.MF"/>
@@ -101,16 +102,16 @@
<fileset dir="${ext.lib.dir}" includes="${ext.javacup.jars}"/>
<fileset dir="${ext.lib.dir}" includes="${ext.javacup.rt.jars}"/>
</classpath>
- <arg value="-package"/>
- <arg value="org.jboss.byteman.rule.grammar"/>
+ <arg value="-package"/>
+ <arg value="org.jboss.byteman.rule.grammar"/>
<arg value="-parser" />
<arg value="ECAGrammarParser" />
<arg value="-nonterms"/>
<arg value="${dd.grammar.dir}/cup/ECAGrammar.cup"/>
</java>
- <delete file="${src.dir}/org/jboss/byteman/rule/grammar/ECATokenLexer.java"/>
- <delete file="${src.dir}/org/jboss/byteman/rule/grammar/ECAGrammarParser.java"/>
- <delete file="${src.dir}/org/jboss/byteman/rule/grammar/sym.java"/>
+ <delete file="${src.dir}/org/jboss/byteman/rule/grammar/ECATokenLexer.java"/>
+ <delete file="${src.dir}/org/jboss/byteman/rule/grammar/ECAGrammarParser.java"/>
+ <delete file="${src.dir}/org/jboss/byteman/rule/grammar/sym.java"/>
<move file="${dd.grammar.dir}/flex/ECATokenLexer.java"
tofile="${src.dir}/org/jboss/byteman/rule/grammar/ECATokenLexer.java"/>
<move file="ECAGrammarParser.java"
@@ -129,8 +130,8 @@
</javac>
</target>
- <target name="jar" depends="compile">
- <!--
+ <target name="jar" depends="compile">
+ <!--
<unjar src="${ext.lib.dir}/junit.jar" dest="${build.classes.dir}"/>
-->
<unjar src="${ext.lib.dir}/javacuprt.jar" dest="${build.classes.dir}"/>
@@ -138,6 +139,12 @@
<jar jarfile="${build.lib.dir}/${byteman.jar}" manifest="${manifest.file}">
<fileset dir="${build.classes.dir}" includes="**/*"/>
</jar>
+ <jar jarfile="${build.lib.dir}/${byteman-submit.jar}">
+ <fileset dir="${build.classes.dir}" includes="**/Submit*.class"/>
+ <manifest>
+ <attribute name="Main-Class" value="org.jboss.byteman.agent.submit.Submit"/>
+ </manifest>
+ </jar>
</target>
<!-- installation targets -->
@@ -153,6 +160,9 @@
<copy todir="${install.lib.dir}">
<fileset dir="${build.lib.dir}" includes="${byteman.jar}"/>
</copy>
+ <copy todir="${install.lib.dir}">
+ <fileset dir="${build.lib.dir}" includes="${byteman-submit.jar}"/>
+ </copy>
<copy todir="${install.bin.dir}">
<fileset dir="${bin.src.dir}" includes="${byteman.bin.scripts}"/>
</copy>
@@ -205,9 +215,11 @@
<target name="clean">
<delete dir="${build.dir}"/>
<delete dir="${dd.grammar.dir}" includes="*.java *.tokens"/>
+ <ant dir="sample" target="clean"/>
</target>
<target name="spotless" depends="clean">
<delete dir="${install.dir}"/>
+ <ant dir="sample" target="spotless"/>
</target>
</project>
Modified: labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Main.java
===================================================================
--- labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Main.java 2009-10-27 12:54:13 UTC (rev 29834)
+++ labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Main.java 2009-10-27 12:57:10 UTC (rev 29835)
@@ -55,6 +55,14 @@
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());
+ } else if (arg.startsWith(PORT_PREFIX)) {
+ try {
+ port = Integer.valueOf(arg.substring(PORT_PREFIX.length(), arg.length()));
+ } 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(LISTENER_PREFIX)) {
@@ -137,14 +145,15 @@
if (allowRedefine && isRedefine) {
transformerClazz = loader.loadClass("org.jboss.byteman.agent.Retransformer");
//transformer = new Retransformer(inst, scriptPaths, scripts, true);
+ Constructor constructor = transformerClazz.getConstructor(Instrumentation.class, List.class, List.class, boolean.class, String.class, Integer.class);
+ transformer = (ClassFileTransformer)constructor.newInstance(new Object[] { inst, scriptPaths, scripts, isRedefine, hostname, port});
} 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});
}
- 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 (isRedefine) {
Method method = transformerClazz.getMethod("installBootScripts");
@@ -154,6 +163,16 @@
}
/**
+ * 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:";
@@ -200,4 +219,14 @@
* 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;
}
\ No newline at end of file
Modified: labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Retransformer.java
===================================================================
--- labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Retransformer.java 2009-10-27 12:54:13 UTC (rev 29834)
+++ labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Retransformer.java 2009-10-27 12:57:10 UTC (rev 29835)
@@ -39,11 +39,11 @@
*
* @param inst the instrumentation object used to interface to the JVM
*/
- public Retransformer(Instrumentation inst, List<String> scriptPaths, List<String> scriptTexts, boolean isRedefine)
+ public Retransformer(Instrumentation inst, List<String> scriptPaths, List<String> scriptTexts, boolean isRedefine, String hostname, Integer port)
throws Exception
{
super(inst, scriptPaths, scriptTexts, isRedefine);
- addTransformListener();
+ addTransformListener(hostname, port);
}
protected void installScript(List<String> scriptTexts, List<String> scriptNames, PrintWriter out) throws Exception
@@ -190,9 +190,9 @@
}
}
- private void addTransformListener()
+ private void addTransformListener(String hostname, Integer port)
{
- TransformListener.initialize(this);
+ TransformListener.initialize(this, hostname, port);
}
public void removeScripts(List<String> scriptTexts, PrintWriter out) throws Exception
Modified: labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/TransformListener.java
===================================================================
--- labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/TransformListener.java 2009-10-27 12:54:13 UTC (rev 29834)
+++ labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/TransformListener.java 2009-10-27 12:57:10 UTC (rev 29835)
@@ -17,6 +17,7 @@
public class TransformListener extends Thread
{
public static int DEFAULT_PORT = 9091;
+ public static String DEFAULT_HOST = "localhost";
private static TransformListener theTransformListener = null;
private static ServerSocket theServerSocket;
private Retransformer retransformer;
@@ -27,14 +28,20 @@
setDaemon(true);
}
- public static synchronized boolean initialize(Retransformer retransformer)
+ public static synchronized boolean initialize(Retransformer retransformer, String hostname, Integer port)
{
if (theTransformListener == null) {
try {
+ if (hostname == null) {
+ hostname = DEFAULT_HOST;
+ }
+ if (port == null) {
+ port = Integer.valueOf(DEFAULT_PORT);
+ }
theServerSocket = new ServerSocket();
- theServerSocket.bind(new InetSocketAddress("localhost", DEFAULT_PORT));
+ theServerSocket.bind(new InetSocketAddress(hostname, port.intValue()));
if (Transformer.isVerbose()) {
- System.out.println("TransformListener() : accepting requests on port " + DEFAULT_PORT);
+ System.out.println("TransformListener() : accepting requests on " + hostname + ":" + port);
}
} catch (IOException e) {
System.out.println("TransformListener() : unexpected exception opening server socket " + e);
@@ -198,8 +205,9 @@
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("ENDBOOT")) {
+ while (line != null && !line.equals(endMarker)) {
try {
JarFile jarfile = new JarFile(new File(line));
retransformer.appendJarFile(out, jarfile, isBoot);
@@ -212,9 +220,9 @@
}
line = in.readLine().trim();
}
- if (line == null || !line.equals("ENDBOOT")) {
+ if (line == null || !line.equals(endMarker)) {
out.append("ERROR\n");
- out.append("Unexpected end of line reading boot jars\n");
+ out.append("Unexpected end of line reading " + ((isBoot) ? "boot" : "system") + " jars\n");
}
out.println("OK");
out.flush();
Modified: labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/submit/Submit.java
===================================================================
--- labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/submit/Submit.java 2009-10-27 12:54:13 UTC (rev 29834)
+++ labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/submit/Submit.java 2009-10-27 12:57:10 UTC (rev 29835)
@@ -1,39 +1,431 @@
package org.jboss.byteman.agent.submit;
-import org.jboss.byteman.agent.TransformListener;
-
-import java.io.*;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
- * Provide a main routine for an app which communicates with the byteman agent at runtime allowing loading,
+ * A Java API that can be used to submit requests to a remote Byteman agent.
+ * This also includes a main routine for use as a command-line utility.
+ * This object provides a means by which you communicate with the Byteman agent at runtime allowing loading,
* reloading, unloading of rules and listing of the current rule set and any successful or failed attempts
* to inject, parse and typecheck the rules.
+ *
+ * Note that this class is completely standalone and has no dependencies on any other Byteman class.
+ * It can be shipped alone in a client jar to be used as a very small app.
*/
public class Submit
{
+ public static final String DEFAULT_ADDRESS = "localhost";
+ public static final int DEFAULT_PORT= 9091;
+
+ private final int port;
+ private final String address;
+
/**
- * main routine which submits a script to the byteman agent
- * @param args command line arguments specifying the script file(s) to be submitted and, optionally,
- * the byteman agent listener port to use.
- * Submit [-p port] [-l|-u] [scriptfile . . .]
- * Submit [-p port] [-b|-s] jarfile . . .
- * -p port specifies theport to use
- * -l implies load/reload all rules found in supplied scripts
- * or list all current rules if no scriptfile
- * -u implies unload all rules found in supplied scripts
- * or unload all rules if no scriptfile
- * -b jarfile implies install jar files into boot class path
- * -s jarfile implies install jar files into system class path
+ * Create a client that will connect to a Byteman agent on the default host
+ * and port.
*/
+ public Submit() {
+ this(DEFAULT_ADDRESS, DEFAULT_PORT);
+ }
+
+ /**
+ * Create a client that will connect to a Byteman agent on the given host
+ * and port.
+ *
+ * @param address
+ * the hostname or IP address of the machine where Byteman agent
+ * is located. If <code>null</code>, the default host is used.
+ * @param port
+ * the port that the Byteman agent is listening to.
+ * If 0 or less, the default port is used.
+ */
+ public Submit(String address, int port) {
+ if (address == null) {
+ address = DEFAULT_ADDRESS;
+ }
+
+ if (port <= 0) {
+ port = DEFAULT_PORT;
+ }
+
+ this.address = address;
+ this.port = port;
+ }
+
+ /**
+ * @return identifies the host where this client expects a Byteman agent to
+ * be running.
+ */
+ public String getAddress() {
+ return this.address;
+ }
+
+ /**
+ * @return the port that this client expects a Byteman agent to be listening
+ * to on the given {@link #getAddress() host}.
+ */
+ public int getPort() {
+ return this.port;
+ }
+
+ /**
+ * Tells the Byteman agent to delete all rules. This will effectively revert
+ * the Byteman's VM to its original state; that is, no Byteman injected
+ * byte-code will be invoked.
+ *
+ * @return the results of the delete-all request to the Byteman agent
+ *
+ * @throws Exception
+ * if the request failed
+ */
+ public String deleteAllRules() throws Exception {
+ Comm comm = new Comm(address, port);
+ comm.println("DELETEALL");
+ String results = comm.readResponse();
+ comm.close();
+ return results;
+ }
+
+ /**
+ * Tells the Byteman agent to list all deployed rules.
+ *
+ * @return all the rules deployed in the Byteman agent
+ *
+ * @throws Exception
+ * if the request failed
+ */
+ public String listAllRules() throws Exception {
+ Comm comm = new Comm(address, port);
+ comm.println("LIST");
+ String results = comm.readResponse();
+ comm.close();
+ return results;
+ }
+
+ /**
+ * This adds the given list of files to the Byteman agent's <em>boot</em>
+ * classloader. Note that if the Byteman agent is running on a remote
+ * machine, the paths must resolve on that remote host (i.e. the file must
+ * exist on the remote machine at the paths given to this method).
+ *
+ * @param jarPaths
+ * the paths to the library .jar files that will be loaded
+ *
+ * @return the result of the load as reported by Byteman
+ *
+ * @throws Exception
+ * if the request failed
+ */
+ public String addJarToBootClassloader(List<String> jarPaths) throws Exception {
+ if (jarPaths == null || jarPaths.size() == 0) {
+ return "";
+ }
+
+ StringBuilder str = new StringBuilder("BOOT\n");
+ for (String jarPath : jarPaths) {
+ str.append(jarPath).append("\n");
+ }
+ str.append("ENDBOOT\n");
+
+ Comm comm = new Comm(this.address, this.port);
+ comm.print(str.toString());
+ String results = comm.readResponse();
+ comm.close();
+ return results;
+ }
+
+ /**
+ * This adds the given list of files to the Byteman agent's <em>system</em>
+ * classloader. Note that if the Byteman agent is running on a remote
+ * machine, the paths must resolve on that remote host (i.e. the file must
+ * exist on the remote machine at the paths given to this method).
+ *
+ * @param jarPaths
+ * the paths to the library .jar files that will be loaded
+ *
+ * @return the result of the load as reported by Byteman
+ *
+ * @throws Exception
+ * if the request failed
+ */
+ public String addJarToSystemClassloader(List<String> jarPaths) throws Exception {
+ if (jarPaths == null || jarPaths.size() == 0) {
+ return "";
+ }
+
+ StringBuilder str = new StringBuilder("SYS\n");
+ for (String jarPath : jarPaths) {
+ str.append(jarPath).append("\n");
+ }
+ str.append("ENDSYS\n");
+
+ Comm comm = new Comm(this.address, this.port);
+ comm.print(str.toString());
+ String results = comm.readResponse();
+ comm.close();
+ return results;
+ }
+
+ /**
+ * Deploys rules into Byteman, where the rule definitions are found in the
+ * local files found at the given paths. The rule definitions found in the
+ * files are actually passed down directly to Byteman, not the file paths
+ * themselves. Therefore, these files must exist on the machine where this
+ * client is running (i.e. the files are not loaded directly by the Byteman
+ * agent).
+ *
+ * @param filePaths
+ * the local files containing the rule definitions to be deployed
+ * to Byteman
+ *
+ * @return the results of the deployment
+ *
+ * @throws Exception
+ * if the request failed
+ */
+ public String addRulesFromFiles(List<String> filePaths) throws Exception {
+ Map<String, String> rules = getRulesFromRuleFiles(filePaths);
+ return addRules(rules);
+ }
+
+ /**
+ * Deploys rules into Byteman, where the rule definitions are found in the
+ * given map's value set. The names of the rule definitions are found as
+ * keys in the given map.
+ *
+ * @param rules
+ * rules to be deployed to Byteman (key=rule name, value=rule
+ * definition)
+ *
+ * @return the results of the deployment
+ *
+ * @throws Exception
+ * if the request failed
+ */
+ public String addRules(Map<String, String> rules) throws Exception {
+ if (rules == null || rules.size() == 0) {
+ return "";
+ }
+
+ StringBuilder str = new StringBuilder("LOAD\n");
+ for (Map.Entry<String, String> entry : rules.entrySet()) {
+ str.append("SCRIPT " + entry.getKey() + '\n');
+ str.append(entry.getValue()).append('\n');
+ str.append("ENDSCRIPT\n");
+ }
+ str.append("ENDLOAD\n");
+
+ Comm comm = new Comm(this.address, this.port);
+ comm.print(str.toString());
+ String results = comm.readResponse();
+ comm.close();
+ return results;
+
+ }
+
+ /**
+ * Deletes rules from Byteman, where the rule definitions are found in the
+ * local files found at the given paths. After this method is done, the
+ * given rules will no longer be processed by Byteman. The rule definitions
+ * found in the files are actually passed down directly to Byteman, not the
+ * file paths themselves. Therefore, these files must exist on the machine
+ * where this client is running (i.e. the files are not read directly by the
+ * Byteman agent).
+ *
+ * @param filePaths
+ * the local files containing the rule definitions to be deleted
+ * from Byteman
+ *
+ * @return the results of the deletion
+ *
+ * @throws Exception
+ * if the request failed
+ */
+ public String deleteRulesFromFiles(List<String> filePaths) throws Exception {
+ Map<String, String> rules = getRulesFromRuleFiles(filePaths);
+ return deleteRules(rules);
+ }
+
+ /**
+ * Deletes rules from Byteman, where the rule definitions are found in the
+ * given map's value set. The names of the rule definitions are found as
+ * keys in the given map. After this method is done, the given rules will no
+ * longer be processed by Byteman.
+ *
+ * @param rules
+ * rules to be deleted from Byteman (key=rule name, value=rule
+ * definition)
+ *
+ * @return the results of the deletion
+ *
+ * @throws Exception
+ * if the request failed
+ */
+ public String deleteRules(Map<String, String> rules) throws Exception {
+ if (rules == null || rules.size() == 0) {
+ return "";
+ }
+
+ StringBuilder str = new StringBuilder("DELETE\n");
+ for (Map.Entry<String, String> entry : rules.entrySet()) {
+ str.append("SCRIPT " + entry.getKey() + '\n');
+ str.append(entry.getValue()).append('\n');
+ str.append("ENDSCRIPT\n");
+ }
+ str.append("ENDDELETE\n");
+
+ Comm comm = new Comm(this.address, this.port);
+ comm.print(str.toString());
+ String results = comm.readResponse();
+ comm.close();
+ return results;
+
+ }
+
+ private Map<String, String> getRulesFromRuleFiles(List<String> filePaths) throws Exception {
+ if (filePaths == null || filePaths.size() == 0) {
+ return new HashMap<String, String>(0);
+ }
+
+ final char[] readBuffer = new char[4096];
+ Map<String, String> rules = new HashMap<String, String>(filePaths.size());
+
+ for (String filePath : filePaths) {
+ // abort if a rule file was invalid - we never submit the request if at least one was invalid
+ if (!confirmRuleFileValidity(filePath)) {
+ throw new Exception("Invalid rule file: " + filePath);
+ }
+
+ // read in the current rule file
+ StringBuilder ruleText = new StringBuilder();
+ try {
+ FileInputStream fis = new FileInputStream(filePath);
+ InputStreamReader reader = new InputStreamReader(fis);
+ int read = reader.read(readBuffer);
+ while (read > 0) {
+ ruleText.append(readBuffer, 0, read);
+ read = reader.read(readBuffer);
+ }
+ reader.close();
+
+ // put the current rule definition in our list of rules to add
+ rules.put(filePath, ruleText.toString());
+ } catch (IOException e) {
+ throw new Exception("Error reading from rule file: " + filePath, e);
+ }
+ }
+
+ return rules;
+ }
+
+ private boolean confirmRuleFileValidity(String path) {
+ // right now, we only check if its a readable file, do we want to see if
+ // its parsable, too?
+ File file = new File(path);
+ if (!file.isFile() || !file.canRead()) {
+ return false;
+ }
+ return true;
+ }
+
+ private class Comm {
+ private Socket commSocket;
+ private BufferedReader commInput;
+ private PrintWriter commOutput;
+
+ public Comm(String address, int port) throws Exception {
+ this.commSocket = new Socket(address, port);
+
+ InputStream is;
+ try {
+ is = this.commSocket.getInputStream();
+ } catch (Exception e) {
+ // oops. cannot handle this
+ try {
+ this.commSocket.close();
+ } catch (Exception e1) {
+ }
+ throw e;
+ }
+
+ OutputStream os;
+ try {
+ os = this.commSocket.getOutputStream();
+ } catch (Exception e) {
+ // oops. cannot handle this
+ try {
+ this.commSocket.close();
+ } catch (Exception e1) {
+ }
+ throw e;
+ }
+
+ this.commInput = new BufferedReader(new InputStreamReader(is));
+ this.commOutput = new PrintWriter(new OutputStreamWriter(os));
+
+ return;
+ }
+
+ public void close() {
+ try {
+ this.commSocket.close(); // also closes the in/out streams
+ } catch (Exception e) {
+ // TODO what should I do here? no need to abort, we are closing this object anyway
+ } finally {
+ // this object cannot be reused anymore, therefore, null everything out
+ // which will force NPEs if attempts to reuse this object occur later
+ this.commSocket = null;
+ this.commInput = null;
+ this.commOutput = null;
+ }
+ }
+
+ public void println(String line) {
+ this.commOutput.println(line);
+ this.commOutput.flush();
+ }
+
+ public void print(String line) {
+ this.commOutput.print(line);
+ this.commOutput.flush();
+ }
+
+ public String readResponse() throws Exception {
+ StringBuilder str = new StringBuilder();
+
+ String line = this.commInput.readLine();
+ while (line != null && !line.trim().equals("OK")) {
+ str.append(line.trim()).append('\n');
+ line = this.commInput.readLine();
+ }
+
+ return str.toString();
+ }
+ }
+
+ /**
+ * A main routine which submits requests to the Byteman agent utilizing the Java API.
+ * @param args see {@link #usage(int)} for a description of the allowed arguments
+ */
public static void main(String[] args)
{
- int port = TransformListener.DEFAULT_PORT;
+ int port = DEFAULT_PORT;
+ String hostname = DEFAULT_ADDRESS;
int startIdx = 0;
int maxIdx = args.length;
- boolean loadOrList = false;
boolean deleteRules = false;
boolean addBoot = false;
boolean addSys = false;
@@ -42,29 +434,31 @@
while (startIdx < maxIdx && args[startIdx].startsWith("-")) {
if (maxIdx >= startIdx + 2 && args[startIdx].equals("-p")) {
try {
- port = Integer.valueOf(args[1]);
+ port = Integer.valueOf(args[startIdx+1]);
} catch (NumberFormatException e) {
- System.out.println("Submit : invalid port " + args[1]);
+ System.out.println("Submit : invalid port " + args[startIdx+1]);
System.exit(1);
}
if (port <= 0) {
- System.out.println("Submit : invalid port " + args[1]);
+ System.out.println("Submit : invalid port " + args[startIdx+1]);
System.exit(1);
}
startIdx += 2;
+ } else if (maxIdx >= startIdx + 2 && args[startIdx].equals("-h")) {
+ hostname = args[startIdx+1];
+ startIdx += 2;
} else if (args[startIdx].equals("-u")) {
deleteRules = true;
startIdx++;
optionCount++;
} else if (args[startIdx].equals("-l")) {
- loadOrList = true;
startIdx++;
optionCount++;
} else if (args[startIdx].equals("-b")) {
addBoot = true;
startIdx ++;
optionCount++;
- } else if (args[startIdx].equals("-b")) {
+ } else if (args[startIdx].equals("-s")) {
addSys = true;
startIdx ++;
optionCount++;
@@ -77,186 +471,65 @@
usage(1);
}
- if (optionCount == 0) {
- loadOrList = true;
- }
+ // must have some file args if adding to sys or boot classpath
- // must have some file args if adding to sys or boot classpath
-
if (startIdx == maxIdx && (addBoot || addSys)) {
usage(1);
}
-
- for (int i = startIdx; i < maxIdx; i++) {
- File file = new File(args[i]);
- if (!file.isFile() || !file.canRead()) {
- System.out.println("Submit : invalid file " + args[i]);
- System.exit(1);
- }
- }
- // ok try to open the socket
- Socket socket = null;
+ Submit client = new Submit(hostname, port);
+ String results = null;
+ List<String> argsList = null;
try {
- socket = new Socket("localhost", port);
- } catch (IOException e) {
- System.out.println("Submit : error opening socket " + e);
- e.printStackTrace();
- System.exit(1);
- }
-
- InputStream is = null;
- try {
- is = socket.getInputStream();
- } catch (IOException e) {
- // oops. cannot handle this
- System.out.println("Submit : error opening socket input stream " + e);
- e.printStackTrace();
- try {
- socket.close();
- } catch (IOException e1) {
- System.out.println("Submit : exception closing socket after failed input stream open" + e1);
- e1.printStackTrace();
- }
- System.exit(1);
- }
-
- OutputStream os = null;
- try {
- os = socket.getOutputStream();
- } catch (IOException e) {
- // oops. cannot handle this
- System.out.println("Submit : error opening socket output stream " + e);
- e.printStackTrace();
- try {
- socket.close();
- } catch (IOException e1) {
- System.out.println("Submit : exception closing socket after failed output stream open" + e1);
- e1.printStackTrace();
- }
- System.exit(1);
- }
-
- PrintWriter out = null;
-
- BufferedReader in = new BufferedReader(new InputStreamReader(is));
- out = new PrintWriter(new OutputStreamWriter(os));
- final int READ_BUFFER_LENGTH = 1024;
- char[] readBuffer = new char[READ_BUFFER_LENGTH];
-
- if (startIdx == maxIdx) {
- // no args means list or delete all current scripts
- if (deleteRules) {
- out.println("DELETEALL");
- } else {
- out.println("LIST");
- }
- out.flush();
- try {
- String line = in.readLine().trim();
- while (line != null && !line.equals("OK"))
- {
- System.out.println(line);
- line = in.readLine();
- }
- socket.close();
- } catch (IOException e) {
- System.out.println("Submit : error reading from socket " + e);
- e.printStackTrace();
- try {
- socket.close();
- } catch (IOException e1) {
- System.out.println("Submit : exception closing socket after failed read " + e1);
- e1.printStackTrace();
- }
- System.exit(1);
- }
- } else {
- StringBuffer stringBuffer = new StringBuffer();
- if (addBoot) {
- stringBuffer.append("BOOT\n");
- for (int i = startIdx; i < maxIdx; i++) {
- String name = args[i];
- stringBuffer.append(name);
- stringBuffer.append("\n");
- }
- stringBuffer.append("ENDBOOT\n");
- } else if (addSys) {
- stringBuffer.append("BOOT\n");
- for (int i = startIdx; i < maxIdx; i++) {
- String name = args[i];
- stringBuffer.append(name);
- stringBuffer.append("\n");
- }
- stringBuffer.append("ENDBOOT\n");
- } else {
+ if (startIdx == maxIdx) {
+ // no args means list or delete all current scripts
if (deleteRules) {
- stringBuffer.append("DELETE\n");
+ results = client.deleteAllRules();
} else {
- stringBuffer.append("LOAD\n");
+ // the default behavior (or if -l was explicitly specified) is to do this
+ results = client.listAllRules();
}
+ } else {
+ argsList = new ArrayList<String>();
for (int i = startIdx; i < maxIdx; i++) {
- String name = args[i];
- stringBuffer.append("SCRIPT " + name + "\n");
- try {
- FileInputStream fis = new FileInputStream(args[i]);
- InputStreamReader reader = new InputStreamReader(fis);
- int read = reader.read(readBuffer);
- while (read > 0) {
- stringBuffer.append(readBuffer, 0, read);
- read = reader.read(readBuffer);
- }
- reader.close();
- stringBuffer.append("ENDSCRIPT\n");
- } catch (IOException e) {
- System.out.println("Submit : error reading from file " + args[i] + " " + e);
- e.printStackTrace();
- try {
- socket.close();
- } catch (IOException e1) {
- System.out.println("Submit : exception closing socket after failed file read " + e1);
- e1.printStackTrace();
- }
-
- System.exit(1);
- }
+ argsList.add(args[i]);
}
- if (deleteRules) {
- stringBuffer.append("ENDDELETE\n");
+ if (addBoot) {
+ results = client.addJarToBootClassloader(argsList);
+ } else if (addSys) {
+ results = client.addJarToSystemClassloader(argsList);
} else {
- stringBuffer.append("ENDLOAD\n");
+ if (deleteRules) {
+ results = client.deleteRulesFromFiles(argsList);
+ } else {
+ // the default behavior (or if -l was explicitly specified) is to do this
+ results = client.addRulesFromFiles(argsList);
+ }
}
}
- out.append(stringBuffer);
- out.flush();
-
- try {
- String line = in.readLine().trim();
- while (line != null & !line.equals("OK")) {
- System.out.println(line);
- line = in.readLine();
- }
- } catch (IOException e) {
- System.out.println("Submit : error reading listener reply "+ e);
- e.printStackTrace();
- try {
- socket.close();
- } catch (IOException e1) {
- System.out.println("Submit : exception closing socket after failed listener read " + e1);
- e1.printStackTrace();
- }
-
- System.exit(1);
+ } catch (Exception e) {
+ System.out.println("Failed to process request: " + e);
+ if (argsList != null) {
+ System.out.println("-- Args were: " + argsList);
}
+ if (results != null) {
+ // rarely will results be non-null on error, but just in case, print it if we got it
+ System.out.println("-- Results were: " + results);
+ }
+ e.printStackTrace();
+ System.exit(1);
}
+
+ System.out.println(results);
}
- public static void usage(int exitCode)
+ private static void usage(int exitCode)
{
- System.out.println("usage : Submit [-p port] [-l|-u] [scriptfile . . .]");
- System.out.println(" Submit [-p port] [-b|-s] jarfile . . .");
+ System.out.println("usage : Submit [-p port] [-h hostname] [-l|-u] [scriptfile . . .]");
+ System.out.println(" Submit [-p port] [-h hostname] [-b|-s] jarfile . . .");
System.out.println(" -p specifies listener port");
+ System.out.println(" -h specifies listener host");
System.out.println(" -l (default) with scriptfile(s) means load/reload all rules in scriptfile(s)");
System.out.println(" with no scriptfile means list all currently loaded rules");
System.out.println(" -u with scriptfile(s) means unload all rules in scriptfile(s)");
More information about the jboss-svn-commits
mailing list