1

I'm developing a web application where I can import a java code, compile and then execute it. This class might have some imports from another libraries, which I added to my classpath. Running it as a desktop application, it works, but running under wildfly 9.0.2 it doesn't find my classpath libs and so, I have errors to compile my code.

Do I have to change any setting on wildfly configuration? I tried my code with and without maven.

I'm using this lib to compile my code, and like I said, it worked using the app like desktop: https://github.com/trung/InMemoryJavaCompiler

Errors:

13:44:57,686 ERROR [stderr] (default task-5) /br/com/project/webtest/service/CompileClass.java:2: error: package org.junit does not exist
13:44:57,686 ERROR [stderr] (default task-5) import static org.junit.Assert.*;
13:44:57,686 ERROR [stderr] (default task-5)                        ^
13:44:57,687 ERROR [stderr] (default task-5) /br/com/project/webtest/service/CompileClass.java:3: error: cannot find symbol
13:44:57,687 ERROR [stderr] (default task-5) import br.com.project.webtest.service.SeleniumService;
13:44:57,688 ERROR [stderr] (default task-5)                                               ^
13:44:57,688 ERROR [stderr] (default task-5)   symbol:   class SeleniumService
13:44:57,688 ERROR [stderr] (default task-5)   location: package br.com.project.webtest.service
13:44:57,689 ERROR [stderr] (default task-5) /br/com/project/webtest/service/CompileClass.java:4: error: package org.junit does not exist
13:44:57,689 ERROR [stderr] (default task-5) import org.junit.*;

The error messages continues with all others import libs and then, the classformaterror:

13:44:57,751 WARNING [javax.enterprise.resource.webcontainer.jsf.lifecycle] (default task-5) #{testController.action()}: java.lang.ClassFormatError: Truncated class file: javax.faces.FacesException: #{testController.action()}: java.lang.ClassFormatError: Truncated class file

Edit: I added using eclipse mars, under the Webcontent/web-inf/lib folder, added my libs and right click, add to build path.

Edit 2: Class responsible for creating the code and get the result:

package br.com.test;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@ManagedBean
@SessionScoped
public class TestController {

    public void actionParent() {
        String javaCode = generateJavaCode();
        Class<?> compile = null;
        try {
            compile = InMemoryCompilerTest.compile(Thread.currentThread().getContextClassLoader(),
                    "br.com.project.webtest.service.CompileClass", javaCode);
            System.out.println("Worked: " + compile);
        } catch (Exception e) {
            e.printStackTrace();
        }           
    }

    public void actionClassLoader() {
        String javaCode = generateJavaCode();
        Class<?> compile = null;
        try {
            compile = InMemoryCompilerTest.compile("br.com.project.webtest.service.CompileClass", javaCode);
            System.out.println("Worked: " + compile);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static String readFile(String path, Charset encoding) throws IOException {
        byte[] encoded = Files.readAllBytes(Paths.get(path));
        return new String(encoded, encoding);
    }

    private static String generateJavaCode() {
        String java = "package br.com.project.webtest.service;\r\n" 
                    + "import static org.junit.Assert.*;\r\n"
                    + "public class CompileClass {\r\n" 
                    + " public CompileClass() {\r\n"
                    + "     System.out.println(\"Dynamically compiled\");\r\n"
                    + "     String text = \"Testing JUnit lib\";\r\n"
                    + "     assertEquals(\"Testing JUnit lib\", text);\r\n" 
                    + "     System.out.println(\"completed\");\r\n"
                    + " }\r\n" + "  public static void main(String[] args) {\r\n" 
                    + "     new CompileClass();\r\n"
                    + "     System.out.println(\"finish\");\r\n" 
                    + " }\r\n" 
                    + "}\r\n";

        return java;

    }

    public static void main(String[] args)  {
        TestController c = new TestController();
        c.actionClassLoader();
        c.actionParent();
    }

}

I created a class which extends the lib and changed use parent classloader:

public class InMemoryCompilerTest extends InMemoryJavaCompiler {
    static JavaCompiler javac = ToolProvider.getSystemJavaCompiler();

    public static Class<?> compile(ClassLoader parent, String className, String sourceCodeInText) throws Exception {
        SourceCode sourceCode = new SourceCode(className, sourceCodeInText);
        CompiledCode compiledCode = new CompiledCode(className);
        Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(sourceCode);
        DynamicClassLoader cl = new DynamicClassLoader(parent);
        ExtendedStandardJavaFileManager fileManager = new ExtendedStandardTest(
                javac.getStandardFileManager(null, null, null), compiledCode, cl);
        JavaCompiler.CompilationTask task = javac.getTask(null, fileManager, null, null, null, compilationUnits);
        boolean result = task.call();
        return cl.loadClass(className);
    }
}

public class ExtendedStandardTest extends ExtendedStandardJavaFileManager{

    protected ExtendedStandardTest(JavaFileManager fileManager, CompiledCode compiledCode, DynamicClassLoader cl) {
        super(fileManager, compiledCode, cl);
    }

}

xhtml:

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html">

<h:head></h:head>
<h:body>

    <h:form>

        <h:commandButton action="#{testController.actionClassLoader()}" value="ClassLoader" />
        <h:commandButton action="#{testController.actionParent()}" value="Parent" />
    </h:form>
</h:body>
</html>

I tested passing parent classloader as you sugested but also didnt work.

The first class "TestController" executing from main method it works.

Edit:

I have added the following code where I can set the classpath with my libs:

// set the classpath
        List<String> options = new ArrayList<String>();

        options.add("-classpath");
        StringBuilder sb = new StringBuilder();
        URLClassLoader urlClassLoader = (URLClassLoader) parent;

        for (URL url : urlClassLoader.getURLs()) {
            sb.append(url.getFile()).append(File.pathSeparator);
        }
        options.add(sb.toString());

Executing as a Java application it returns:

[-classpath, /C:/Users/dev/Automacao/workspace/test/build/classes/;/C:/Users/dev/Automacao/workspace/test/WebContent/WEB-INF/lib/InMemoryJavaCompiler-1.2.jar;/C:/Users/dev/Automacao/workspace/test/WebContent/WEB-INF/lib/commons-io-2.5.jar;/C:/Users/dev/Automacao/workspace/test/WebContent/WEB-INF/lib/workspace_libs/client-combined-3.0.0-beta3-nodeps.jar;/C:/Users/dev/Automacao/workspace/test/WebContent/WEB-INF/lib/workspace_libs/commons-codec-1.10.jar;/C:/Users/dev/Automacao/workspace/test/WebContent/WEB-INF/lib/workspace_libs/commons-exec-1.3.jar;/C:/Users/dev/Automacao/workspace/test/WebContent/WEB-INF/lib/workspace_libs/commons-logging-1.2.jar;/C:/Users/dev/Automacao/workspace/test/WebContent/WEB-INF/lib/workspace_libs/gson-2.3.1.jar;/C:/Users/dev/Automacao/workspace/test/WebContent/WEB-INF/lib/workspace_libs/guava-19.0.jar;/C:/Users/dev/Automacao/workspace/test/WebContent/WEB-INF/lib/workspace_libs/hamcrest-core-1.3.jar;/C:/Users/dev/Automacao/workspace/test/WebContent/WEB-INF/lib/workspace_libs/hamcrest-library-1.3.jar;/C:/Users/dev/Automacao/workspace/test/WebContent/WEB-INF/lib/workspace_libs/httpclient-4.5.2.jar;/C:/Users/dev/Automacao/workspace/test/WebContent/WEB-INF/lib/workspace_libs/httpcore-4.4.4.jar;/C:/Users/dev/Automacao/workspace/test/WebContent/WEB-INF/lib/workspace_libs/httpmime-4.5.2.jar;/C:/Users/dev/Automacao/workspace/test/WebContent/WEB-INF/lib/workspace_libs/jna-4.1.0.jar;/C:/Users/dev/Automacao/workspace/test/WebContent/WEB-INF/lib/workspace_libs/jna-platform-4.1.0.jar;/C:/Users/dev/Automacao/workspace/test/WebContent/WEB-INF/lib/workspace_libs/junit-4.12.jar;/C:/Users/dev/Automacao/workspace/test/WebContent/WEB-INF/lib/workspace_libs/netty-3.5.7.Final.jar;/C:/Users/dev/Automacao/workspace/test/WebContent/WEB-INF/lib/workspace_libs/phantomjsdriver-1.3.0.jar;/C:/Users/dev/Automacao/workspace/test/WebContent/WEB-INF/lib/workspace_libs/cglib-nodep-3.2.4.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/annotations-api.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/catalina-ant.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/catalina-ha.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/catalina-storeconfig.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/catalina-tribes.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/catalina.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/ecj-4.3.1.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/el-api.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/jasper-el.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/jasper.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/jsp-api.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/servlet-api.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/tomcat-api.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/tomcat-coyote.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/tomcat-dbcp.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/tomcat-i18n-es.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/tomcat-i18n-fr.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/tomcat-i18n-ja.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/tomcat-jdbc.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/tomcat-jni.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/tomcat-spdy.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/tomcat-util-scan.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/tomcat-util.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/tomcat-websocket.jar;/C:/Program%20Files/Apache%20Software%20Foundation/Apache%20Tomcat%208.0.3/lib/websocket-api.jar;/C:/Users/dev/Automacao/workspace/libraries/JSF%202.2%20(Mojarra%202.2.0)/mojarra-2.2.0-FCS/lib/javax.faces.jar;]

Executing as webapplication:

[-classpath, /C:/Users/dev/Automacao/workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps/test/WEB-INF/classes/;/C:/Users/dev/Automacao/workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps/test/WEB-INF/lib/commons-io-2.5.jar;/C:/Users/dev/Automacao/workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps/test/WEB-INF/lib/InMemoryJavaCompiler-1.2.jar;/C:/Users/dev/Automacao/workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps/test/WEB-INF/lib/javax.faces.jar;]

How could I get the same result?

4
  • you should described how you added them to your classpath Commented Sep 30, 2016 at 17:04
  • @NicolasFilotto, edited. I added like always did. Right click add to build path. Also I tried using maven. The libs are exported to war project, but not found when dynamically try to compile. Commented Sep 30, 2016 at 17:08
  • please show also the code that you use to compile Commented Sep 30, 2016 at 17:12
  • @NicolasFilotto updated. Tried your answer sugestion but didn't worked. Commented Sep 30, 2016 at 19:01

3 Answers 3

1

Get it working.

I have to make that change mentioned by @NicolasFilotto, to use parent classloader when using a webapp. After that, for JavaCompiler works, as I'm using external libraries it's necessary to pass my classpath like mentioned on my last edit. Basically the code changed to:

public static Class<?> compile(ClassLoader parent, String className, String sourceCodeInText) throws Exception {
        SourceCode sourceCode = new SourceCode(className, sourceCodeInText);
        CompiledCode compiledCode = new CompiledCode(className);
        Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(sourceCode);
        DynamicClassLoader cl = new DynamicClassLoader(parent);
        ExtendedStandardJavaFileManager fileManager = new ExtendedStandard(
                javac.getStandardFileManager(null, null, null), compiledCode, cl);

        // set the classpath
        List<String> options = new ArrayList<String>();

        options.add("-classpath");
        StringBuilder sb = new StringBuilder();
        Enumeration<URL> resources = parent.getResources("/");
        while (resources.hasMoreElements()) {
            URL url = resources.nextElement();
            sb.append(url.getFile()).append(File.pathSeparator);
        }

        options.add(sb.toString());

        // execute the compiler
        Boolean call = javac.getTask(null, fileManager, null, options, null, compilationUnits).call();
        if (call) {
            return cl.loadClass(className);
        }
        return null;
    }

Using wildfly I had to change URLClassLoader urlClassLoader = (URLClassLoader) parent; to Enumeration<URL> resources = parent.getResources("/");

Sign up to request clarification or add additional context in comments.

1 Comment

This solution works for me like a charm!! Thanks!! I'm using Apache Tomcat 8. The only thing I had to modify was to remove the exclamation sign "!" at the end of every classpath. I don't know why, but in my classloader all the paths are excluded. I did this: url.replaceAll("!", ""); to the string before append to sb, and it works fine!!
0

@VictorBello I already implements these features in Dynamic Loader. Even you can include Jar at run time in Dynamic Loader

public void DynamicJarCodeTest() throws Exception {
    try {
        StringBuilder sourceCode = new StringBuilder();
        sourceCode.append("package org.dvare.dynamic;\n");
        sourceCode.append("import org.apache.commons.math3.Field;\n");
        sourceCode.append("public class SourceClass {\n");
        sourceCode.append("   public String test() { \n");
        sourceCode.append("   return \"inside test method\";\n");
        sourceCode.append("   }\n");
        sourceCode.append("}");


        DynamicCompiler dynamicCompiler = new DynamicCompiler();
        dynamicCompiler.setSeparateContext(true);
        dynamicCompiler.setUpdateContextClassLoader(false);

        dynamicCompiler.addJar(getClass().getClassLoader().getResource("commons-math3.jar"));
        dynamicCompiler.addSource("org.dvare.dynamic.SourceClass", sourceCode.toString());
        dynamicCompiler.build();

        Class aClass = Class.forName("org.apache.commons.math3.Field", false, dynamicCompiler.getClassLoader());
        Assert.assertNotNull(aClass);

        Class bClass = Class.forName("org.dvare.dynamic.SourceClass", false, dynamicCompiler.getClassLoader());
        Assert.assertNotNull(bClass);


    } catch (DynamicCompilerException e) {
        System.out.println(e.getErrorList());
    } catch (Exception e) {
        e.printStackTrace();
    }

}

4 Comments

Thank you for your answer @Hammad, but I can't get this working using your library. I just changed the "addSource" and "sourceCode" for my example and I get the error: [{kind=ERROR, line=4, message=package org.junit does not exist}, {kind=ERROR, line=9, message=cannot find symbol symbol: method assertEquals(java.lang.String,java.lang.String) location: class br.com.project.webtest.service.CompileClass}]. I Also tried adding the jar which is inside my lib folder but couldn't make work.
see this line package 'org.junit' does not exist mean junit dependency not found include junit dependency in your build path or add jar from your library using add jar
I understood the error, the problem is that the junit lib is included in my buildbath and I got this error.
options inside DynamicCompiler get this path, which is correct file:/C:/Users/dev/Automacao/workspace/test/WebContent/WEB-INF/lib/junit-4.12.jar;
0

Thanks to @victor-bello for the great solution. My solution looks like this:

  1. Need to copy all jar from your maven repository into one folder and distribute that folder with your app: lib folder
  2. From them generate classpath for javac (stupid way): classpath
  3. Copy InMemoryJavaCompiler code source from github and override:

    public class InMemoryCompilerTest extends InMemoryJavaCompiler {

    static JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
    
    public static Class<?> compile(ClassLoader parent, String className, String sourceCodeInText, String classpath ) throws Exception {
    
        SourceCode sourceCode = new SourceCode(className, sourceCodeInText);
        CompiledCode compiledCode = new CompiledCode(className);
        Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(sourceCode);
        DynamicClassLoader cl = new DynamicClassLoader(parent);
        ExtendedStandardJavaFileManager fileManager =
                new ExtendedStandard(javac.getStandardFileManager(null, null, null), compiledCode, cl);
    
        // set the classpath
        List<String> options = new ArrayList<String>();
    
        options.add("-classpath");
        options.add(classpath);
    
        // execute the compiler
        Boolean call = javac.getTask(null, fileManager, null, options, null, compilationUnits).call();
        if (call) {
            return cl.loadClass(className);
        }
        return null;
    }
    

    }

  4. Call compile method:

    Class testClass = inMemoryCompilerTest.compile(Thread.currentThread().getContextClassLoader(),"com.namespace.here.ClassName", someCode, stringBuilder.toString());

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.