[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