[jboss-svn-commits] JBL Code SVN: r29187 - in labs/jbosstm/workspace/adinn/byteman/trunk: bin and 3 other directories.

jboss-svn-commits at lists.jboss.org jboss-svn-commits at lists.jboss.org
Fri Sep 4 12:00:06 EDT 2009


Author: adinn
Date: 2009-09-04 12:00:06 -0400 (Fri, 04 Sep 2009)
New Revision: 29187

Added:
   labs/jbosstm/workspace/adinn/byteman/trunk/bin/submit.sh
   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/
   labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/submit/Submit.java
Modified:
   labs/jbosstm/workspace/adinn/byteman/trunk/build.xml
   labs/jbosstm/workspace/adinn/byteman/trunk/docs/ProgrammersGuide.odt
   labs/jbosstm/workspace/adinn/byteman/trunk/docs/ProgrammersGuide.pdf
   labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Main.java
   labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/RuleScript.java
   labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Transformer.java
Log:
initial draft of dynamic rule upload capability which attempts to fix BYTEMAN-26 and also incorporates a fix for BYTEMAN-25

Added: labs/jbosstm/workspace/adinn/byteman/trunk/bin/submit.sh
===================================================================
--- labs/jbosstm/workspace/adinn/byteman/trunk/bin/submit.sh	                        (rev 0)
+++ labs/jbosstm/workspace/adinn/byteman/trunk/bin/submit.sh	2009-09-04 16:00:06 UTC (rev 29187)
@@ -0,0 +1,62 @@
+#!/bin/bash
+#
+# JBoss, Home of Professional Open Source
+# Copyright 2009, Red Hat Middleware LLC, 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
+#
+# shell script which submits a request to the Byteman agent listener
+#
+# usage: submit [script1 . . . scriptN]
+#
+# use the root of the path to this file to locate the byteman jar
+BASE=${0%*bin/submit.sh}
+# the binary release puts byteman jar in lib while source puts it in
+# build/lib so add both paths to the classpath just in case
+CP=${BASE}lib/byteman.jar
+CP=${BASE}build/lib/byteman.jar
+# hmm. the asm code should be bundled in the byteman jar?
+CP=${CP}:${BASE}ext/asm-all-3.0.jar
+
+SCRIPT_OPTS=""
+
+if [ $# -gt 0 -a ${1#-*} != ${1} ]; then
+   echo "${1#-*} ${1}"
+   echo "usage: submit [script1 . . . scriptN]"
+   exit
+fi
+
+error=0
+while [ $# -ne 0 ]
+do
+  if [ ! -f $1 -o ! -r $1 ] ; then
+    echo "$1 is not a readable file";
+    error=1
+  fi
+  FILES="${FILES} $1";
+  shift
+done
+
+if [ $error -ne 0 ] ; then
+  exit
+fi
+
+# allow for extra java opts via setting BYTEMAN_JAVA_OPTS
+
+java ${BYTEMAN_JAVA_OPTS} -classpath ${CP} org.jboss.byteman.agent.submit.Submit $FILES


Property changes on: labs/jbosstm/workspace/adinn/byteman/trunk/bin/submit.sh
___________________________________________________________________
Name: svn:executable
   + *

Modified: labs/jbosstm/workspace/adinn/byteman/trunk/build.xml
===================================================================
--- labs/jbosstm/workspace/adinn/byteman/trunk/build.xml	2009-09-04 14:54:01 UTC (rev 29186)
+++ labs/jbosstm/workspace/adinn/byteman/trunk/build.xml	2009-09-04 16:00:06 UTC (rev 29187)
@@ -27,7 +27,9 @@
     <property name="ext.asm.jars" value="asm-all-3.0.jar"/>
 
     <property name="src.dir"           value="src"/>
+    <property name="src.dir.jdk6"           value="srcjdk6"/>
     <property name="dd.dir"            value="dd"/>
+    <property name="dd.dir.jdk6"            value="ddjdk6"/>
     <property name="dd.grammar.dir"    value="${dd.dir}/grammar"/>
     <property name="ext.lib.dir" value="ext"/>
     <property name="build.dir"         value="build"/>
@@ -111,7 +113,7 @@
      <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}"/>
          <unjar src="${ext.lib.dir}/asm-all-3.0.jar" dest="${build.classes.dir}"/>
          <jar jarfile="${build.lib.dir}/byteman.jar" manifest="${dd.dir}/META-INF/MANIFEST.MF">
@@ -124,7 +126,7 @@
             <fileset dir="${build.dir}" includes="lib/byteman.jar"/>
             <fileset dir="." includes="README"/>
             <fileset dir="." includes="docs/ProgrammersGuide.pdf"/>
-            <fileset dir="." includes="bin/bytemancheck.sh"/>
+            <fileset dir="." includes="bin/bytemancheck.sh" "bin/submit.sh"/>
             <fileset dir="." includes="ext/asm-all-3.0.jar"/>
             <fileset dir="." includes="ext/third_party_licenses.txt"/>
         </zip>
@@ -156,6 +158,7 @@
                <fileset dir="${ext.lib.dir}" includes="${ext.javacup.rt.jars}"/>
            </classpath>
            <fileset dir="${src.dir}" includes="**/*.java"/>
+           <fileset dir="${src.dir.jdk6}" includes="**/*.java"/>
        </javadoc>
    </target>
 

Modified: labs/jbosstm/workspace/adinn/byteman/trunk/docs/ProgrammersGuide.odt
===================================================================
(Binary files differ)

Modified: labs/jbosstm/workspace/adinn/byteman/trunk/docs/ProgrammersGuide.pdf
===================================================================
(Binary files differ)

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-09-04 14:54:01 UTC (rev 29186)
+++ labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Main.java	2009-09-04 16:00:06 UTC (rev 29187)
@@ -39,6 +39,8 @@
     public static void premain(String args, Instrumentation inst)
             throws Exception
     {
+        boolean allowRedefine = false;
+
         if (args != null) {
             // args are supplied eparated by ',' characters
             String[] argsArray = args.split(",");
@@ -48,10 +50,13 @@
                     bootJarPaths.add(arg.substring(BOOT_PREFIX.length(), arg.length()));
                 } else if (arg.startsWith(SCRIPT_PREFIX)) {
                     scriptPaths.add(arg.substring(SCRIPT_PREFIX.length(), arg.length()));
+                } else if (arg.startsWith(REDEFINE_PREFIX)) {
+                    String value = arg.substring(REDEFINE_PREFIX.length(), arg.length());
+                    allowRedefine = 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> or script:<path-to-scriptr>");
+                            "  valid arguments are boot:<path-to-jar>, script:<path-to-script> or redefine:<true-or-false>");
                 }
             }
         }
@@ -62,7 +67,7 @@
         for (String bootJarPath : bootJarPaths) {
             try {
                 JarFile jarfile = new JarFile(new File(bootJarPath));
-                // inst.appendToBootstrapClassLoaderSearch(jarfile);
+                inst.appendToBootstrapClassLoaderSearch(jarfile);
             } catch (IOException ioe) {
                 System.err.println("org.jboss.byteman.agent.Main: unable to open boot jar file : " + bootJarPath);
                 throw ioe;
@@ -90,7 +95,18 @@
 
         // install an instance of Transformer to instrument the bytecode
 
-        inst.addTransformer(new Transformer(inst, scriptPaths, scripts));
+        boolean isRedefine = inst.isRedefineClassesSupported();
+
+        if (allowRedefine && isRedefine) {
+            System.out.println("Adding retransformer");
+            Retransformer retransformer = new Retransformer(inst, scriptPaths, scripts);
+            inst.addTransformer(retransformer, true);
+            retransformer.installBootScripts();
+        } else {
+            System.out.println("Adding transformer");
+            inst.addTransformer(new Transformer(inst, scriptPaths, scripts, isRedefine), isRedefine);
+        }
+
     }
 
     /**
@@ -105,6 +121,12 @@
     private static final String SCRIPT_PREFIX = "script:";
 
     /**
+     * prefix used to specify transformer type argument for agent
+     */
+
+    private static final String REDEFINE_PREFIX = "redefine:";
+
+    /**
      * list of paths to extra bootstrap jars supplied on command line
      */
     private static List<String> bootJarPaths = new ArrayList<String>();
@@ -118,4 +140,4 @@
      * list of scripts read from script files
      */
     private static List<String> scripts = new ArrayList<String>();
-}
+}
\ No newline at end of file

Copied: labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Retransformer.java (from rev 27152, labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Transformer.java)
===================================================================
--- labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Retransformer.java	                        (rev 0)
+++ labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Retransformer.java	2009-09-04 16:00:06 UTC (rev 29187)
@@ -0,0 +1,243 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2008-9, Red Hat Middleware LLC, 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 org.jboss.byteman.agent;
+
+import java.lang.instrument.Instrumentation;
+import java.util.*;
+import java.io.PrintWriter;
+
+/**
+ * byte code transformer used to introduce byteman events into JBoss code
+ */
+public class Retransformer extends Transformer {
+
+    /**
+     * 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 Retransformer(Instrumentation inst, List<String> scriptPaths, List<String> scriptTexts)
+            throws Exception
+    {
+        super(inst, scriptPaths, scriptTexts, true);
+        addTransformListener();
+    }
+
+    protected void installScript(List<String> scriptTexts, List<String> scriptNames) 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 = 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;
+
+            synchronized (nameToScriptMap) {
+                previous = nameToScriptMap.get(name);
+                if (previous != null) {
+                    System.out.println("redefining rule " + name);
+                    toBeRemoved.add(previous);
+                    previous.setDeleted();
+                }
+                nameToScriptMap.put(name, ruleScript);
+            }
+
+            // remove any old scripts and install the new ones to ensure that
+            // automatic loads do the right thing
+
+            synchronized(targetToScriptMap) {
+                List<RuleScript> list = targetToScriptMap.get(className);
+                if (list != null) {
+                    if (previous != null) {
+                        list.remove(previous);
+                    }
+                } else {
+                    list = new ArrayList<RuleScript>();
+                    targetToScriptMap.put(className, list);
+                }
+                list.add(ruleScript);
+                if (baseName != null) {
+                    list = targetToScriptMap.get(baseName);
+                    if (list != null) {
+                        if (previous != null) {
+                            list.remove(previous);
+                        }
+                    } else {
+                        list = new ArrayList<RuleScript>();
+                        targetToScriptMap.put(baseName, list);
+                    }
+                }
+            }
+        }
+
+
+        // ok, now that we have updated the maps we need to find all classes which match the scripts and
+        // retransform them
+
+        List<Class<?>> transformed = new LinkedList<Class<?>>();
+
+        for (Class clazz : inst.getAllLoadedClasses()) {
+            String name = clazz.getName();
+            int lastDot = name.lastIndexOf('.');
+
+            if (isBytemanClass(name) || !isTransformable(name)) {
+                continue;
+            }
+
+            if (targetToScriptMap.containsKey(name)) {
+                transformed.add(clazz);
+            } else if (lastDot >= 0 && targetToScriptMap.containsKey(name.substring(lastDot+1))) {
+                transformed.add(clazz);
+            }
+        }
+
+        // retransform all classes whose rules have changed
+
+        if (!transformed.isEmpty()) {
+            Class<?>[] transformedArray = new Class<?>[transformed.size()];
+            inst.retransformClasses(transformed.toArray(transformedArray));
+        }
+    }
+
+
+    protected void listScripts(PrintWriter out)  throws Exception
+    {
+        synchronized (nameToScriptMap) {
+            Iterator<RuleScript> iterator = nameToScriptMap.values().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);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private void addTransformListener()
+    {
+        TransformListener.initialize(this);
+    }
+
+    /**
+     * ensure that scripts which apply to classes loaded before registering the transformer get
+     * are installed by retransforming the relevant classes
+     */
+
+    public void installBootScripts() throws Exception
+    {
+        // check for scrips which apply to classes already loaded during bootstrap and retransform those classes
+        // so that rule triggers are injected
+
+        List<Class<?>> omitted = new LinkedList<Class<?>>();
+
+        Class<?>[] loaded = inst.getAllLoadedClasses();
+        if (isVerbose()) {
+            System.out.println("loaded classes size = " + loaded.length);
+        }
+
+        for (Class clazz : loaded) {
+            String name = clazz.getName();
+            int lastDot = name.lastIndexOf('.');
+
+            if (isBytemanClass(name) || !isTransformable(name)) {
+                continue;
+            }
+
+            boolean found = false;
+
+            // although this is done synchronized a transformation may sneak in between this check and
+            // the retransformClasses call below causing unnecessary redefinition of the some classes
+            // TODO -- see if we can tighten up the synchronization here (probably very tricky :-)
+            synchronized(targetToScriptMap) {
+                List<RuleScript> scripts = targetToScriptMap.get(name);
+                if (scripts != null) {
+                    for (RuleScript script : scripts) {
+                        System.out.println("Checking script " + script.getName());
+                        if (!script.hasTransform(clazz)) {
+                            omitted.add(clazz);
+                            found = true;
+                            if (isVerbose()) {
+                                System.out.println("Found script for bootstrap class " + clazz.getName());
+                            }
+                            break;
+                        }
+                    }
+                }
+                if (!found && lastDot >= 0) {
+                    scripts = targetToScriptMap.get(name.substring(lastDot + 1));
+                    if (scripts != null) {
+                        for (RuleScript script : scripts) {
+                            System.out.println("Checking script " + script.getName());
+                            if (!script.hasTransform(clazz)) {
+                                omitted.add(clazz);
+                                found = true;
+                                if (isVerbose()) {
+                                    System.out.println("Found script for bootstrap class " + clazz.getName());
+                                }
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        // retransform all classes for which we found untransformed rules
+
+        if (!omitted.isEmpty()) {
+            Class<?>[] transformedArray = new Class<?>[omitted.size()];
+            inst.retransformClasses(omitted.toArray(transformedArray));
+        }
+    }
+}

Modified: labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/RuleScript.java
===================================================================
--- labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/RuleScript.java	2009-09-04 14:54:01 UTC (rev 29186)
+++ labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/RuleScript.java	2009-09-04 16:00:06 UTC (rev 29187)
@@ -96,8 +96,8 @@
     }
 
     /**
-     * getter for list of transformed applied for this script. must be called synchronized on the script.
-     * @return te list of transforms
+     * getter for list of transforms applied for this script. must be called synchronized on the script.
+     * @return the list of transforms
      */
     public List<Transform> getTransformed()
     {
@@ -105,6 +105,15 @@
     }
 
     /**
+     * return a count of the number of transforms applied for this script. must be called synchronized on the script.
+     * @return the size of the list of transforms
+     */
+    public int getTransformedCount()
+    {
+        return (transformed != null ? transformed.size() : 0);
+    }
+
+    /**
      * invoked by the retransformer code when a rule is redefined to inhibit further transformations via this script
      */
     public synchronized void setDeleted()
@@ -154,6 +163,24 @@
         transformed.add(transform);
     }
 
+
+    public synchronized boolean hasTransform(Class<?> clazz)
+    {
+        ClassLoader loader = clazz.getClassLoader();
+
+        if (loader == null) {
+            loader = ClassLoader.getSystemClassLoader();
+        }
+
+        int count = getTransformedCount();
+        for (int i =  0; i < count; i++) {
+            Transform transform = transformed.get(i);
+            if (transform.getLoader() == loader) {
+                return true;
+            }
+        }
+        return false;
+    }
     /**
      * record the fact that a rule has been compiled wiht or without success
      * @param triggerClass the name of the trigger class to which the rule is attached
@@ -163,7 +190,7 @@
      */
     public synchronized void recordCompile(String triggerClass, ClassLoader loader, boolean successful, String detail)
     {
-        int count = transformed.size();
+        int count = getTransformedCount();
         for (int i =  0; i < count; i++) {
             Transform transform = transformed.get(i);
             if (transform.getLoader() == loader) {

Added: 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	                        (rev 0)
+++ labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/TransformListener.java	2009-09-04 16:00:06 UTC (rev 29187)
@@ -0,0 +1,219 @@
+package org.jboss.byteman.agent;
+
+import org.jboss.byteman.agent.Retransformer;
+
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.InetSocketAddress;
+import java.io.*;
+import java.util.List;
+import java.util.LinkedList;
+
+/**
+ * a socket based listener class which reads scripts from stdin and installs them in the current runtime
+ */
+public class TransformListener extends Thread
+{
+    public static int DEFAULT_PORT = 9091;
+    private static TransformListener theTransformListener = null;
+    private static ServerSocket theServerSocket;
+    private Retransformer retransformer;
+
+    private TransformListener(Retransformer retransformer)
+    {
+        this.retransformer = retransformer;
+        setDaemon(true);        
+    }
+
+    public static synchronized boolean initialize(Retransformer retransformer)
+    {
+        if (theTransformListener == null) {
+            try {
+                theServerSocket = new ServerSocket();
+                theServerSocket.bind(new InetSocketAddress("localhost", DEFAULT_PORT));
+            } catch (IOException e) {
+                System.out.println("TransformListener() : unexpected exception opening server socket " + e);
+                e.printStackTrace();
+                return false;
+            }
+            theTransformListener = new TransformListener(retransformer);
+
+            theTransformListener.start();
+        }
+
+        return true;
+    }
+
+    public static synchronized boolean terminate()
+    {
+        if (theTransformListener != null) {
+            try {
+                theServerSocket.close();
+            } catch (IOException e) {
+                // ignore -- the thread should exit anyway
+            }
+            try {
+                theTransformListener.join();
+            } catch (InterruptedException e) {
+                // ignore
+            }
+
+            theTransformListener = null;
+            theServerSocket = null;
+        }
+
+        return true;
+    }
+
+    public void run()
+    {
+        while (true) {
+            if (theServerSocket.isClosed()) {
+                return;
+            }
+            Socket socket = null;
+            try {
+                socket = theServerSocket.accept();
+            } catch (IOException e) {
+                if (!theServerSocket.isClosed()) {
+                    System.out.println("TransformListener.run : exception from server socket accept " + e);
+                    e.printStackTrace();
+                }
+                return;
+            }
+
+            handleConnection(socket);
+        }
+    }
+
+    private void handleConnection(Socket socket)
+    {
+        InputStream is = null;
+        try {
+            is = socket.getInputStream();
+        } catch (IOException e) {
+            // oops. cannot handle this
+            System.out.println("TransformListener.run : error opening socket input stream " + e);
+            e.printStackTrace();
+            try {
+                socket.close();
+            } catch (IOException e1) {
+                System.out.println("TransformListener.run : exception closing socket after failed input stream open" + e1);
+                e1.printStackTrace();
+            }
+            return;
+        }
+
+        OutputStream os = null;
+        try {
+            os = socket.getOutputStream();
+        } catch (IOException e) {
+            // oops. cannot handle this
+            System.out.println("TransformListener.run : error opening socket output stream " + e);
+            e.printStackTrace();
+            try {
+                socket.close();
+            } catch (IOException e1) {
+                System.out.println("TransformListener.run : exception closing socket after failed output stream open" + e1);
+                e1.printStackTrace();
+            }
+            return;
+        }
+
+        BufferedReader in = new BufferedReader(new InputStreamReader(is));
+        PrintWriter out = new PrintWriter(new OutputStreamWriter(os));
+
+        String line = null;
+        try {
+            line = in.readLine();
+        } catch (IOException e) {
+            System.out.println("TransformListener.run : exception " + e + " while reading command");
+            e.printStackTrace();
+        }
+
+        try {
+            if (line == null) {
+                out.println("ERROR");
+                out.println("Expecting input command");
+                out.println("OK");
+            } else if (line.equals("LOAD")) {
+                loadScripts(in, out);
+            } else if (line.equals("LIST")) {
+                listScripts(in, out);
+            } else {
+                out.println("ERROR");
+                out.println("Unexpected command " + line);
+                out.println("OK");
+            }
+        } catch (Exception e) {
+            System.out.println("TransformListener.run : exception " + e + " processing command " + line);
+            e.printStackTrace();
+            try {
+                socket.close();
+            } catch (IOException e1) {
+                System.out.println("TransformListener.run : exception closing socket " + e1);
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private void loadScripts(BufferedReader in, PrintWriter out) 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 (!line.equals("ENDLOAD")) {
+            out.append("ERROR ");
+            out.append("Unexpected end of line reading script " + scriptName + "\n");
+            out.println("OK");
+            out.flush();
+            return;
+        }
+
+        try {
+            retransformer.installScript(scripts, scriptNames);
+            out.println("OK");
+            out.flush();
+        } catch (Exception e) {
+            out.append("EXCEPTION ");
+            out.append(e.toString());
+            out.append('\n');
+            e.printStackTrace(out);
+            out.println("OK");
+            out.flush();
+        }
+    }
+
+    private void listScripts(BufferedReader in, PrintWriter out) throws Exception
+    {
+        retransformer.listScripts(out);
+        out.println("OK");
+        out.flush();
+    }
+}
\ No newline at end of file

Modified: labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Transformer.java
===================================================================
--- labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Transformer.java	2009-09-04 14:54:01 UTC (rev 29186)
+++ labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/Transformer.java	2009-09-04 16:00:06 UTC (rev 29187)
@@ -58,12 +58,14 @@
      *
      * @param inst the instrumentation object used to interface to the JVM
      */
-    public Transformer(Instrumentation inst, List<String> scriptPaths, List<String> scriptTexts)
+    public Transformer(Instrumentation inst, List<String> scriptPaths, List<String> scriptTexts, boolean isRedefine)
             throws Exception
     {
         theTransformer = this;
         this.inst = inst;
+        this.isRedefine = isRedefine;
         targetToScriptMap = new HashMap<String, List<RuleScript>>();
+        nameToScriptMap = new HashMap<String, RuleScript>();
 
         Iterator<String> iter = scriptTexts.iterator();
         int scriptIdx = 0;
@@ -77,10 +79,10 @@
         }
     }
 
-    private List<RuleScript> processScripts(String scriptText, String scriptFile) throws Exception
+    protected List<RuleScript> processScripts(String scriptText, String scriptFile) throws Exception
     {
         List<RuleScript> ruleScripts = new LinkedList<RuleScript>();
-
+        
         if (scriptText != null) {
             // split rules into separate lines
             String[] lines = scriptText.split("\n");
@@ -167,8 +169,24 @@
         return ruleScripts;
     }
 
-    private void indexScriptByTarget(RuleScript ruleScript)
+    protected void indexScriptByName(RuleScript ruleScript) throws Exception
     {
+        String name = ruleScript.getName();
+
+        synchronized (nameToScriptMap) {
+            RuleScript old = nameToScriptMap.get(name);
+            if (old != null) {
+                throw new Exception("duplicated rule name " + name +
+                        " at ruleScript " + old.getFile() + " line " + old.getLine() +
+                        " and ruleScript "  + ruleScript.getFile() + " line " + ruleScript.getLine());
+            }
+
+            nameToScriptMap.put(name, ruleScript);
+        }
+    }
+
+    protected void indexScriptByTarget(RuleScript ruleScript)
+    {
         String targetClass = ruleScript.getTargetClass();
 
         List<RuleScript> ruleScripts;
@@ -184,7 +202,7 @@
         ruleScripts.add(ruleScript);
     }
 
-    private void dumpScript(RuleScript ruleScript)
+    protected void dumpScript(RuleScript ruleScript)
     {
         String file = ruleScript.getFile();
         int line = ruleScript.getLine();
@@ -204,13 +222,14 @@
 
     private void addScript(RuleScript ruleScript) throws Exception
     {
+        indexScriptByName(ruleScript);
         indexScriptByTarget(ruleScript);
 
         if (isVerbose()) {
             dumpScript(ruleScript);
         }
     }
-    
+
     /**
      * The implementation of this method may transform the supplied class file and
      * return a new replacement class file.
@@ -284,7 +303,7 @@
         }
 
         // TODO-- reconsider this as it is a bit dodgy as far as security is concerned
-
+        
         if (loader == null) {
             loader = ClassLoader.getSystemClassLoader();
         }
@@ -496,7 +515,7 @@
      * @param className
      * @return true if a class is located in the byteman package otherwise return false
      */
-    private boolean isBytemanClass(String className)
+    protected boolean isBytemanClass(String className)
     {
         return className.startsWith(BYTEMAN_PACKAGE_PREFIX) && !className.startsWith(BYTEMAN_TEST_PACKAGE_PREFIX);
     }
@@ -551,7 +570,7 @@
      * @param className
      * @return true if a class is a potential candidate for insertion of event notifications otherwise return false
      */
-    private boolean isTransformable(String className)
+    protected boolean isTransformable(String className)
     {
         /*
          * ok, we are now going to allow any code to be transformed so long as it is not in the java.lang package
@@ -566,16 +585,28 @@
     /**
      * the instrumentation interface to the JVM
      */
-    private final Instrumentation inst;
+    protected final Instrumentation inst;
 
     /**
-     * a mapping from class names which appear in rule targets to a script object holding the
+     * true if the instrumentor allows redefinition
+     */
+    protected boolean isRedefine;
+
+    /**
+     * a mapping from target class names which appear in rules to a script object holding the
      * rule details
      */
 
-    private final HashMap<String, List<RuleScript>> targetToScriptMap;
+    protected final HashMap<String, List<RuleScript>> targetToScriptMap;
 
     /**
+     * a mapping from rule names which appear in rules to a script object holding the
+     * rule details
+     */
+
+    protected final HashMap<String, RuleScript> nameToScriptMap;
+
+    /**
      *  switch to control verbose output during rule processing
      */
     private final static boolean verbose = (System.getProperty(VERBOSE) != null);

Added: 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	                        (rev 0)
+++ labs/jbosstm/workspace/adinn/byteman/trunk/src/org/jboss/byteman/agent/submit/Submit.java	2009-09-04 16:00:06 UTC (rev 29187)
@@ -0,0 +1,186 @@
+package org.jboss.byteman.agent.submit;
+
+import org.jboss.byteman.agent.TransformListener;
+
+import java.io.*;
+import java.net.Socket;
+
+/**
+ * Provide a main routine for an app which submits a script to a byteman agent for installation in the JVM runtime
+ * or, with no arguments, lists all currently installed scripts
+ */
+public class Submit
+{
+    /**
+     * 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 [- port] [scriptfile . . .]
+     */
+    public static void main(String[] args)
+    {
+        int port = TransformListener.DEFAULT_PORT;
+        int startIdx = 0;
+        int maxIdx = args.length;
+
+        if (maxIdx >= 2 && args[0].equals("-p")) {
+            try {
+                port = Integer.valueOf(args[1]);
+            } catch (NumberFormatException e) {
+                System.out.println("Submit : invalid port " + args[1]);
+                System.exit(1);
+            }
+            if (port <= 0) {
+                System.out.println("Submit : invalid port " + args[1]);
+                System.exit(1);
+            }
+            startIdx = 2;
+        }
+        if (startIdx < maxIdx && args[startIdx].startsWith("-")) {
+            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;
+
+        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 all current scripts;
+            // !!! TODO -- invoke list command
+            out.println("LIST");
+            out.flush();
+            try {
+                String line = in.readLine();
+                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();
+            stringBuffer.append("LOAD\n");
+            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);
+                    }
+                    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);
+                }
+            }
+            stringBuffer.append("ENDLOAD\n");
+
+            out.append(stringBuffer);
+            out.flush();
+
+            try {
+                String line = in.readLine();
+                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);
+            }
+        }
+    }
+
+    public static void usage(int exitCode)
+    {
+        System.out.println("usage : Submit [-p port] [scriptfile . . .]");
+        System.out.println("        -p specifies listener port");
+        System.out.println("        no args means list installed scripts");
+        System.exit(exitCode);
+    }
+}



More information about the jboss-svn-commits mailing list