,
:
Write a Java program which should be able to
1. Take an input as Java program file
2. Compile it and produce errors if can't compile
3. Run the class file generated in previous step
4. Produce the run time exceptions generated by the invocation if any
Assumptions:
1. Class would contain a "main" method else it can't be run using "java" program
, . , , , , , .
:
1. Compile the Java code using the compiler API (using ToolProvider.getSystemJavaCompiler())
2. Use DiagnosticCollector to collect for any compilation errors that might have ocurred.
3. If compilation is successful then load the generated class in byte array.
4. Use ClassLoader.defineClass() to load class file from byte array into JVM runtime.
5. Once class is loaded, use reflection to find the main method, if not present throw main not found related exceptions.
6. Run main method, and report back any runtime exceptions produced.
Note: If needed Standard input and output streams can be redirected for
the new program and original objects can be saved as originals for
main program. I haven't done it, but it trivial to do.
:
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class JreMain {
private static final String PATH_TO_JAVA_FILE = "src/main/java/MyProgram.java";
public static void main(String[] args) {
JreMain main = new JreMain();
System.out.println("Running a java program");
String filePath = PATH_TO_JAVA_FILE;
File javaFile = new File(filePath);
List<String> errorList = main.compile(Arrays.asList(javaFile));
if(errorList.size() != 0) {
System.out.println("file could not be compiled, check below for errors");
for(String error : errorList) {
System.err.println("Error : " + error);
}
} else {
main.runJavaClass(filePath, new String[] {});
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
private void runJavaClass(String filePath, String[] mainArguments) {
System.out.println("Running " + filePath);
ClassLoader classLoader = getClass().getClassLoader();
Class klass = null;
String fileNameWithoutExtension = filePath.substring(0, filePath.length() - ".java".length());
String className = getClassName(fileNameWithoutExtension);
try {
Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, Integer.TYPE, Integer.TYPE);
defineClassMethod.setAccessible(true);
byte[] classBytes = getClassBytes(fileNameWithoutExtension + ".class");
klass = (Class)defineClassMethod.invoke(classLoader, className, classBytes, 0, classBytes.length);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
if(klass != null) {
try {
Method mainMethod = klass.getMethod("main", String[].class);
Class returnType = mainMethod.getReturnType();
if( !Modifier.isStatic(mainMethod.getModifiers()) || !Modifier.isPublic(mainMethod.getModifiers()) || !(returnType.equals(Void.TYPE) || returnType.equals(Void.class))) {
throw new RuntimeException("Main method signature incorrect, expected : \"public static void main(String[] args)\",");
}
mainMethod.invoke(null, new Object[]{mainArguments});
} catch (NoSuchMethodException e) {
throw new RuntimeException("Class " + klass.getCanonicalName() + " does not declare main method");
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
System.err.println("Exception in main :");
throw new RuntimeException(e.getCause());
}
}
}
private String getClassName(String fileNameWithoutExtension) {
String className = null;
int lastIndex = -1;
if( ( lastIndex = fileNameWithoutExtension.lastIndexOf(File.separator)) != -1) {
className = fileNameWithoutExtension.substring(lastIndex + 1);
} if( ( lastIndex = fileNameWithoutExtension.lastIndexOf("\\")) != -1) {
className = fileNameWithoutExtension.substring(lastIndex + 1);
} else if( ( lastIndex = fileNameWithoutExtension.lastIndexOf("/")) != -1) {
className = fileNameWithoutExtension.substring(lastIndex + 1);
}
return className;
}
private byte[] getClassBytes(String classFilePath) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
File classFile = new File(classFilePath);
if(!classFile.exists()) {
throw new RuntimeException("Class file does not exist : " + classFile.getAbsolutePath());
}
byte[] buffer = new byte[2048];
int readLen = -1;
FileInputStream fis = null;
try {
fis = new FileInputStream(classFile);
while( (readLen = fis.read(buffer)) != -1) {
baos.write(buffer, 0, readLen);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return baos.toByteArray();
}
@SuppressWarnings("restriction")
public List<String> compile (List<File> javaFileList) {
System.out.println("Started compilation");
List<String> errorList = new ArrayList<String>();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(
diagnostics, null, null);
Iterable<? extends JavaFileObject> compilationUnits = fileManager
.getJavaFileObjectsFromFiles(javaFileList);
compiler.getTask(null, fileManager, diagnostics, null, null, compilationUnits)
.call();
for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics
.getDiagnostics()) {
String diagnosticMessage = String.format("Error on line %d in %s%n",
diagnostic.getLineNumber(), diagnostic.getSource().toUri() + " : \n\t" + diagnostic.getMessage(null));
errorList.add(diagnosticMessage);
}
try {
fileManager.close();
} catch (IOException e) {
e.printStackTrace();
}
return errorList;
}
}
@Miserable Variable, javac, .
** * **
:
List<String> compilerOptionsList = Arrays.asList("-classpath", "jar/slf4j-api-1.7.10.jar", "-verbose");
JavaCompiler.CompilationTask compilationTask = compiler.getTask(null,
fileManager, diagnostics, compilerOptionsList, null,
compilationUnits);
Java runtime:
They will have to be passed to our JareMain program itself.
java -classpath "jar/slf4j-api-1.7.10.jar;" -verbose JreMain
:
main.runJavaClass(filePath, new String[] {});