InstantJ. Get it @ instantj.sourceforge.net

Abstract

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

instantj.compiler.*

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

instantj.expression.*

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

To make sure that your expression will actually end up with values in its properties you can If you decide to provide values by passing a map or by calling set() then a value can either be 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");
  

Note on Valid Expressions: The constructor of instantj.expression.Expression tries to compile the body of the expression that you provide. You will get a instantj.compiler.CompilationFailedException if there are problems with your expression.

Please follow the these rules when preparing expressions:

  1. use fully-qualified types when referring to types other than those from the java.lang.*, java.util.*, java.text.*, java.io.*, java.net.*
  2. make sure that your expression ends with a variable or value and not an instruction (e.g. it shouldn't end with semicolon ';')
  3. don't use return in your expression
  4. make sure that when using instructions in your expression it still ends in something that can be assigned to a variable (can be primitive!). See how the red expression is looked at by InstantJ (in blue)
       INSTRUCTION; INSTRUCTION; INSTRUCTION; VALUE
             ->    INSTRUCTION; INSTRUCTION; INSTRUCTION; Object result = VALUE; 
  5. terminate a code-block (ends with '}') just before the trailing value with ';'
Here are a couple of examples:

  "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

instantj.tst.DescriptorTest - INTERNALS

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

instantj.j2ee.*

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!

Build&Test

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.cmd
      
    to 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

    About

    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  USA
     
     

    back to top