Динамически компилировать веб-проект класса java

#java #jakarta-ee #classpath #wildfly-9

#java #джакарта-ee #путь к классу #wildfly-9

Вопрос:

Я разрабатываю веб-приложение, в котором я могу импортировать код Java, скомпилировать и затем выполнить его. Этот класс может иметь некоторый импорт из других библиотек, которые я добавил в свой classpath. Запустив его как настольное приложение, он работает, но, работая под управлением wildfly 9.0.2, он не находит мои библиотеки classpath, и поэтому у меня возникают ошибки при компиляции моего кода.

Должен ли я изменять какие-либо настройки в конфигурации wildfly? Я пробовал свой код с maven и без него.

Я использую эту библиотеку для компиляции своего кода, и, как я уже сказал, она работала с использованием приложения, подобного desktop: https://github.com/trung/InMemoryJavaCompiler

Ошибки:

 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.*;
  

Сообщения об ошибках продолжаются со всеми остальными библиотеками импорта
, а затем 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
  

Редактировать:
Я добавил, используя eclipse mars, в папку Webcontent / web-inf / lib, добавил свои библиотеки и щелкните правой кнопкой мыши, добавить в путь сборки.

Редактирование 2: класс, ответственный за создание кода и получение результата:

 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;rn" 
                      "import static org.junit.Assert.*;rn"
                      "public class CompileClass {rn" 
                      " public CompileClass() {rn"
                      "     System.out.println("Dynamically compiled");rn"
                      "     String text = "Testing JUnit lib";rn"
                      "     assertEquals("Testing JUnit lib", text);rn" 
                      "     System.out.println("completed");rn"
                      " }rn"   "  public static void main(String[] args) {rn" 
                      "     new CompileClass();rn"
                      "     System.out.println("finish");rn" 
                      " }rn" 
                      "}rn";

        return java;

    }

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

}
  

Я создал класс, который расширяет библиотеку, и изменил использование родительского загрузчика классов:

 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>
  

Я протестировал передачу родительского загрузчика классов, как вы предлагали, но также не сработал.

Первый класс «TestController», выполняемый из основного метода, работает.

Редактировать:

Я добавил следующий код, в котором я могу задать путь к классу с помощью своих библиотек:

 // 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());
  

Выполняемый как Java-приложение, он возвращает:

 [-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 Files/Apache Software Foundation/Apache Tomcat%208.0.3/lib/annotations-api.jar;/C:/Program Files/Apache Software Foundation/Apache Tomcat%208.0.3/lib/catalina-ant.jar;/C:/Program Files/Apache Software Foundation/Apache Tomcat%208.0.3/lib/catalina-ha.jar;/C:/Program Files/Apache Software Foundation/Apache Tomcat%208.0.3/lib/catalina-storeconfig.jar;/C:/Program Files/Apache Software Foundation/Apache Tomcat%208.0.3/lib/catalina-tribes.jar;/C:/Program Files/Apache Software Foundation/Apache Tomcat%208.0.3/lib/catalina.jar;/C:/Program Files/Apache Software Foundation/Apache Tomcat%208.0.3/lib/ecj-4.3.1.jar;/C:/Program Files/Apache Software Foundation/Apache Tomcat%208.0.3/lib/el-api.jar;/C:/Program Files/Apache Software Foundation/Apache Tomcat%208.0.3/lib/jasper-el.jar;/C:/Program Files/Apache Software Foundation/Apache Tomcat%208.0.3/lib/jasper.jar;/C:/Program Files/Apache Software Foundation/Apache Tomcat%208.0.3/lib/jsp-api.jar;/C:/Program Files/Apache Software Foundation/Apache Tomcat%208.0.3/lib/servlet-api.jar;/C:/Program Files/Apache Software Foundation/Apache Tomcat%208.0.3/lib/tomcat-api.jar;/C:/Program Files/Apache Software Foundation/Apache Tomcat%208.0.3/lib/tomcat-coyote.jar;/C:/Program Files/Apache Software Foundation/Apache Tomcat%208.0.3/lib/tomcat-dbcp.jar;/C:/Program Files/Apache Software Foundation/Apache Tomcat%208.0.3/lib/tomcat-i18n-es.jar;/C:/Program Files/Apache Software Foundation/Apache Tomcat%208.0.3/lib/tomcat-i18n-fr.jar;/C:/Program Files/Apache Software Foundation/Apache Tomcat%208.0.3/lib/tomcat-i18n-ja.jar;/C:/Program Files/Apache Software Foundation/Apache Tomcat%208.0.3/lib/tomcat-jdbc.jar;/C:/Program Files/Apache Software Foundation/Apache Tomcat%208.0.3/lib/tomcat-jni.jar;/C:/Program Files/Apache Software Foundation/Apache Tomcat%208.0.3/lib/tomcat-spdy.jar;/C:/Program Files/Apache Software Foundation/Apache Tomcat%208.0.3/lib/tomcat-util-scan.jar;/C:/Program Files/Apache Software Foundation/Apache Tomcat%208.0.3/lib/tomcat-util.jar;/C:/Program Files/Apache Software Foundation/Apache Tomcat%208.0.3/lib/tomcat-websocket.jar;/C:/Program Files/Apache Software Foundation/Apache Tomcat%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;]
  

Выполняется как веб-приложение:

 [-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;]
  

Как я мог получить тот же результат?

Комментарии:

1. вы должны описать, как вы добавили их в свой путь к классу

2. @NicolasFilotto, отредактировано. Я добавил, как всегда. Щелкните правой кнопкой мыши добавить, чтобы построить путь. Также я попытался использовать maven. Библиотеки экспортируются в war project, но не обнаруживаются при динамической попытке компиляции.

3. пожалуйста, покажите также код, который вы используете для компиляции

4. обновлено @NicolasFilotto. Попробовал ваше предложение ответа, но не сработало.

Ответ №1:

Заставьте его работать.

Я должен внести это изменение, упомянутое @NicolasFilotto, чтобы использовать родительский загрузчик классов при использовании веб-приложения. После этого для работы JavaCompiler, поскольку я использую внешние библиотеки, необходимо передать мой путь к классу, как указано в моем последнем редактировании. В основном код изменился на:

 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;
    }
  

Используя wildfly, мне пришлось перейти URLClassLoader urlClassLoader = (URLClassLoader) parent; на Enumeration<URL> resources = parent.getResources("/");

Комментарии:

1. Это решение работает для меня как шарм!! Спасибо!! Я использую Apache Tomcat 8. Единственное, что мне пришлось изменить, это убрать восклицательный знак «!» в конце каждого пути к классу. Я не знаю почему, но в моем загрузчике классов все пути исключены. Я сделал это: url.replaceAll("!", ""); к строке перед добавлением в sb, и она отлично работает!!

Ответ №2:

@VictorBello Я уже реализовал эти функции в динамическом загрузчике. Даже вы можете включить Jar во время выполнения в динамический загрузчик

 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();
    }

}
  

Комментарии:

1. Спасибо за ваш ответ @Hammad, но я не могу заставить это работать, используя вашу библиотеку. Я только что изменил «addSource» и «Исходный код» для своего примера, и я получаю сообщение об ошибке: [{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}] . Я также попытался добавить jar, который находится внутри моей папки lib, но не смог заставить работать.

2. смотрите эту строку пакет ‘org.junit’ не существует означает, что зависимость junit не найдена включите зависимость junit в свой путь сборки или добавьте jar из своей библиотеки с помощью add jar

3. Я понял ошибку, проблема в том, что библиотека junit включена в мою buildbath, и я получил эту ошибку.

4. параметры внутри DynamicCompiler получают этот путь, который является правильным file:/C:/Users/dev/Automacao/workspace/test/WebContent/WEB-INF/lib/junit-4.12.jar;

Ответ №3:

Спасибо @victor-bello за отличное решение. Мое решение выглядит так:

  1. Необходимо скопировать все jar из вашего репозитория maven в одну папку и распространить эту папку вместе с вашим приложением: папка библиотеки
  2. Из них генерируется путь к классу для javac (глупый способ): путь к классу
  3. Скопируйте исходный код InMemoryJavaCompiler из github и переопределите:

    открытый класс InMemoryCompilerTest расширяет 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. Вызов метода компиляции:

    Класс TestClass = inMemoryCompilerTest.compile(Thread.currentThread().getContextClassLoader(),»com.namespace.here.className», someCode, StringBuilder.toString());