5 Replies Latest reply on Sep 27, 2019 12:08 PM by Lei Yu

    How to print out exception in a method?

    Lei Yu Newbie

      I am trying to write a byteman rule to capture the exception in my test class, the class is written to throw IndexOutOfBoundsException

       

       

      public void myMethod(PrintWriter out) {

        ConcurrentMap<String, String> applicationMap =

         new ConcurrentHashMap<String, String>();

       

         // The FileWriter constructor throws IOException, which must be caught.
         System.out.println("SecondServlet.myMethod: just in");

         // PrintWriter out = null;

         int start =0;

         try {

        out = new PrintWriter(new FileWriter("OutFile.txt"));

       

        System.out.println("args[" +

        Integer.toString(start) );

       

         for (int i = 0; i < SIZE; i++) {

         // The get(int) method throws IndexOutOfBoundsException, which must be caught.
         out.println("Value at: " + i + " = " + list.get(11));

        }

        } catch (IOException ce) {

        applicationMap.put("error", "test1");

        ce.printStackTrace();

        } catch (IndexOutOfBoundsException ie) {

        System.out.println("SecondServlet.myMethod idenxoutofbound");

        applicationMap.put("error", "test2");

        System.out.println("SecondServlet.myMethod "+ applicationMap.get("error"));

         // ie.printStackTrace();

         } finally {

        out.close();

        }

        System.out.println("SecondServlet.myMethod >>>> done");

      }

       

      My rule is

       

      RULE myMethod Exception 1

      CLASS com.larinia.app.SecondServlet

      METHOD myMethod

      AT CALL applicationMap.put

      IF TRUE

      DO

      traceln("********>IndexOutOfBoundsException " + $ie)

      ENDRULE

       

      but AT CALL does not seems to work here, what can I use to get $ie out please? I have tried AT LINE, which does not work either.

       

      Thanks

        • 1. Re: How to print out exception in a method?
          Andrew Dinn Master

          Hi Lei Yu,

           

          Let's just use Java syntax formatting so we can read your code a bit better

           

          public void myMethod(PrintWriter out) {
              ConcurrentMap<string, string=""> applicationMap =
                  new ConcurrentHashMap<string, string="">();
              // The FileWriter constructor throws IOException, which must be caught.
              System.out.println("SecondServlet.myMethod: just in");
              // PrintWriter out = null;
              int start = 0;
          
              try {
                  out = new PrintWriter(new FileWriter("OutFile.txt"));
                  System.out.println("args[" +
                                     Integer.toString(start) );
          
                  for (int i = 0; i < SIZE; i++) {
                      // The get(int) method throws IndexOutOfBoundsException, which must be caught.
                      out.println("Value at: " + i + " = " + list.get(11));
                  }
              } catch (IOException ce) {
                  applicationMap.put("error", "test1");
                  ce.printStackTrace();
              } catch (IndexOutOfBoundsException ie) {
                  System.out.println("SecondServlet.myMethod idenxoutofbound");
                  applicationMap.put("error", "test2");
                  System.out.println("SecondServlet.myMethod "+ applicationMap.get("error"));
                  // ie.printStackTrace();
              } finally {
                  out.close();
              }
              System.out.println("SecondServlet.myMethod >>>> done");
          }

           

          There are a few things missing from the code that need adding so we can test this. list appears to be a field of the owning class and SIZE appears to be a final constant.

           

          Also, you there are a few things wrong with the program. You pass out as an argument then shadow it with a local variable out in the try block. So, the method argument is not needed. However, when you delete it you find that the call to close in the finally block does nto compile because out is undefined. That's because the local variable out goes out of scope when you leave the try block. You need to declare it outside the block and intiialize it inside.  You also need to fix the finally code so it does not call close if out == null.

           

          Here is a slightly modified version which fixes these problems and can be compiled and run:

           

          import java.io.FileWriter;
          import java.io.IOException;
          import java.io.PrintWriter;
          import java.util.concurrent.ConcurrentMap;
          import java.util.concurrent.ConcurrentHashMap;
          import java.util.List;
          import java.util.LinkedList;
          
          public class MyTest
          {
              private List list;
              public final static int SIZE = 3;
              public MyTest() {
                  list = new LinkedList();
                  list.add("one");
                  list.add("two");
                  list.add("three");
              }
              public void myMethod() {
                  ConcurrentMap<string, string=""> applicationMap =
                      new ConcurrentHashMap<string, string="">();
                  // The FileWriter constructor throws IOException, which must be caught.
                  System.out.println("SecondServlet.myMethod: just in");
                  int start = 0;
                  PrintWriter out = null;
          
                  try {
                      out = new PrintWriter(new FileWriter("OutFile.txt"));
                      System.out.println("args[" +
                                         Integer.toString(start) );
          
                      for (int i = 0; i < SIZE; i++) {
                          // The get(int) method throws IndexOutOfBoundsException, which must be caught.
                          out.println("Value at: " + i + " = " + list.get(11));
                      }
                  } catch (IOException ce) {
                      applicationMap.put("error", "test1");
                      ce.printStackTrace();
                  } catch (IndexOutOfBoundsException ie) {
                      System.out.println("SecondServlet.myMethod indexoutofbound");
                      applicationMap.put("error", "test2");
                      System.out.println("SecondServlet.myMethod "+ applicationMap.get("error"));
                      // ie.printStackTrace();
                  } finally {
                      if (out != null) {
                          out.close();
                      }
                  }
                  System.out.println("SecondServlet.myMethod >>>> done");
              }
              public static void main(String[] args) {
                  MyTest myTest = new MyTest();
                  myTest.myMethod();
              }
          }
          So, just to check it works as expected here is a normal run
          [adinn@localhost byteman]$ javac -g MyTest.java
          [adinn@localhost byteman]$ java MyTest
          SecondServlet.myMethod: just in
          args[0
          SecondServlet.myMethod indexoutofbound
          SecondServlet.myMethod test2
          SecondServlet.myMethod >>>> done
          [adinn@localhost byteman]$ 
          
          

          Notice that I passed -g to javac. That's because you want to refer to local variable ie in your rule. You need to ensure  javac adds symbols to the bytecode for class MyTest if you want to be able to refer to them in rules.

           

          Now, lets look at the rule you provided (n.b. I have changed the class name to refer to class MyTest)

           

          RULE myMethod Exception 1
          CLASS MyTest
          METHOD myMethod
          AT CALL applicationMap.put
          IF TRUE
          DO
          traceln("********>IndexOutOfBoundsException " + $ie)
          ENDRULE
          
          

           

          The first thing to do is to run script bmcheck to see if the rule works ok

           

          $ bmcheck.sh -cp . mytest.btm
          Checking rule myMethod Exception 1 against class MyTest
          WARNING : Problem type checking rule "myMethod Exception 1" loaded from mytest.btm line 5 against method myMethod() void
          org.jboss.byteman.rule.exception.TypeWarningException: no matching injection point for method myMethod() void
          WARNING : Problem type checking rule "myMethod Exception 1" loaded from mytest.btm line 5
          org.jboss.byteman.rule.exception.TypeWarningException: failed to find any matching trigger method in class MyTest
          TestScript: 2 total warnings
                      2 type warnings
          [adinn@localhost byteman]$ 
          
          

          So, Byteman tried to inject the rule into the right clas sand method but could not find an injection location that matches. That's easy to explain. The AT CALL location says that the call is to method called applicationMap.put. That doesn't actually name a method. applicationMap is the name of a variable of type ConcurrentMap. So, if you want to name the method that is being called you need to refer to it as ConcurrentMap.put. Ok, so let's fix that

           

          RULE myMethod Exception 1
          CLASS MyTest
          METHOD myMethod
          AT CALL ConcurrentMap.put
          IF TRUE
          DO
          traceln("********>IndexOutOfBoundsException " + $ie)
          ENDRULE
          
          

           

          and run bmcheck again

           

          [adinn@localhost byteman]$ bmcheck -cp . mytest.btm
          Checking rule myMethod Exception 1 against class MyTest
          WARNING : Problem type checking rule "myMethod Exception 1" loaded from mytest.btm line 5 against method myMethod() void
          org.jboss.byteman.rule.exception.TypeWarningException: invalid local variable binding $ie for method myMethod() void
          WARNING : Problem type checking rule "myMethod Exception 1" loaded from mytest.btm line 5
          org.jboss.byteman.rule.exception.TypeWarningException: failed to find any matching trigger method in class MyTest
          TestScript: 2 total warnings
                      2 type warnings
          [adinn@localhost byteman]$ 
          
          
          So, now Byteman has foudn a place to inject and rejected it because local variable ie is not in scope at that point. This is because it has foudn the first call to ConcurrentMap.put in the first catch block and, yes, ie is not in scope at that call. The full syntax for AT CALL is
            AT CALL method [count=1]
          where method identifies the method being called and count identifies which call you want if more than one cal appears in the body of the methodl. The default count is 1. You can specify ALL to inject at every call or a positive number N to inject at the Nth call. So, what you want is to inject at the 2nd call:
          RULE myMethod Exception 1
          CLASS MyTest
          METHOD myMethod
          AT CALL ConcurrentMap.put 2
          IF TRUE
          DO
          traceln("********>IndexOutOfBoundsException " + $ie)
          ENDRULE
          
          

          If we try again with that rule it bmcheck shoudl be happy

          [adinn@localhost byteman]$ bmcheck -cp . mytest.btm
          Checking rule myMethod Exception 1 against class MyTest
          Parsed rule "myMethod Exception 1" for class MyTest
          Type checked rule "myMethod Exception 1"
          TestScript: no errors
          [adinn@localhost byteman]$ 
          
          

           

          Ok, so now let's run it:

           

          [adinn@localhost byteman]$ bmjava -l mytest.btm MyTest
          SecondServlet.myMethod: just in
          args[0
          SecondServlet.myMethod indexoutofbound
          ********>IndexOutOfBoundsException java.lang.IndexOutOfBoundsException: Index: 11, Size: 3
          SecondServlet.myMethod test2
          SecondServlet.myMethod >>>> done
          [adinn@localhost byteman]$ 
          
          

           

          I hope that helps you to get started ijecting rules into your application. Let me know if you need more help getting your ruels to run.

           

          regards,

           

           

          Andrew Dinn

           

          • 2. Re: How to print out exception in a method?
            Lei Yu Newbie

            Hi Andrew,

             

            Thanks a lot for the detailed explanation, it is clear to me now. Sorry about my test program, I have not done any coding for years. My real aim was to create a byteman rule for the class FactoryFinder at line 630

             

            mojarra/FactoryFinder.java at ac33eac70d458d97c3a7ba7f8ea504e16f23a930 · jboss/mojarra · GitHub

             

            and line 637, but as we cannot reproduce the exception, I created a test program. So for my test program, there are two exceptions, how do you use the rule to capture either IOException and IndexOutOfBoundsException, here both have AT CALL ConcurrentMap.put 2, is there a way to check which exception it is? I would like to distinguishing which exceptions the application has encountered, but the local variable for both exceptions are different.

             

            I would also like to know:

             

            * For a static method that returns Object, what would be $0?

            * Is it possible to use bmcheck.sh when the application is deployed to JBoss? I have used -cp to add classpath, but it was still complaining missing libs.

             

            Thanks a lot

             

            Lei

            • 3. Re: How to print out exception in a method?
              Lei Yu Newbie

              Hi Andrew,

               

              I have figured out below

               

              So for my test program, there are two exceptions, how do you use the rule to capture either IOException and IndexOutOfBoundsException, here both have AT CALL ConcurrentMap.put 2, is there a way to check which exception it is? I would like to distinguishing which exceptions the application has encountered, but the local variable for both exceptions are different.

               

               

              The rules below works for catching both exceptions

               

              RULE myMethod Exception 1

              CLASS com.larinia.app.SecondServlet

              METHOD myMethod

              AT CALL ConcurrentMap.put 1

              IF TRUE

              DO

              traceln("********> IOException" + $ce);

              ENDRULE

               

               

              RULE myMethod Exception 2

              CLASS com.larinia.app.SecondServlet

              METHOD myMethod

              AT CALL ConcurrentMap.put 2

              IF TRUE

              DO

              traceln("********> IndexOutOfBoundexception" + $ie);

              ENDRULE

               

              I am still not sure about the answers for my other two questions, if you can help would be great.

               

              BTW, the FactoryFinder is not compile with -g flag, would that effect the result of byteman? I didn't use AT LINE before of that.

               

              Kind Regards

               

              Lei

              • 4. Re: How to print out exception in a method?
                Andrew Dinn Master

                Hi Lei Yu,

                 

                * For a static method that returns Object, what would be $0?

                * Is it possible to use bmcheck.sh when the application is deployed to JBoss? I have used -cp to add classpath, but it was still complaining missing libs.

                 

                $0 cannot be used in a rule injected into a static method. $0 can only be used in an instance method to refer to this.

                 

                bmcheck is an offline checker. When you have several jars you have to provide multiple -cp arguments like this:

                 

                $ bmcheck -cp AppClass.jar -cp LibClasses.jar -cpo LibClasses2.jar myscript.btm

                 

                You won't need to provide every jar that you app uses. bmcheck has to be able to load the classes or interfaces mentioned in the CLASS clauses of your rules. So, assume your rules mention classes C1 and C2 and interface I1  Obviously, you need to provide the jars that include class files for C1, C2 and I1. You will also need to include jars that contain classes that are directly referenced from C1, C2 and I1. So, if C1 imports C3 or has a field of type I2 and C3 or I2 are in a library jar then will need to add the librray jar on the command using -cp.

                 

                BTW, the FactoryFinder is not compile with -g flag, would that effect the result of byteman? I didn't use AT LINE before of that.

                 

                When you inject into a class that has not been compiled with -g you cannot use line numbers to specify the location for rule injection (that may explain why your AT LINE rule failed). Also, you cannot employ local variable names (like ce in the FactoryFinder code you refer to) or parameter variable names (like the ClassLoader parameter cl).  You can still refer to parameter cl by index. Since it is the first parameter you would refer to it as $1. Byteman knows that this parameter is of type ClassLoader. You can also refer to $0 (the FactoryManagerCache instance that is handling the getApplicationFactoryManager) and you can refer to field applicationMap using $0.applicationMap. Unfortunately, without -g compilation there is no way for a Byteman rule to access the value of local variables.

                 

                regards,

                 

                 

                Andrew Dinn

                 

                1 of 1 people found this helpful
                • 5. Re: How to print out exception in a method?
                  Lei Yu Newbie

                  Hi Andrew,

                   

                  Thanks a lot for your help. It's much clearer to me now.

                   

                  I will try out the bmcheck script with relevant jars. My mistake before was setting JBOSS_HOME as the classpath.

                   

                  Thanks again.

                   

                  Lei