3 Replies Latest reply on Nov 29, 2012 4:34 AM by adinn

    Arquillian Byteman extension - Rule ruleMapKey always empty -> no code injected

    henrik_sr

      Hi

      This question may not be related to Byteman but instead the Arquillian forum - if so I will move it.

       

      I am working on a project where we use Arquillian together with GlassFish 3.

       

      However, we have struggled to use the Arquillian-ByteMan extension and seem to have hit wall wrt. getting our injection code invoked.

       

      When we debug, the context in which the public static execute method on Rule is invoked has an empty  HashMap<String, Rule> ruleKeyMap.

      Hence our injected rule code is never invoked.

      Before reaching the execute method we have verified that the rule is in fact added to the ruleKeyMap and not yet been purged.

       

      Below you will find the very simple test class and also the dependencies used.

       

      Please note that I have to set autoInstallAgent to false and in my running Glassfish3 instance add this to the

      JVM options of the server config: -javaagent:${com.sun.aas.instanceRoot}/lib/byteman-2.1.0.jar=listener:true,port:9999

       

      So to sum up - can anyone maybe explain in which contexts the Rule class is used and why this problem occurs.

       

      Currently we can not defend spending more time on getting this work so another approach will have to be taken.

      If anyone can guide us in the right direction I will however try to get this working since it really seems ideal for our error test scenarios.

       

      Thank you in advance.

       

      Regards Henrik

       

      @RunWith(Arquillian.class)

      public class ByteManGlassfish3Test_IT

      {

       

          @Deployment

          public static Archive<?> deployment()

          {

              return DeploymentHelper.createArchive();

          }

       

          @Test

          @BMRule(

                  name="FaultyInjectionRule",

                  targetClass="com.ejb.egs.ByteManGlassfish3Test_IT.TestClass",

                  targetMethod="testMethod",

                  action="System.out.println(\"Test impl\");"

                  )

       

          public void injectFaultyMethod() throws Exception

          {

              new TestClass().testMethod();

          }

       

          public static class TestClass

          {

              public void testMethod()

              {

                  System.out.println("Real Impl");

                  Assert.fail();

              }

          }

      }

      Added to my pom.xml

        <properties>

            <toolsjar>${java.home}/../Classes/classes.jar</toolsjar>

            <version.byteman>2.1.0</version.byteman>

        </properties>

      <dependency>

                <groupId>org.jboss.arquillian.extension</groupId>

                <artifactId>arquillian-extension-byteman</artifactId>

                <version>1.0.0.Alpha2</version>

                <scope>test</scope>

            </dependency>

            <dependency>

                <groupId>com.sun</groupId>

                <artifactId>tools</artifactId>

                <version>1.6.0</version>

                <scope>system</scope>

                <systemPath>${toolsjar}</systemPath>

            </dependency>

            <dependency>

                <groupId>org.jboss.byteman</groupId>

                <artifactId>byteman</artifactId>

                <version>${version.byteman}</version>

                <scope>provided</scope>

            </dependency>

            <dependency>

                <groupId>org.jboss.byteman</groupId>

                <artifactId>byteman-submit</artifactId>

                <version>${version.byteman}</version>

                <scope>provided</scope>

            </dependency>

            <dependency>

                <groupId>org.jboss.byteman</groupId>

                <artifactId>byteman-install</artifactId>

                <scope>test</scope>

                <version>${version.byteman}</version>

            </dependency>

            <dependency>

                <groupId>org.jboss.byteman</groupId>

                <artifactId>byteman-bmunit</artifactId>

                <scope>test</scope>

                <version>${version.byteman}</version>

            </dependency>

       

      Added to my arquillian.xml in test resources:

      <extension qualifier="byteman">

            <property name="autoInstallAgent">false</property>

            <property name="agentProperties">org.jboss.byteman.verbose=true</property>

            <property name="clientAgentPort">9999</property>

            <property name="containerAgentPort">9999</property>

        </extension>

       

      Added to my running Glassfish 3 instance   

        -javaagent:${com.sun.aas.instanceRoot}/lib/byteman-2.1.0.jar=listener:true,port:9999

          -Dorg.jboss.byteman.verbose=true

        • 1. Re: Arquillian Byteman extension - Rule ruleMapKey always empty -> no code injected
          adinn

          Hi Henrik,

           

          I don't know exactly how the Arquilian code works as I did not implement it. However, this looks remarkably like a problem I have seen before where the Byteman Rule class is being loaded in both the system and bootstrap classloaders. So, even if it is caused by Arqulian rather than your code here is one avenue to investigate before pushing this over to the Arquilian team.

           

          When the problem you describe has been observed before it is because there are two versions of class Rule in the runtime. The Byteman agent (loaded via the bootstrap loader) installs the rule key in the static Hashmap associated with the bootstrap loader version of the class. However, the application method containing the injected callout looks up the rule key in the static Hashmap associated with the system loader version of the class. So, you find yourself in classloader hell modulo a missing the ClassCastException.

           

          The way this normally happens is as follows

           

          The Byteman jar is in the CLASSPATH which means it can be loaded via the system loader even though this is not desired (I believe this is always the case when running with maven/Arquilian)

           

          A class in your application which references class Rule (or some other Byteman class which indirectly references Rule) gets loaded before your test is run.

           

          The app executes some code on this class which (directly or indirectly) requires dynamic resolution of the link to class Rule (it usually turns out that the dependency needs to be resolved because you have executed code belonging to a Helper class but in your case it might also be an Arqulian class which is doing this)

           

          Class Rule is loaded via the classpath installing it in the system classloader.

           

          Arquilian schedules the first test which relies upon Byteman.

           

          The Byteman agent is loaded dynamically and as it is loaded  places the Byteman jar in the bootstrap classpath (this step is necessary in order to ensure that Byteman can inject rules into JVM runtime classes).

           

          Arquilian submits a request to the agent to load rules specified in @BMRule or @BMScript annotations.

           

          The agent (whose class is located in the bootstrap path) loads a 2nd version of class Rule via the bootstrap loader and installs the injected Rule instance keys into its static hashmap.

           

          The test code is run and executes the injected trigger code, calling Rule.execute.

           

          The latter method is resolved using the application classloader context and so finds the method attached to the 1st version of Rule which checks the empty static hashmap.

           

          Viola! (or, as they say in Germany, gigue!)

           

          So, I recommend you check to see if either your app or Arquilian might be referencing some Byteman classes and loading Rule before the test is loaded.

           

          If you find this is a problem then it should still be possible to refactor the code to avoid the dependency on Rule being triggered before the agent is loaded. e.g if the problem is a Helper class which references Rule(say, because you want your helper to be able to manipulate app data) then you just need to isolate the Helper methods from the app code. So, for example, don't use an app class directly as a helper, have the helper class call out to app code.

           

          If it turns out to be an Arquilian dependency which is referencing class Rule then this will need to be taken to the Arquilian forum. I'll be happy to follow this up with the Arquilian team if you can rule out any possible reference from your class. It would also help if you could find any evidence for it being an Arqulian dependency or, at least, provide a reproducer for it which you can give to the Arquilian team.

           

          regards,

           

           

          Andrew Dinn

          1 of 1 people found this helpful
          • 2. Re: Arquillian Byteman extension - Rule ruleMapKey always empty -> no code injected
            henrik_sr

            Hi Andrew

             

            Thank you for your speedy and helpful answer.

            It helped me and my colleague to understand parts of the problem.

            I also asked the same question in the Arquillian forum (sorry for cross-posting if thats considered bad-style) and Aslak provided an answer which in the end helped me get the setup to work.

            See the post here: https://community.jboss.org/message/778832.

             

            However, I am still observing something which I would like to be sure that I have understood correctly.

            If you would please read my comment to Aslak's answer, I would appreciated that since it is probably related to how Byteman works.

            Again - thank you.

            • 3. Re: Arquillian Byteman extension - Rule ruleMapKey always empty -> no code injected
              adinn

              Hi Henrik,

               

              I have answered this on the Arquilian forum.

               

              regards,

               

               

              Andrew Dinn