I have a java source file containing two classes:
package com.example;
public class Test {
public void sayHello() {
String hello = new HelloProducer().getHello();
System.out.println(hello);
}
public static void main(String[] args) {
new Test().sayHello();
}
}
class HelloProducer {
public String getHello() {
return "hello";
}
}
I want to compile this java source file programmatically using Java SDK, here is what I've tried.
package com.example;
import java.io.IOException;
import javax.tools.*;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
public class Compiler {
public static void main(String[] args) throws IOException {
byte[] classBytes = compile();
// how to write classBytes to two class files: HelloProducer.class and Test.class?
}
public static byte[] compile() throws IOException {
String javaSource = "package com.example;\n" +
"\n" +
"public class Test {\n" +
" public void sayHello() {\n" +
" String hello = new HelloProducer().getHello();\n" +
" System.out.println(hello);\n" +
" }\n" +
" public static void main(String[] args) {\n" +
" new Test().sayHello();\n" +
" }\n" +
"}\n" +
"class HelloProducer {\n" +
" public String getHello() {\n" +
" return \"hello\";\n" +
" }\n" +
"}\n";
GeneratedClassFile gcf = new GeneratedClassFile();
DiagnosticCollector<JavaFileObject> dc = new DiagnosticCollector<>();
GeneratedJavaSourceFile gjsf = new GeneratedJavaSourceFile("Test.java", javaSource);
JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
GeneratingJavaFileManager fileManager = new GeneratingJavaFileManager(
jc.getStandardFileManager(dc, null, null),
gcf
);
JavaCompiler.CompilationTask task = jc.getTask(null, fileManager, dc,
new ArrayList<>(), null,
Collections.singletonList(gjsf));
boolean success = task.call();
fileManager.close();
return gcf.getClassAsBytes();
}
}
class GeneratingJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> {
private final GeneratedClassFile gcf;
public GeneratingJavaFileManager(
StandardJavaFileManager sjfm,
GeneratedClassFile gcf) {
super(sjfm);
this.gcf = gcf;
}
public JavaFileObject getJavaFileForOutput(
Location location, String className,
JavaFileObject.Kind kind, FileObject sibling) {
return gcf;
}
}
class GeneratedClassFile extends SimpleJavaFileObject {
private final ByteArrayOutputStream outputStream =new ByteArrayOutputStream();
public GeneratedClassFile() {
super(URI.create("generated.class"), Kind.CLASS);
}
public OutputStream openOutputStream() {
return outputStream;
}
public byte[] getClassAsBytes() {
return outputStream.toByteArray();
}
}
class GeneratedJavaSourceFile extends SimpleJavaFileObject {
private CharSequence javaSource;
public GeneratedJavaSourceFile(String fileName,
CharSequence javaSource) {
super(URI.create(fileName), Kind.SOURCE);
this.javaSource = javaSource;
}
public CharSequence getCharContent(boolean ignoreEncodeErrors) {
return javaSource;
}
}
It worked. I successfully compiled this java source code and got a byte array classBytes
. But here comes the problem: When you compile Test.java
using javac
, you will get two class files: Test.class
and HelloProducer.class
, but how now I only have a byte array classBytes
, how can I write the byte array into two files(Test.class
and HelloProducer.class
) correctly just like javac
?