4 Replies Latest reply on Dec 12, 2012 8:54 AM by simonboggild

    Problems transforming an EJB

    simonboggild

      Hi,

       

      I've been testing Byteman (both version 2.1.0 and 2.1.2) lately in a JEE setup on Glassfish 3.1.2 together with Arquillian 1.0.3.Final and Shrinkwrap 1.1.1-alpha.

      Initially I created a simple test class (POJO) and instrumented it with Byteman using a @BMRule in my Arquillian test class. It worked as expected: The action parameter declared in the @BMRule annotation was executed.

       

      However, when I tried to do the same thing on an EJB, I could see in the log that an error occured when Byteman tried to transform the EJB class. Instead of the EJB throwing a RuntimeException as declared in the @BMRule annotation (see below), the EJB just followed its normal flow, indicating that it was not instrumented/transformed at all.

      I used the following BMRule annotation:

       

       

      @Test

                @BMRule(

                  name="MyInjectionRule",

                  targetClass="com.test.MySessionBean",

                  targetMethod="testMe",

                  action="throw new java.lang.RuntimeException();"

                  )

       

      and saw this output from the server log:

       

       

      INFO: TransformListener() : handling connection on port 8888

       

       

      INFO: retransforming com.test.MySessionBean

      INFO: retransforming com.test.MySessionBean

      INFO: org.jboss.byteman.agent.Transformer : possible trigger for rule MyInjectionRule in class com.test.MySessionBean

      INFO: RuleTriggerMethodAdapter.injectTriggerPoint : inserting trigger into com.test.MySessionBean.testMe(java.lang.String) java.lang.String for rule MyInjectionRule

      INFO: org.jboss.byteman.agent.Transformer : inserted trigger for MyInjectionRule in class com.test.MySessionBean

      INFO: Retransformer : VerifyError during retransformation : some rules may not have been correctly injected or uninjected!

      SEVERE: java.lang.VerifyError

                at sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)

                at sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:124)

                at org.jboss.byteman.agent.Retransformer.installScript(Retransformer.java:136)

                at org.jboss.byteman.agent.TransformListener.handleScripts(TransformListener.java:337)

                at org.jboss.byteman.agent.TransformListener.loadScripts(TransformListener.java:260)

                at org.jboss.byteman.agent.TransformListener.handleConnection(TransformListener.java:213)

                at org.jboss.byteman.agent.TransformListener.run(TransformListener.java:146)

       

       

      INFO: TransformListener() : handling connection on port 8888

      INFO: retransforming com.test.MySessionBean

      INFO: retransforming com.test.MySessionBean

       

      With regards to the whole Glassfish/Arquillian/Byteman setup, I followed the instructions from this post, found on the Arquillian forum: https://community.jboss.org/thread/213901?tstart=0

       

      How will I go about debugging what's causing the abovementioned error? As stated in the beginning of this post, Byteman seems to be doing its job on my simple POJO, so what could be the cause of it not being able to transform my EJB?

      Another observation is, that if I try to run my working POJO sample after having run the non working EJB sample, the POJO transformation fails with the same error as the EJB. Only after restarting the Glassfish server will the POJO sample start working again (as long as I don't run the EJB sample).

      Any help, hints much appreciated.

       

       

      Kind regards,

      Simon

        • 1. Re: Problems transforming an EJB
          adinn

          HI Simon,

           

          Thanks for reporting this problem.

           

          Can I assume this is a JDK7 release of Java you are using? Could you please let me know the precise details (for example, the output of java -version woudl be perfect).

           

          The verify error indicates that the JVM does not like the format of the code handed back to the VM after transformation. So, it suggests that either the Byteman transformer or, possibly, an EJB-specific transformer are making some incorrect assumptions about what is correct bytecode. Alternatively, they might both be making valid assumptions in isolation but breaking the code when acting in combination.

           

          In general, injection of Byteman rules into EJBs can be a little tricky because the target class may not actually be what it appears to be. The EJB implementation has to modify an EJB in order to make it capable of interacting with/being driven by an EJB container. So, when you specify a location where you want Byteman to inject the code (using the CLASS METHOD and AT clauses) that location may have shifted or may not contain the same local variables as in the original code or may include synthetic code generated by the EJB implementation. Conversely, if the Byteman transformer gets to modify the rule first it may introduce code which confuses the EJB implementation (I think that is less likely though, given what Byteman does).

           

          In order to help diagnose the problem I will probably need to see the bytecode of the EJB after it has been transformed by Byteman. Could you rerun your test wtih the following aditional java command line arguments

           

            -Dorg.jboss/.byteman.dump.generated.classes -Dorg.jboss.dump.generated.classes.directory=/path/to/my/dumpdir

           

          Obviously, you need to replace the path component with a path to an actual directory -- you can use a relative path if you want but the directory must exist.

           

          When you run the test Byteman should dump the transformed bytecode to

           

          .../dumpdir/com/test/MySessionBean.class

           

          You can either post me a zip with the clas file or else execute

           

            javap -c -private -classpath .../dumpdir com/test/MySessionBean

           

          and post me the output. If you can also post me the original source that would help. Obviously, if this code is confidential then please try to find another example which reproduces the problem and avoids releasing any confidential details.

          • 2. Re: Problems transforming an EJB
            simonboggild

            Hi Andrew.

             

            Thank you for your quick response, it's much appreciated.

            To firstly answer your question about the JDK version used, it's not JDK7 but 6.

            Java -version (running on a Mac OSX) yields the following output:

             

            java version "1.6.0_37"

            Java(TM) SE Runtime Environment (build 1.6.0_37-b06-434-11M3909)

            Java HotSpot(TM) 64-Bit Server VM (build 20.12-b01-434, mixed mode)

             

             

            The original source of the EJB looks the following:

             

            package com.test;

             

             

            import javax.ejb.Stateless;

            import javax.ejb.TransactionAttribute;

            import javax.ejb.TransactionAttributeType;

            import javax.ejb.TransactionManagement;

            import javax.ejb.TransactionManagementType;

             

             

            @Stateless(mappedName = "MySessionBean")

            @TransactionManagement(TransactionManagementType.CONTAINER)

            @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)

            public class MySessionBean implements MySessionBeanLocal

            {

             

             

                      @Override

                      public String testMe(String message)

                      {

                                System.out.println("Got message: " + message);

                                return message;

                      }

            }

             

             

            and the printed bytecode from the dump yields the following output:

             

             

            Compiled from "MySessionBean.java"

            public class com.test.MySessionBean extends java.lang.Object implements com.test.MySessionBeanLocal{

            public com.test.MySessionBean();

              Code:

               0:          aload_0

               1:          invokespecial          #21; //Method java/lang/Object."<init>":()V

               4:          return

             

             

            public java.lang.String testMe(java.lang.String);

              Code:

               0:          ldc          #25; //String MyInjectionRule_0

               2:          aload_0

               3:          aconst_null

               4:          invokestatic          #31; //Method org/jboss/byteman/rule/Rule.execute:(Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)V

               7:          getstatic          #43; //Field java/lang/System.out:Ljava/io/PrintStream;

               10:          new          #45; //class java/lang/StringBuilder

               13:          dup

               14:          ldc          #47; //String Got message:

               16:          invokespecial          #50; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V

               19:          aload_1

               20:          invokevirtual          #54; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

               23:          invokevirtual          #58; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;

               26:          invokevirtual          #63; //Method java/io/PrintStream.println:(Ljava/lang/String;)V

               29:          aload_1

               30:          areturn

               31:          invokevirtual          #67; //Method org/jboss/byteman/rule/exception/EarlyReturnException.getReturnValue:()Ljava/lang/Object;

               34:          checkcast          #69; //class java/lang/String

               37:          areturn

               38:          invokevirtual          #73; //Method org/jboss/byteman/rule/exception/ThrowException.getThrowable:()Ljava/lang/Throwable;

               41:          athrow

               42:          athrow

              Exception table:

               from   to  target type

                 0     7    31   Class org/jboss/byteman/rule/exception/EarlyReturnException

             

             

                 0     7    38   Class org/jboss/byteman/rule/exception/ThrowException

             

             

                 0     7    42   Class org/jboss/byteman/rule/exception/ExecuteException

             

             

             

             

            }

             

             

            Thanks,

            Simon

            • 3. Re: Problems transforming an EJB
              adinn

              Hi Simon,

               

              Looking at the bytecode I don't see any reason why it should result in a a verify error. The main part of the injected code is at bytecodes 0 to 6 which calls out to the rule engine. This is all just boiler plate code. At bytecodes 31 - 42 is the handler code which deals with the exceptions Byteman allows to escape the injection block. Bytecodes 31-36 handle an EarlyReturnException which Byteman uses to implement RETURN. It unpacks the embedded return value, ensures it is a String and then returns it. Bytecode 38-41 handles a ThrowException which Byteman uses to implement THROW. It unpacks the embedded Throwable and throws it. Bytecode 42 just rethrows an ExecuteException. All of this is correctly typed as far as I can see.

               

              It looks to me like this problem relates to some interaction between Byteman's transformation and whatever transformations the EJB code makes to the class. Clearly, with this example the code is unmodified by any EJB transform at the point when Byteman sees it so it looks like the changes Byteman has made invaldiate some assumptions about the layout of the bytecode that the EJB code depends upon. I don't really know what that might be though. You might try posting a message on the EJB forum asking for nay input on this case. Of course, the answer might just be don't inject dircetly into an EJB because it doesn't work (or maybe just doesn't always work). I'll be very happy to advise the EJB team on whatever aspects of the problem relate to Byteman is doing but I cannot debug this any further without some input from them.

               

              regards,

               

               

              Andrew Dinn

              • 4. Re: Problems transforming an EJB
                simonboggild

                Hi Andrew.

                 

                Alright, thank you for your initial investigations figuring out what could be the cause of this issue. I will try to take this matter further with the EJB team and see what more it could bring to enlighten things, and getting closer to finding a way to resolve the issue.

                 

                Thanks,

                Simon