InstantJ. Get it @ instantj.sourceforge.net
InstantJ is a library you can use to compile and execute Java code or evaluate expressions written in Java. This is done on the fly - there is no pre-compiling step necessary. This is ideal in cases where expressions are either assembled programmatically at runtime, are read from descriptors or are received from user-input.
InstantJ differs from other approaches that can be utilized to compile and execute Java functionality:
One important thing to mention is the overall overhead of InstantJ. Imagine reading a set of expressions from a Servlet and executing them in multiple concurrent transactions. An Expression in InstantJ is compiled and can be evaluated over and over again while taking advantage of the VM's speed. Here are a couple of numbers that show how feasible the deployment of InstantJ really is (taken from instantj.tst.PerformanceTest in J2SE v1.4):
x1000 x10000 pre-compiled Java code 16 ms 94 ms instantj.expression.Expression 31 ms 187 ms Beanshell 27 seconds! 4.5 minutes!
back to top
To compile dynamic Java code and creat a Java type at runtime simply invoke the static method 'compile' of the type instantj.compile.Compiler. It returns an instance of java.lang.Class that you can use as see fit.
Example instantj.tst.SimpleCompileTest shows how this is done:
// Compile the source & get the type Class type = Compiler.compile(new Source(TYPE, SOURCE), true); // Test the type System.out.println(TYPE+".toString() returns "+type.newInstance());
(exception handling removed for simplicity)
You wonder how this works internally? The compilation relies on Sun's compiler-package sun.tools.javac.*. That's why a JDK's ./lib/tools.jar has to be in the classpath (see "Build&Test" below). Importantly no external files are generated - the compilation and classloading of the dynamic type is all done in-memory.
back to top
To evaluate Java expressions you take a valid expression (see "Valid Expressions" below) and pass it to a new instance of instantj.expression.Expression. You can then create an instance of that expression and us its evaluate method as shown in the example instantj.tst.HelloWorldTest:
result = new Expression(body).getInstance().evaluate();
(exception handling removed for simplicity)
It becomes a little bit more complicated if you want to put the expression into a context. How about using properties in the body of the expression and providing the values dynamically? The example instantj.tst.DescriptorTest shows how this is done:
// Try to compile the expression ex = new Expression(conf.getExpression(),conf.getProperties()); // now we create an instance for the expression and let it grab // necessary properties from the map we've collected but // alternatively we could set properties ourselves with : // ex.getInstance().set("something", somevalue).set("else", anothervalue).evaluate(). result = ex.getInstance(conf.getValues()).evaluate();
This example needs a bit more of explaining. It's rather big because the expressions are read from test-cases that reside in the folder ./tests. For now we want to know what is passed into the constructor of instantj.expression.Expression. The first argument is the expression-body ... o.k.! The second argument is a java.util.Map that maps property-names of the context to types ... aha. This is necessary for the expression to be setup correctly.
Then we create an instance of the expressoin - we've decided to pass in a java.util.Map that maps the property-names to values ... now the context is all set.
So lastly we call evaluate on instantj.expression.ExpressionInstance and retrieve the result. Not THAT hard indeed! Since expressions don't change (only values change for successive evaluations) an instance of instantj.expression.Expression can be used over and over again to resolve a instantj.expression.ExpressionInstance (the former being thread-safe the latter not!).
(you can read more about the internal workings of this test below)
Note on Expression Properties: If you use properties in your expression body as mentioned above you have to provide a map of property-names mapped to property-types. The latter can be instances of
Note on Debugging: By setting the system-parameter instantj.expression.output you can force that generated expression's debug information is dumped into the specified directory. A bunch of Java-files will appear that contain the temporary (normally all in memory) expression-body that is compiled into a Java-type. Either set this parameter on the command-line:
java -Dinstantj.expression.output=c:/temp ...or dynamically in your code:
System.setProperty("instantj.expression.output","c:/temp");
Please follow the these rules when preparing expressions:
INSTRUCTION; INSTRUCTION; INSTRUCTION; VALUE -> INSTRUCTION; INSTRUCTION; INSTRUCTION; Object result = VALUE;
"Hello World" | is valid only with the enclosing quotes " |
String message = "Hello World"; message | is not a valid expression without the trailing value message because it would end with an instruction |
someVariable | is a valid expression if someVariable was declared in the Expression's constructor |
new java.awt.Button("Click!") | is not valid without the package qualifier java.awt. |
Arrays.asList(new String[]{"4", "3", "1"} | is a valid expression because java.util.* is imported by default |
if (true) { salary+=10000; } ; salary | is not valid without the ';' after the code-block |
System.gc(); null | is valid and will run the system's garbage-collector - using null as the resulting value is a trick to circumvent rule #2 |
For further examples look into ./tests.
back to top
We had a quick look at instant-2.xml above. This test reads descriptors from .xml-files in folder ./tests. A descriptor consists of XML that describes the expression and its context. Here's the example again:
<test> <comment> This expression formats a property (Note: by default expressions import java.util.* and java.text.* that's why specifying 'Date' is enough) </comment> <property name="birth" type="Date" value="1970/05/25"/> <expression> new SimpleDateFormat("MMM dd yyyy").format(birth) </expression> </test>
instantj.tst.DescriptorTest uses a SAXParser (included in JDK 1.4) to parse the file, ignore the comment-element, extract the property-information from the property-elements and use the expression-body from the expression-element. Have a look! Next time your information about a dynamic expression might come from a descriptor read from a database :)
back to top
The distribution of InstantJ comes with a web archive (WAR) that is ready to be deployed to your favourite Application Server. Simply locate instantj-x.y.war in directory ./lib, deploy it to your server and point your browser to http://myserver:myport/instantj-x.y
Your browser will show a simple form that is fed by
instantj.j2ee.ExpressionServlet.
This servlet compiles and evaluates expressions entered in a HTML-form.
This is a proof of concept only - don't deploy to a system accessible
from the outside. Be aware that anyone accessing this form can run any Java expression
on your server!
To compile and test InstantJ you need Ant. Make sure your system-environment ANT_HOME points to its installation-directory and JAVA_HOME points the installation-directory of your JDK of choice (1.3 or 1.4).
Adopt ant.properties to match your environment. The following parameters have to be set:
dir.build the directory to build into j2ee.class.path the classpath used to compile j2ee components (uncomment to enable building of those)
Then run
ant.cmdto build and run the examples (you will have to adopt ant.cmd to your non-Windows system).
Note: instantj.tst.DescriptorTest utilizes SAX support - ant's libraries are used if you use the provided ./ant.cmd script. When integrating in another environment (e.g. servlet-engine) make sure to either run in JDK/JRE 1.4 or higher or to supply a JAXP and SAX implementation.
Note: InstantJ utilizes Sun's Java compiler support from package sun.tools.javac (contained in %JAVA_HOME%/lib/tools.jar). The build process places this archive into the classpath automatically. For other environments you will have to make sure yourself that tools.jar is in the classpath!
Note: The j2ee components will not be build unless j2ee.classpath is uncommented
back to top
Get more information from InstantJ @ Sourceforge - Disclaimer:
Copyright (C) 2002 Nils Meier This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USAback to top