Search code examples
javagroovyapache-camelcdiwildfly-10

unable to resolve class issue with Executing Grrovy Scripts in Java in Wildfly with Camel CDI


Background :

I'm invoking a Groovy script using GrrovyShell as this.

@GET
@Path("/endpoint/{param}")
public Response getEndpointService(@PathParam("param") String messageId) {

    String scriptPath = "/path/to/Grrovyfile.gsh";

    Binding binding = new Binding();
    binding.setProperty("in", inParams);

    GroovyShell shell = new GroovyShell(binding);
    shell.evaluate(new File(scriptPath));

}

Groovy Script File :

def condition = "good"
def severity = "N/A"
def detail = ""

import net.sf.json.JSONObject
import java.util.Random

try{

    def detailMap = [
        condition: condition,
        success: false,
        severity: severity,
    ]

    JSONObject jsonObject = JSONObject.fromObject(detailMap)
    detail = jsonObject.toString()
}

And there is a seperate camel router implementation in this war and it is get initialized with camel cdi.

import javax.enterprise.context.ApplicationScoped;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.cdi.ContextName;

@ApplicationScoped
@ContextName("camel-cdi-context")
public class MiscRouter extends RouteBuilder {

    @Override
    public void configure() throws Exception {
      // router dsl
    }

}

Problem :
Above groovy code is getting failed with below error when the router is stared with the camel cdi. But is succeeded with no errors when the @ApplicationScoped, @ContextName("camel-cdi-context") lines are commented.

12:47:28,108 ERROR [stderr] (default task-11) org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
12:47:28,109 ERROR [stderr] (default task-11) /path/to/Grrovyfile.gsh: 9: unable to resolve class net.sf.json.JSONObject
12:47:28,109 ERROR [stderr] (default task-11)  @ line 9, column 1.
12:47:28,109 ERROR [stderr] (default task-11)    import net.sf.json.JSONObject
12:47:28,109 ERROR [stderr] (default task-11)    ^
12:47:28,109 ERROR [stderr] (default task-11) 
12:47:28,109 ERROR [stderr] (default task-11) 1 error
12:47:28,109 ERROR [stderr] (default task-11) 
12:47:28,110 ERROR [stderr] (default task-11)   at org.codehaus.groovy.control.ErrorCollector.failIfErrors(ErrorCollector.java:310)
12:47:28,110 ERROR [stderr] (default task-11)   at org.codehaus.groovy.control.CompilationUnit.applyToSourceUnits(CompilationUnit.java:958)
12:47:28,110 ERROR [stderr] (default task-11)   at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:605)
12:47:28,113 ERROR [stderr] (default task-11)   at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:554)
12:47:28,113 ERROR [stderr] (default task-11)   at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:298)
12:47:28,113 ERROR [stderr] (default task-11)   at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:268)
12:47:28,113 ERROR [stderr] (default task-11)   at groovy.lang.GroovyShell.parseClass(GroovyShell.java:688)
12:47:28,113 ERROR [stderr] (default task-11)   at groovy.lang.GroovyShell.parse(GroovyShell.java:700)
12:47:28,113 ERROR [stderr] (default task-11)   at groovy.lang.GroovyShell.evaluate(GroovyShell.java:584)
12:47:28,114 ERROR [stderr] (default task-11)   at groovy.lang.GroovyShell.evaluate(GroovyShell.java:632)
12:47:28,114 ERROR [stderr] (default task-11)   at TestGroovy.getEndpointService(TestGroovy.java:91)
12:47:28,114 ERROR [stderr] (default task-11)   at org.jboss.weld.proxies.TestGroovy$Proxy$_$$_WeldClientProxy.getEndpointService(Unknown Source)

I assume this is an class loading issue and I'm trying to resolve this. Expect a little help here.

Already tried :

  • Provided these dependant jars as modules using jboss-deployment-structure.xml.
  • Provided imports to GroovyShell using ImportCustomizer in CompilerConfiguration
  • Tried to load this using a Startup Singleton EJB also.
  • Tried to load this using a seperate injection
  • Tried to load the JSONObject in seperate static class

Note :

  • Please note that the relevant jars are already in the war file (WEB-INF/lib).
  • This same issue happens for other third party artifacts like com.fasterxml.jackson.databind.node.ObjectNode, com.fasterxml.jackson.databind.ObjectMapper also.
  • But this issue doesn't happen for third party artifacts like org.apache.log4j.Logger. (may be they are already loaded through wildfly)

Update :
I'm one step closer to the answer.As per the forums / artiles classLoader returns all classes on the CLASSPATH when the JVM started. So, I tried adding the related artifacts manually and invoke the script using the classloaders rather than GroovyShell. And it worked! Now need to find a way to load these classes in these artifacts at the JVM start / add them to the class path within wildfly.

URL[] classLoaderUrls = new URL[] {
        new URL("file:///path/to/repo/net/sf/json-lib/json-lib/2.4/json-lib-2.4-jdk15.jar"),
        new URL("file:///path/to/repo/commons-beanutils-1.8.0.jar"),
        new URL("file:///path/to/repo/commons-collections-3.2.1.jar"),
        new URL("file:///path/to/repo/commons-lang-2.5.jar"),
        new URL("file:///path/to/repo/commons-logging-1.1.1.jar"),
        new URL("file:///path/to/repo/groovy-all-2.4.11.jar"),
        new URL("file:///path/to/repo/net/sf/json/main/ezmorph-1.0.6.jar") };

URLClassLoader urlClassLoader = new URLClassLoader(classLoaderUrls);

//GroovyClassLoader groovyClassLoader = new GroovyClassLoader(urlClassLoader);
GroovyClassLoader groovyClassLoader = new GroovyClassLoader();

Script script = InvokerHelper.createScript(groovyClassLoader.parseClass(new
        File(scripts)), binding);
System.out.println("getEndpointService().script : " + script);

Object responseObj = script.run();

Versions :

  • Wildfly version : 10.1.0
  • Wildfly Camle Patch : 4.9.0
  • Groovy version : groovy-all-2.4.7

Solution

  • I found the answer by my self.
    This issue can be resolved when you are using GroovyClassLoader instead of GroovyShell. Actually I dig into the GroovyShell by decompiling it and I saw this is used in there also.

    Note : if the jars in the classpath you don't need to import them manually using URLClassLoader as mentoned in the above post.

    So with the below implementation it works fine now.

    GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
    
    Script script = InvokerHelper.createScript(groovyClassLoader.parseClass(new
            File(scriptPath)), binding);
    logger.info("getEndpointService().script : " + script);
    
    Object responseObj = script.run();
    
    logger.info("getEndpointService().responseObj : " + responseObj);
    


    Further you can use GroovyCodeSource also if you need to play with codeBase like additional attributes.

    GroovyCodeSource groovyCodeSource = new GroovyCodeSource(new File(scriptPath));
    
    GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
    
    Script script = InvokerHelper.createScript(groovyClassLoader.parseClass(groovyCodeSource), binding);
    logger.info("getEndpointService().script : " + script);
    
    Object responseObj = script.run();
    
    logger.info("getEndpointService().responseObj : " + responseObj);