1 2 Previous Next 18 Replies Latest reply on Sep 26, 2016 8:43 AM by Jörg Thönnes

    JCA and Class Loading on WildFly 8.2.0/9.0.0CR2

    Alex Lashchuk Newbie

      Hello,

       

      I am working on migrating an enterprise application from GlassFish 3.1 to WildFly. The application makes use of a JCA resource adapter, the sole purpose of which is to call a method in a native library. This is mainly to stay in compliance with the Java EE spec, which forbids direct calls to native methods in EJBs. I can confirm that the adapter loads and works correctly on GlassFish and the connection factory exposed by it can be injected as an interface reference into a CDI named bean using the @Resource annotation with the mappedName attribute.

       

      Now moving on to WildFly, the resource adapter deploys correctly and the native library packaged in its RAR is loaded into process space (verified on 64-bit Windows with Sysinternals Process Monitor) from the ephemeral vfs directory created by the container during deployment. However, two things appear broken:

       

      1. Any attempt to inject the connection factory reference into a named bean via its interface results in the following message:

       

      java.lang.IllegalArgumentException: Can not set net.buildinglogix.bdx.npi.api.INativePlatformInterfaceConnectionFactory field net.buildinglogix.test.RABean.factory to net.buildinglogix.bdx.npi.impl.NativePlatformInterfaceConnectionFactory
      at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:164)
      at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:168)
      at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:81)
      at java.lang.reflect.Field.set(Field.java:741)
      at org.jboss.weld.injection.FieldResourceInjection.injectMember(FieldResourceInjection.java:62)
      at org.jboss.weld.injection.AbstractResourceInjection.injectResourceReference(AbstractResourceInjection.java:53)
      at org.jboss.weld.util.Beans.injectEEFields(Beans.java:348)
      at org.jboss.weld.injection.producer.ResourceInjector$1.proceed(ResourceInjector.java:69)
      at org.jboss.weld.injection.InjectionContextImpl.run(InjectionContextImpl.java:48)
      at org.jboss.weld.injection.producer.ResourceInjector.inject(ResourceInjector.java:72)
      at org.jboss.weld.injection.producer.BasicInjectionTarget.inject(BasicInjectionTarget.java:121)
      at org.jboss.weld.bean.ManagedBean.create(ManagedBean.java:159)
      at org.jboss.weld.util.bean.IsolatedForwardingBean.create(IsolatedForwardingBean.java:45)
      at org.jboss.weld.context.AbstractContext.get(AbstractContext.java:96)
      at org.jboss.weld.context.PassivatingContextWrapper$AbstractPassivatingContextWrapper.get(PassivatingContextWrapper.java:76)
      at org.jboss.weld.bean.ContextualInstanceStrategy$DefaultContextualInstanceStrategy.get(ContextualInstanceStrategy.java:101)
      at org.jboss.weld.bean.ContextualInstanceStrategy$CachingContextualInstanceStrategy.get(ContextualInstanceStrategy.java:178)
      at org.jboss.weld.bean.ContextualInstance.get(ContextualInstance.java:50)
      at org.jboss.weld.manager.BeanManagerImpl.getReference(BeanManagerImpl.java:761)
      at org.jboss.weld.el.AbstractWeldELResolver.lookup(AbstractWeldELResolver.java:107)
      at org.jboss.weld.el.AbstractWeldELResolver.getValue(AbstractWeldELResolver.java:90)
      at org.jboss.as.jsf.injection.weld.ForwardingELResolver.getValue(ForwardingELResolver.java:46)

      .

      .

       

      Given that NativePlatformInterfaceConnectionFactory implements the INativePlatformInterfaceConnectionFactory, it appears that there is a classloader mismatch between the loaded JCA adapter class and the application calling into it.

       

      2. If the first problem is overcome with the use of dynamic JNDI lookups and reflection, any attempt at calling the native method from the resource adapter code fails with UnsatisfiedLinkError.

       

      I have done some digging around with a debugger and can confirm that the classloader used to load the native library during deployment is NOT the same as the classloader that is used to load the resource adapter classes. Furthermore, when the RA is initialized, the BootstrapContext instance passed to it does indeed have the correct classloader and that classloader correctly references the loaded native library. However, the classloader, which is used to create the RA classes themselves is different, and it does not contain any references to the native library. I believe this to be the source of the issues I listed above.

       

      My question is: does this appear to be a bug, or are there settings in WildFly that define the level of classloader isolation that I need to set somewhere to work around this problem? I verified that if the native library is placed directly on java.library.path and is explicitly loaded with System.loadLibrary in RA initialization code (from my local classloader), the adapter works correctly. However, this is contrary to how the native library loading behavior is defined in JCA spec and may fail in other containers. Looking at the source code of org.jboss.as.connector.deployers.ra.processors.RaNativeProcessor, line 85, all the right steps are being taken, but probably in the wrong classloader. I have tried this both on WildFly 8.2.0.Final and 9.0.0CR2, and the problem exists in both environments.

       

      Any help or insight into these issues will be greatly appreciated.

       

      Thanks in advance,

       

      Alex

        • 1. Re: JCA and Class Loading on WildFly 8.2.0/9.0.0CR2
          Philippe Marschall Expert

          How do you package your RAR? Inside and EAR or as a stand alone deployment? How often and in how many JARs is INativePlatformInterfaceConnectionFactory present?

          • 2. Re: JCA and Class Loading on WildFly 8.2.0/9.0.0CR2
            Alex Lashchuk Newbie

            Philippe,

             

            Thank you for responding. The RAR is packaged as a standalone deployment. There is one JAR, which defines INativePlatformInterfaceConnectionFactory and INativePlatformInterfaceConnection. However, this JAR is being packaged in both the RAR and the EAR. Initially, the JAR was only packaged in the RAR and the EAR used it only as a compile-time dependency. This worked on GlassFish, because its classloader ends up "knowing" about all classes, which are packaged in the RAR and the application would gain access to interface definitions automatically. When I tried that on WildFly, I started getting NoClassDefFoundError on deployment and so I was forced to include the JAR inside the EAR as well. Again, I suspect that this is all due to classloader isolation in WildFly.

             

            Thanks again,

             

            Alex

            • 3. Re: JCA and Class Loading on WildFly 8.2.0/9.0.0CR2
              Philippe Marschall Expert

              Alex Lashchuk wrote:

               

              Philippe,

               

              Thank you for responding. The RAR is packaged as a standalone deployment. There is one JAR, which defines INativePlatformInterfaceConnectionFactory and INativePlatformInterfaceConnection. However, this JAR is being packaged in both the RAR and the EAR. Initially, the JAR was only packaged in the RAR and the EAR used it only as a compile-time dependency.

              That is the correct and portable way and that should work. Let me quote JSR 342: JavaTM Platform, Enterprise Edition 7 (Java EE 7) Specification

              EE.8.3.2 EJB Container Class Loading Requirements

              Components in the EJB container must have access to the following classes and resources.

              • ...
              • The contents of all jar files included in each resource adapter archive(rar file) deployed separately to the application server, if that resource adapter is used to satisfy any resource references in the module.
              • ...

              You could hack around something with jboss-deployment-structure.xml but I wouldn't recommend this. I would make sure you have a resource reference in your deployment to a resource defined by the connector (eg. @Resource). If it then still fails file a JIRA because that would be a spec violation.

               

               

               

              1 of 1 people found this helpful
              • 4. Re: JCA and Class Loading on WildFly 8.2.0/9.0.0CR2
                Alex Lashchuk Newbie

                Thank you very much, Philippe. I will go ahead and file a bug report as neither way seems to work.

                 

                Alex

                • 5. Re: JCA and Class Loading on WildFly 8.2.0/9.0.0CR2
                  Philippe Marschall Expert

                  As a workaround until the bug is fixed you can try a jboss-deployment-structure.xml like this (and exclude the JAR from the EAR)

                   

                  <?xml version="1.0" encoding="UTF-8"?> 
                  <jboss-deployment-structure> 
                      <deployment> 
                          <dependencies> 
                                <module name="deployment.myresourceadapter.rar /> 
                          </dependencies> 
                      </deployment> 
                  </jboss-deployment-structure> 
                  
                  2 of 2 people found this helpful
                  • 6. Re: JCA and Class Loading on WildFly 8.2.0/9.0.0CR2
                    Alex Lashchuk Newbie

                    If anyone is interested, I have worked around the interface visibility issue by including the RAR in my EAR file and using jboss-deployment-structure.xml per Philippe's suggestion. The deployment structure file ended up being a little different, but the idea is basically the same:

                     

                    <?xml version="1.0" encoding="UTF-8"?>
                    <jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
                        <ear-subdeployments-isolated>false</ear-subdeployments-isolated>
                        <sub-deployment name="name_of_client_war_file.war">
                            <dependencies>
                                <module name="deployment.name_of_the_rar_file.rar" />
                            </dependencies>
                        </sub-deployment>
                    </jboss-deployment-structure>
                    

                     

                    Getting around the native library loading problem was not as easy. My solution (which I admit is a huge hack) was to load the helper class, which declares the native method, into the classloader that holds the reference to the native library. This is the classloader referenced by the BootstrapContext instance, which is passed into the start method of the resource adapter. So, in my resource adapter I now have the following code:

                     

                    private BootstrapContext bootstrap;
                    
                    public void start(BootstrapContext bc) throws ResourceAdapterInternalException
                    {
                         this.bootstrap = bc;
                    }
                    
                    public BootstrapContext getBootstrap()
                    {
                         return bootstrap;
                    }
                    

                     

                    Then in my managed connection class I load the helper class into the classloader of the BootstrapContext and use reflection to access the native method. In my case, the method takes no parameters and returns a byte array, so it was not too difficult to call. Your mileage may vary.

                     

                    Class<?> platformInterfaceClass = null;
                    BootstrapContext bc = resourceAdapter.getBootstrap();
                    
                    try
                    {
                         // Check if the class is already loaded. If it is, don't load another copy
                         platformInterfaceClass = bc.getClass().getClassLoader().loadClass("test.MyHelperClass");
                    }
                    catch(NoClassDefFoundError ex)
                    {
                    }
                    catch(ClassNotFoundException ex)
                    {
                    }
                    
                    if (platformInterfaceClass == null)
                    {
                         Class<?> clazz = PlatformInterface.class;
                         InputStream is = clazz.getClassLoader().getResourceAsStream("test/MyHelperClass.class");
                         ByteArrayOutputStream os = new ByteArrayOutputStream();
                         byte[] buffer = new byte[1024];
                         int bytesRead = Integer.MAX_VALUE;
                    
                         try
                         {
                              while(bytesRead > 0)
                              {
                                   bytesRead = is.read(buffer);
                    
                                   if (bytesRead > 0)
                                   {
                                        os.write(buffer, 0, bytesRead);
                                   }
                              }
                         }
                         catch(IOException ex)
                         {
                              return null;
                         }       
                    
                         clazz = bc.getClass().getClassLoader().getClass();
                    
                         try
                         {
                              // Make the defineClass method accessible. It is protected by default
                              Method m = null;
                    
                              while ((m == null) && (clazz != null))
                              {
                                   try
                                   {
                                        m = clazz.getDeclaredMethod("defineClass", new Class[] { String.class, byte[].class, int.class, int.class });
                                   }
                                   catch(NoSuchMethodException ex)
                                   {
                                        clazz = clazz.getSuperclass();
                                   }
                              }
                    
                              if (m == null) throw new ResourceAdapterInternalException("defineClass not found");
                    
                              m.setAccessible(true);
                    
                              byte[] bytes = os.toByteArray();
                    
                              platformInterfaceClass = (Class<?>) m.invoke(bc.getClass().getClassLoader(), new Object[] { null, bytes, 0, bytes.length });
                         }
                         catch(Exception ex)
                         {
                              return null;
                         }
                    }
                    
                    try
                    {
                         Object pi = platformInterfaceClass.newInstance();
                         Method m = platformInterfaceClass.getMethod("myJniMethod");
                         platformHash = (byte[]) m.invoke(pi);
                    }
                    catch(Exception ex)
                    {
                         return null;
                    }
                    return platformHash;
                    

                     

                    I have the code above executing from the UnsatisfiedLinkError exception handler around the original JNI call, so the solution works well under both WildFly and GlassFish.

                     

                    If anyone knows of a better way, which would not require the classloader hack above, please post your suggested solution, until then I am stuck with this workaround.

                     

                    Regards,

                     

                    Alex

                    • 7. Re: JCA and Class Loading on WildFly 8.2.0/9.0.0CR2
                      Syed Mahdi Apprentice

                      Hello Alex/All.

                       

                      i think I am having the same issue (I am new to Wildfly). It started with the error message as in your original post

                       

                      Can not Set (FactoryImplClass)  to (FactoryInterface) then I changed the factoryInterface to what the FactoryInterface was extending which was javax.resource.cci.ConnectionFactory, If i changed it to that then it went away and then I started having the java.lang.NoClassDefFoundError . If i added the RAR inside the ear/lib i started having the  java.lang.ClassCastException: com.myComp.MyCompConnector.InteractionSpec cannot be cast to com.myComp.MyCompConnector.InteractionSpec. (As there are two RARs) this also occured when i added only the jar in ear/lib. as there are two jars one in rar and one in ear/lib

                       

                      I detailed it out here as well java - Wildfly Class Loading issue with jca resourceadapter - Stack Overflow.

                       

                      There is a JIRA: [WFLY-4842] A resource packaged in a RAR file fails to call a native library packaged in the same RAR. - JBoss Issue Tra… of the same issue from you. but there is no resolution to it. Are we stuck with your work around?, I did not get the workaround in the JIRA: I tried loading my libraries in the java.library.path but it did not work as stated. I created a folder and placed my jar in there and the added it ot class path. It loads the folder in the java.library.path, but i still get the same classDefNotFoundError.

                       

                      I will appreciate your reply.

                       

                      If i do go with your solution (Which I am afraid I will have to) can you please recall how did you create the resourceAdapter to get the Boptstrap.

                      1. BootstrapContext bc = resourceAdapter.getBootstrap(); 

                       

                      I dont think this is a different bug but if it is Do i need to create a JIRA for it.

                      Thanks

                      • 8. Re: JCA and Class Loading on WildFly 8.2.0/9.0.0CR2
                        Philippe Marschall Expert

                        java.library.path is related to native code. ClassDefNotFoundError is related to Java code. WFLY-4842 applies only if you're getting an UnsatisfiedLinkError.

                        • 9. Re: JCA and Class Loading on WildFly 8.2.0/9.0.0CR2
                          Syed Mahdi Apprentice

                          Thanks for the reply. I really appreciate it Actually it seems that it is like the same problem. The only difference is that that the library is not loaded in the same class loader as the RAR itself. I actually circumvented the issue not by using the Dynamic JNDI reference lookup but by actually making the definition not to the interface itself but to the interface the interface extended.

                           

                          My issue si different in the sense that i have an ear file and the EJB module is where the library within in the Rar is referenced (The library is not in the Ear or War or Jar in that package, i am expecting the External RAR to be supplying it) and that is when it says ClassDefnotFoundError. If i Add the jar in the Ear file then it loads the Class but then it says that com.comp.MyCompConnector.MyCompConnectorInspector.java cannot be cast to com.comp.MyCompConnector.MyCompConnectorInspector.java. Which looks like a classloader issue.

                           

                          May be it is not the same issue but it does look like a bug in the Wildfly.

                           

                          I have a war where i reference the same RAR and it gets picked up without an issue. It seems like it is only an EAR issue esp if the library/class within the RAR is referenced inside the EJB jar in the ear.

                          • 10. Re: JCA and Class Loading on WildFly 8.2.0/9.0.0CR2
                            Stefano Maestri Expert

                            Can you provide a simple package reproducing the issue. If it's not too hard to produce it I would be happy to run w/ it in debug mode

                             

                            Thanks in advance

                            S.

                            • 11. Re: JCA and Class Loading on WildFly 8.2.0/9.0.0CR2
                              Stefano Maestri Expert

                              Have you chance to try your issue w/ WF10?

                               

                              regards

                              S.

                              • 12. Re: JCA and Class Loading on WildFly 8.2.0/9.0.0CR2
                                Syed Mahdi Apprentice

                                No I have not had a chance to run it on WF10. I am in the process of buidling a simple project to see if it can reproduce it.

                                 

                                Thanks

                                Syed

                                • 13. Re: JCA and Class Loading on WildFly 8.2.0/9.0.0CR2
                                  Syed Mahdi Apprentice

                                  I tried this issue in WF10 as well. It is the same.

                                  Actually What I think the problem is that : <module name="deployment.mycommunicatorappConnector-1.2.0.rar"/> never works for me, it always says Caused by: org.jboss.modules.ModuleNotFoundException: deployment.mycommunicatorappConnector-1.2.0.rar:main

                                  even if i put it in jboss-deployment-structure.xml of a jar.  (If i add it in the resource adapter definition in standalone.xml it only works with mycommunicatorappConnector-1.2.0.rar and not deployment.mycommunicatorappConnector-1.2.0.rar, so i tried both )

                                  I moved on to another project that is on jboss and needed to run on Wildfly, it also used the same mycommunicatorappConnector  rar module but had the same issue again  but this time it was a simple JAR login module taht referenced a class from the rar's main jar mycommunicatorappConnector-1.2.0.jar.  loginmodule cannot see deployed resource adapter

                                  I have been dealing with this issue and still no solution.

                                   

                                  A rar should technically be a globalmodule. It is referenced by every other application as a resource. So i tried to make it as a Global module like this:

                                  <global-modules>
                                                  <module name="javax.resource.api"/>
                                                  <module name="com.gbst.security.auth.spi"/>
                                    <module name="deployment.mycommunicatorappConnector-1.2.0.rar"/>
                                              </global-modules>
                                  

                                  09:40:10,208 INFO  [org.jboss.as.server.deployment] (MSC service thread 1-6) WFLYSRV0027: Starting deployment of "mycommunicatorapp-1.2.0.rar" (runtime-name: "mycommunicatorapp-1.2.0.rar")

                                  09:40:10,496 INFO  [org.jboss.ws.common.management] (MSC service thread 1-4) JBWS022052: Starting JBoss Web Services - Stack CXF Server 5.0.0.Final

                                  09:40:10,629 WARN  [org.jboss.as.server.deployment] (MSC service thread 1-2) WFLYSRV0059: Class Path entry messages.jar in /C:/JAVA/WF9/wildfly-9.0.0.Final/bin/content/mycommunicatorapp-1.2.0.rar/mess

                                  ages-10.2b07.jar  does not point to a valid jar for a Class-Path reference.

                                  09:40:10,631 WARN  [org.jboss.as.server.deployment] (MSC service thread 1-2) WFLYSRV0059: Class Path entry schema.jar in /C:/JAVA/WF9/wildfly-9.0.0.Final/bin/content/mycommunicatorapp-1.2.0.rar/messag

                                  es-10.2b07.jar  does not point to a valid jar for a Class-Path reference.

                                  09:40:10,809 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-4) MSC000001: Failed to start service jboss.module.service."deployment.mycommunicatorapp-1.2.0.rar".main: org.jboss.msc.service.St

                                  artException in service jboss.module.service."deployment.mycommunicatorapp-1.2.0.rar".main: WFLYSRV0179: Failed to load module: deployment.mycommunicatorapp-1.2.0.rar:main

                                          at org.jboss.as.server.moduleservice.ModuleLoadService.start(ModuleLoadService.java:91)

                                          at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1948)

                                          at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1881)

                                          at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)

                                          at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)

                                          at java.lang.Thread.run(Thread.java:745)

                                  Caused by: org.jboss.modules.ModuleNotFoundException: deployment.mycommunicatorapp-1.2.0.rar:main

                                          at org.jboss.modules.Module.addPaths(Module.java:1042)

                                          at org.jboss.modules.Module.link(Module.java:1398)

                                          at org.jboss.modules.Module.relinkIfNecessary(Module.java:1426)

                                          at org.jboss.modules.ModuleLoader.loadModule(ModuleLoader.java:238)

                                          at org.jboss.as.server.moduleservice.ModuleLoadService.start(ModuleLoadService.java:68)

                                          ... 5 more

                                   

                                   

                                  09:40:10,816 ERROR [org.jboss.as.controller.management-operation] (Controller Boot Thread) WFLYCTL0013: Operation ("add") failed - address: ([("deployment" => "mycommunicatorapp-1.2.0.rar")]) - failur

                                  e description: {"WFLYCTL0080: Failed services" => {"jboss.module.service.\"deployment.mycommunicatorapp-1.2.0.rar\".main" => "org.jboss.msc.service.StartException in service jboss.module.service.\"dep

                                  loyment.mycommunicatorapp-1.2.0.rar\".main: WFLYSRV0179: Failed to load module: deployment.mycommunicatorapp-1.2.0.rar:main

                                      Caused by: org.jboss.modules.ModuleNotFoundException: deployment.mycommunicatorapp-1.2.0.rar:main"}}

                                  09:40:10,849 INFO  [org.jboss.as.server] (Controller Boot Thread) WFLYSRV0010: Deployed "mycommunicatorapp-1.2.0.rar" (runtime-name : "mycommunicatorapp-1.2.0.rar")  (Seroulsy !!!!  )

                                   

                                  is this a order issue, like in what order it is loading the module and when it completes the deployment. If you can help me out, that will be great as I have tried all of the ways i can think of.

                                   

                                  Thanks

                                  Syed

                                  • 14. Re: JCA and Class Loading on WildFly 8.2.0/9.0.0CR2
                                    Stefano Maestri Expert

                                    You are trying to use a deployment as a module. If you want to use it as module (global module in your case) you should define it in module directory, because stacktrace is saying it's not finding module in that directory. For documentation on how define a module have a looke there:

                                    Defining a module - JBoss Modules - Project Documentation Editor

                                     

                                    Other infos about classloading can be found here:

                                    Class Loading in WildFly - WildFly 8 - Project Documentation Editor

                                     

                                    regards

                                    S.

                                    1 2 Previous Next