13

Here I have found some good example:

// Prepare source somehow.
String source = "package test; public class Test { static { System.out.println(\"hello\"); } public Test() { System.out.println(\"world\"); } }";

// Save source in .java file.
File root = new File("/java"); // On Windows running on C:\, this is C:\java.
File sourceFile = new File(root, "test/Test.java");
sourceFile.getParentFile().mkdirs();
new FileWriter(sourceFile).append(source).close();

// Compile source file.
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler.run(null, null, null, sourceFile.getPath());

// Load and instantiate compiled class.
URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { root.toURI().toURL() });
Class<?> cls = Class.forName("test.Test", true, classLoader); // Should print "hello".
Object instance = cls.newInstance(); // Should print "world".
System.out.println(instance); // Should print "test.Test@hashcode".

Question: Is it possible to achieve exactly the same thing without writing to a file?

@Edit: To be more exact: I know how to compile from string (overloading JavaFileObject). But after doing this, I have no idea how to load the class. I probably missed the output-write part, but this also a thing I would like not to do.

@Edit2 For anyone interested, I created this small project to implement discussed feature: https://github.com/Krever/JIMCy

Krever
  • 1,371
  • 1
  • 13
  • 32
  • OK, I deleted my comment since actually it did write to a file... However note that I detected the package name automatically, along with the class name ;) – fge May 14 '14 at 19:42
  • @Joe no, this is not what the OP wants; he knows how to compile from a String, he wants to write the result of the compile process in memory and not in a file – fge May 14 '14 at 19:47
  • @fge quite right, I apologise for haste, I'll remove all other traces :) – Joe May 14 '14 at 19:49
  • Have a look at the first link I posted; I do also load the compiled result with a classloader. – fge May 14 '14 at 20:05

2 Answers2

4

OK, so here is an example of using JavaCompiler to compile from a String input. this file is the core of it.

In this file, I load the class which is compiled. You will notice that I also detect the package and classname using regexes.


As to the output, if you want to do that in memory, it appears that you can do so if you implement your own JavaFileManager; however I have never tried that!

Note that you can debug what happens quite easily by extending ForwardingJavaFileManager

fge
  • 119,121
  • 33
  • 254
  • 329
  • What about the input files? From a brief reading of the [`FileObject`](http://docs.oracle.com/javase/7/docs/api/javax/tools/FileObject.html) interface that `JavaCompiler` gets from its file manager it appears that one should be able to "plug in" his own in-memory streams into compiler's input. – Sergey Kalinichenko May 14 '14 at 19:46
  • @dasblinkenlight yes, this is doable and this is what the OP already does; the problem here is the output, not the inputs. In fact I have coded a compilation process which can read sources from a `String` as well – fge May 14 '14 at 19:48
  • It does not look like the OP's code avoids writing the input files: see the `new FileWriter(sourceFile).append(source).close()` line. – Sergey Kalinichenko May 14 '14 at 19:59
  • @dasblinkenlight yeah, brain fart from my part. Therefore I pasted the link to the code I wrote which does exactly that. – fge May 14 '14 at 20:00
  • Ah, `FromStringFileObject`!!! That's beautiful! – Sergey Kalinichenko May 14 '14 at 20:02
  • As long, as it is not possible to output to the memory, what you give is probably the best what I can get. Thanks. – Krever May 14 '14 at 20:12
  • @Krever well, as I said, if you have a mount point to an in-memory filesystem then it will be in memory :p But that remains quite a hack, of course. – fge May 14 '14 at 20:13
  • @Krever looking at the API again, though, I may be wrong... It does look like there is a possibility to place the result in memory; however, I don't quite know how you would retrieve it again and load it (ie, how to initiate the class loader) – fge May 14 '14 at 20:17
  • @dasblinkenlight it appears that I was wrong and that you may be able to output the compilation result in memory after all – fge May 14 '14 at 20:24
  • @fge Nice, thanks for help! There is still a problem of loading such classes from memory, but they say "everything is posible"... :) – Krever May 14 '14 at 20:40
  • @Krever indeed, the second tricky part (after `JavaFileManager`) will be to create an appropriate classloader... – fge May 14 '14 at 20:45
  • @fge [This](http://stackoverflow.com/questions/19895550/getjavafileforoutput-method-of-custom-javafilemanager-not-called-by-compile) shows it is possible... but probably not so simple :) – Krever May 14 '14 at 20:59
0

Not exactly what you are asking for, but Groovy's library for Java makes it easy to compile and evaluate expressions from String. The syntax is not identical but very similar to Java, so if you can change the strings to be compiled so they are in Groovy instead of Java, the task will be very easy. See Embedding Groovy for code samples.

Michał Kosmulski
  • 9,855
  • 1
  • 32
  • 51