views:

1415

answers:

6

Hi all,

I am in the process of writing a rule engine that performs simple assignments as determined by conditional constructs. It is a prerequisite of the project that the rules be in XML format. I have modeled my XML schema to resemble simple code blocks. I wish to parse the XML and to then transform it into Java code. I then wish to compile (and run) this code at runtime. Doing so would mean that my rule engine no longer acts as an interpreter but executes native Java Byte Code.

I have figured out the parsing stage and more or less the Java code generation phase. I would now like to figure out the last phase - the compile at runtime phase.

Following this thread: http://stackoverflow.com/questions/688098/compile-to-java-bytecode-without-using-java I have become aware of the following possible solutions:

I would love a comparison of these as well as other suggestions for solving the Java compile at runtime phase.

Thanks a lot in advance!

+3  A: 

Use the Java 6 Compiler API.

cletus
A: 

You could transform it into Clojure code, and the Clojure compiler will turn it into bytecode for you.

kwatford
Could you elaborate please?
Yaneeve
@Yaneeve, Clojure (http://clojure.org/) is a lisp dialect written for the JVM. If you were to implement the semantics of your tags as lisp functions/macros, it should be trivial to convert from your XML structures to the corresponding lisp structures, which can then be compiled into a class if you like. If XML wasn't a requirement, I'd say just use lisp data begin with. For a java+xml-centric discussion of why, see: http://www.defmacro.org/ramblings/lisp.html
kwatford
+1  A: 

Save yourself the hassle and use BeanShell as alluded to here http://stackoverflow.com/questions/1057796/executing-java-code-given-in-a-text-file.

What is BeanShell?

BeanShell is a small, free, embeddable Java source interpreter with object scripting language features, written in Java. BeanShell dynamically executes standard Java syntax and extends it with common scripting conveniences such as loose types, commands, and method closures like those in Perl and JavaScript.

You can use BeanShell interactively for Java experimentation and debugging as well as to extend your applications in new ways. Scripting Java lends itself to a wide variety of applications including rapid prototyping, user scripting extension, rules engines, configuration, testing, dynamic deployment, embedded systems, and even Java education.

BeanShell is small and embeddable, so you can call BeanShell from your Java applications to execute Java code dynamically at run-time or to provide extensibility in your applications. Alternatively, you can use standalone BeanShell scripts to manipulate Java applications; working with Java objects and APIs dynamically. Since BeanShell is written in Java and runs in the same VM as your application, you can freely pass references to "live" objects into scripts and return them as results.

In short, BeanShell is dynamically interpreted Java, plus a scripting language and flexible environment all rolled into one clean package.

Nick Holt
Correct me if I'm wrong, though, BeanShell is an interpreter and therefore won't create Java Byte Code as a result of its execution...
Yaneeve
Yes, I think BeanShell is interpreted. The reason I mentioned it is I assume you want to use Java to express more complicated constructs in your rule engine, in which case does it matter is the Java code is compiled or interpreted?
Nick Holt
It does matter since cpu and memory are of an issue in the environment I must deploy to...
Yaneeve
I'm tempted to say be wary of premature optimization, BeanShell may be performant enough and will save you a lot of time and effort that can be redirected at user visible features. Then again, without knowing the specifics of your situation I can't say for sure.
Nick Holt
My case is actually a refactoring of 2 similar components into one shared service. This service is to be invoked millions of times in a given hour for "simple" assignments. I need to reduce any overhead possible in order to maintain the efficiency of the current in memory model... I therefore believe that this is not premature. The architecture is cleaned up at the expense of performance and therefore I would like to keep that penalty down as low as possible :)
Yaneeve
I see, have fun with that one ;-) Did you see my comment on your original question about looking at Tomcat? Like I said that does just what you want to do when it creates the classes for JSPs.
Nick Holt
Now I have ;) Thanks!
Yaneeve
BeanShell is interpreted, but it creates byte code on the fly. Its scripting language based on JVM.
Silent Warrior
Could you elaborate please? If it is interpreted then how come it creates byte code? If it creates byte code then it means that it is compiled and linked during runtime...
Yaneeve
The fact that BeanShell allows you to call into actual classes implies BeanShell is creating byte code, the question is whether it discards that byte code once it has run.
Nick Holt
That of course is the million dollar question...
Yaneeve
+2  A: 

Groovy, BeanShell or any other scripting language which is based on JVM have such a facility to inject, modify, add and run code at runtime. Actually all the scripting language are interpreted, so actually those are not compiling at runtime.

Silent Warrior
A: 

you can fork a process like this

Process p = Runtime.getRuntime().exec("java -classpath "..." SomeClassContainingMain ...other arguments);    

        //you need to consume the outputs of the command if output/error is large otherwise the process is going to hang if output/error buffer is full. and create a seperate thead for it (not created here).
     log.debug("PROCESS outputstream : " + p.getInputStream() );
     log.debug("PROCESS errorstream : " + p.getErrorStream());   
    p.waitFor(); // Wait till the process is finished

and can compile and run it.

Rahul Garg
A: 

Javassist is almost a full Java compiler written in Java, and it's completely made of Java. You can't give it a whole .java file at once, but you can give it the code string for individual functions and add them to the same CtClass object, which becomes bytecode and then a java.lang.Class.

I just released version 0.1 of GigaLineCompile, which uses Javassist (compiler) and Beanshell (interpreter) together and gives you control over which code to optimize and when. In later versions, it will change between Javassist and Beanshell at a smaller granularity so if you have many strings of code that share some substrings, the substrings will be compiled and the other parts run in beanshell. Its mostly useful for artificial intelligence that generates Java code, but its also an alternative to Clojure or the extremes of Javassist/Beanshell alone.

Javassist, Beanshell, and GigaLineCompile can be downloaded (with source) here: http://sourceforge.net/projects/gigalinecompile

Ben F Rayfield