Существует несколько вариантов этого, каждый из которых связан с различными усилиями и возможностями. Вы можете написать собственный интерпретатор или использовать существующий iterpreter, или использовать другой язык для разметки и использовать один из встроенных движков сценариев.
Однако, возвращаясь к исходному намерению, чтобы пользователь написал код Java, есть также опция для этого: если установлен JDK, вы можете использовать Java Tools JDK. (Обратите внимание, что это не будет работать с JRE).
В частности, вы можете использовать класс JavaCompiler для компиляции ваших классов Java на лету.
Я явно делаю НЕ хочу сказать, что это наиболее подходящее решение для вашей проблемы. Я просто хочу сказать, что это опция и что вы действительно можете скомпилировать Java-код на лету.
Когда я хотел просто ссылку на пример (или скопировать & пасту один, с указанием авторства), я заметил, что примеры, которые я нашел в Интернете либо plainly don't work или used temporary files, или только сосредоточены на некоторых конкретных аспектах JavaCompiler
и его использование (представьте себе список ответов Blogs и StackOverflow здесь).
я не найти MCVE, который показал, как компилировать исходный код одного класса (или даже меньше: несколько классов), и загрузить полученный класс, полностью в памяти. Поэтому я создал такой класс утилиты: класс InMemoryCompiler
может получить список имен классов и соответствующий исходный код и возвращает карту из имен классов в Class<?>
объектов. Это можно будьте этим простыми, и это должно быть таким простым. Может быть, кто-то найдет это полезным.
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;
public class JavaCompilerExample
{
public static void main(String[] args)
{
String classNameA = "ExampleClassA";
String sourceA =
"public class " + classNameA + " {" + "\n" +
" public static void main(String args[]) {" + "\n" +
" System.out.println(\"Hello, compiler!\");" + "\n" +
" ExampleClassB b = new ExampleClassB();" + "\n" +
" b.someMethod();" + "\n" +
" }" + "\n" +
"}" + "\n";
String classNameB = "ExampleClassB";
String sourceB =
"public class " + classNameB + " {" + "\n" +
" public void someMethod() {" + "\n" +
" System.out.println(\"Some method was called\");" + "\n" +
" }" + "\n" +
"}" + "\n";
InMemoryCompiler c = new InMemoryCompiler();
Map<String, Class<?>> classes = c.compile(
Arrays.asList(classNameA, classNameB),
Arrays.asList(sourceA, sourceB));
try
{
Class<?> exampleClass = classes.get(classNameA);
Method m = exampleClass.getDeclaredMethod("main",
new Class[] { String[].class });
m.invoke(null, (Object) null);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
class InMemoryCompiler
{
private final Map<String, ByteArrayJavaFileObject> classFileObjects;
private final JavaCompiler javaCompiler;
private final JavaFileManager fileManager;
private final ClassLoader classLoader;
InMemoryCompiler()
{
classFileObjects =
new LinkedHashMap<String, ByteArrayJavaFileObject>();
javaCompiler = ToolProvider.getSystemJavaCompiler();
JavaFileManager standardFileManager = javaCompiler
.getStandardFileManager(null, Locale.ENGLISH, null);
fileManager = new CompiledFileManager(standardFileManager);
classLoader = new CompiledClassLoader();
}
public Map<String, Class<?>> compile(
List<String> classNames, List<String> sources)
{
List<JavaFileObject> javaFileObjects = new ArrayList<JavaFileObject>();
for (int i = 0; i < classNames.size(); i++)
{
String className = classNames.get(i);
String source = sources.get(i);
javaFileObjects.add(new StringJavaFileObject(className, source));
}
CompilationTask compilationTask = javaCompiler.getTask(
new PrintWriter(System.out), fileManager, null, null, null,
javaFileObjects);
compilationTask.call();
Map<String, Class<?>> classes = new LinkedHashMap<String, Class<?>>();
for (int i = 0; i < classNames.size(); i++)
{
String className = classNames.get(i);
try
{
Class<?> c = classLoader.loadClass(className);
classes.put(className, c);
}
catch (ClassNotFoundException e)
{
System.out.println(e);
}
}
return classes;
}
private class CompiledFileManager
extends ForwardingJavaFileManager<JavaFileManager>
{
CompiledFileManager(JavaFileManager fileManager)
{
super(fileManager);
}
@Override
public JavaFileObject getJavaFileForOutput(Location location,
String className, javax.tools.JavaFileObject.Kind kind,
FileObject sibling) throws IOException
{
ByteArrayJavaFileObject javaFileObject =
new ByteArrayJavaFileObject(className);
classFileObjects.put(className, javaFileObject);
return javaFileObject;
}
}
private class CompiledClassLoader extends ClassLoader
{
@Override
public Class<?> findClass(String name)
{
byte[] b = classFileObjects.get(name).getBytes();
return defineClass(name, b, 0, b.length);
}
}
private class ByteArrayJavaFileObject extends SimpleJavaFileObject
{
private final ByteArrayOutputStream stream;
public ByteArrayJavaFileObject(String name)
{
super(URI.create("bytes:///" + name), Kind.CLASS);
stream = new ByteArrayOutputStream();
}
@Override
public OutputStream openOutputStream() throws IOException
{
return stream;
}
public byte[] getBytes()
{
return stream.toByteArray();
}
}
private class StringJavaFileObject extends SimpleJavaFileObject
{
private final String code;
StringJavaFileObject(String name, String code)
{
super(URI.create("string:///" + name.replace('.', '/') +
Kind.SOURCE.extension), Kind.SOURCE);
this.code = code;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors)
{
return code;
}
}
}
(Примечание: Существует едва ли любой обработки ошибок в этом классе Она может быть расширена с помощью DiagnosticListener
и такие, но это не главное намерение здесь.)
Вы должны рассмотреть вопросы безопасности позволяя пользователю создавать и запускать собственный код в вашей системе. – Grice
Ну, программа должна запускаться в системе пользователя, это не апплет. – lost
Похоже, вы хотите, чтобы API облегчил визуализацию алорифм с графиками. В то время как это так, вы не имеете никакого отношения к компиляции кода клиента, используя ваш API. Просто подумайте, что вы создаете то есть. Java Swing, заданный для графиков. –