Компилировать Java файлы программно
Я знаю, что это было задано и много ответа, но у меня все еще нет хорошего решения, и я до сих пор не понимаю некоторых частей. Поэтому у меня есть требование скомпилировать программные файлы *.java.
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
- это то, что я использую и (как и ожидалось) компилятор null
.
Теперь я знаю, что я должен использовать JDK, а не JRE как "runtime", но вот что-то я не понимаю: недостаточно ли просто поместить tools.jar
в путь к классам приложения и затем получить доступ к API компилятора Java? Если это так, существует (я думаю, есть) разница между автономным Java-приложением и веб-приложениями.
На самом деле, я пытаюсь вызвать JavaCompiler из веб-приложения PlayFramework, поэтому я понял, могут ли эти решения (включая tools.jar
) работать только для автономных приложений?
Я также попытался создать собственный ClassLoader и вызвать вышеупомянутый метод с отражением, но все, что я получаю, это null
для объекта compiler
:
ClassLoader classloader = app.classloader();
File file = new File("lib/tools.jar");
URL url = file.toURI().toURL();
URL[] urls = new URL[]{url};
ClassLoader newCL = new URLClassLoader(urls, classloader) {
};
Class<?> loadClass = newCL.loadClass("javax.tools.ToolProvider");
Method method = loadClass.getMethod("getSystemJavaCompiler", null);
Object object = method.invoke(null);
System.out.println("Object: " + object); // NULL
Объяснение для приведенного выше кода:
- Я просто не использовал try/catch для простоты.
- app.classloader() - это метод воспроизведения, который возвращает ClassLoader приложения
-
tools.jar
включен в мою папку lib в проекте Play (это означает, что он находится в пути к классу проекта - в соответствии с документацией Play)
Я уверен, что перед тем, как Play сможет загрузить класс компилятора Java, я просто не знаю, что мне не хватает.
Мне известны опции, такие как Runtime.exec("javac myFile.java")
и компилятор Eclipse JDT, но это не то, что я ищу.
О, и что-то вроде System.setProperty("java.home", "PATH_TO_YOUR_JDK");
, а затем ToolProvider.getSystemJavaCompiler();
работает, но я считаю это решение таким уродливым.
С наилучшими пожеланиями
EDIT: (чтобы предоставить дополнительную информацию и отобразить последний статус)
Это базовое представление структуры web-app:
myApp
|-conf\...
|-lib\MyJar.jar
|-lib\tools.jar
|-logs\...
|-...
В MyJar.jar теперь есть файл META-INF/MANIFEST.MF
со следующим содержимым:
Manifest-Version: 1.0
Sealed: true
Main-Class: here.comes.my.main.class
Class-Path: tools.jar
Без запуска приложения Play я пытаюсь (в папке lib
): java -jar MyJar.jar
- мой простой метод main
пытается вызвать компилятор (ToolProvider.getSystemJavaCompiler();
) и возвращает null
. Таким образом, это заставляет меня поверить, что проблема не имеет ничего общего с Play - я даже не могу получить компилятор при нормальной работе Jar!
Ответы
Ответ 1
Нет никакой разницы между веб-приложениями и отдельными приложениями.
Вы не должны упаковывать tools.jar в свой путь к классу webapp. Классный загрузчик на Java обычно работает только снизу вверх. Таким образом, ToolProvider, который является частью jdk, не увидит ваши tools.jar в пути к классу webapp (он не может смотреть вниз в путь класса webapp).
Решение: используйте JDK и убедитесь, что вы указали на него или поместите tools.jar в каталог Java ext. Вы можете переопределить ext dir, установив свойство -Djava.ext.dir в любой каталог, который вам нравится.
Ответ 2
В documentation говорится, что To run the Play framework, you need JDK 6 or later.
Как вам удалось запустить его с помощью jre?
В любом случае, если getSystemJavaCompiler
возвращает null, это означает, что у вас нет tools.jar
в вашем пути к классам или он поврежден. Если это Java/Java, убедитесь, что в нем есть класс com.sun.tools.javac.api.JavacTool
.
И если вы хотите использовать пользовательский загрузчик классов, это класс JavacTool, который вам нужно загрузить, а не ToolProvider. Посмотрите, как это делается в java 6:
URL[] urls = {file.toURI().toURL()};
ClassLoader cl = URLClassLoader.newInstance(urls);
cl.setPackageAssertionStatus("com.sun.tools.javac", true);
return Class.forName(defaultJavaCompilerName, false, cl);
где defaultJavaCompilerName = "com.sun.tools.javac.api.JavacTool"
Подкласс к JavaCompiler
и получить новый экземпляр - у вас будет ваш компилятор.