I have a program that writes a string to a .java file, uses javax.tools
to compile that .java file into a .class file, and I then use a modified ClassLoader to attempt to get a Runnable
instance of the class (by turning it into a byte array and using ClassLoader.defineClass
to get an instance). But.., I'm having a problem. When the program attempts to get an instance of the class, it notices that it was not compiled the right way. I get a ClassFormatError
that reads Incompatible magic value 1885430635 in class file <Unknown>
. Here is my (fairly sloppy, at the moment) code:
import java.io.*;
import java.security.SecureClassLoader;
public class EnhancedClassLoader extends SecureClassLoader
{
public Object createObjectFromFile(String fileName) throws
InstantiationException, IOException, IllegalAccessException
{
File file = new File(fileName);
definePackage("compClassPack", null, null, null, null, null, null, file.toURI().toURL());
byte[] classBytes = null;
//ReadFileToByteArray
{
FileInputStream fis = new FileInputStream(file);
int size = (int)file.length();
classBytes = new byte[size];
int offset = 0;
int readed;
while (offset < size && (readed = fis.read(classBytes, offset, size - offset)) != -1)
{
offset += readed;
}
fis.close();
}
Class<?> clazz = defineClass(null, classBytes, 0, classBytes.length);
//The error is thrown here! ^^^
return clazz.newInstance();
}
}
.
import java.io.*;
import javax.tools.*;
public class Main
{
public static void main(String[] args)throws Exception
{
File file = new File("Sample.java");
FileWriter fw = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(fw);
String code =
"package compClassPack;\n" +
"public class Sample implements Runnable\n" +
"{\n" +
" public Sample(){}\n\n" +
" public static void main(String[] args)\n" +
" {\n" +
" new Sample().run();\n" +
" }\n\n" +
" public void run()\n" +
" {\n" +
" System.out.println(\"It worked! :D\");\n" +
" }\n" +
"}";
for(byte ch : code.getBytes())
{
if((char)ch!='\n')
bw.write((char)ch);
else
bw.newLine();
}
bw.flush();
bw.close();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects(file);
JavaCompiler.CompilationTask ct = compiler.getTask(null, fileManager, diagnostics, null, null, compilationUnits);
ct.call();
for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics())
System.out.format("Error on line %d in %d%n",
diagnostic.getLineNumber(),
((FileObject)diagnostic.getSource()).toUri());
fileManager.close();
EnhancedClassLoader loader = new EnhancedClassLoader();
Runnable runs = (Runnable) loader.createObjectFromFile(file.getAbsolutePath());
runs.run();
}
}
Your byte array is not valid bytecode. If it were, it would begin with the magic string (hex) 0xCAFEBABE
. Instead, your first bytes are 1885430635
(decimal), which is hex 0x7061636b
, which read one byte at a time gives the ASCII characters pack
, which is just the beginning of the package declaration.
In other words, you are trying to load a text file as if it were bytecode, and obviously Java can't construct any class from such a byte stream.