Janino

What is Janino? Here is the description from the Janino homepage:

Janino is a compiler that reads a JavaTM expression, block, class body, source file or a set of source files, and generates JavaTM bytecode that is loaded and executed directly. Janino is not intended to be a development tool, but an embedded compiler for run-time compilation purposes, e.g. expression evaluators or "server pages" engines like JSP.

Although Janino comes with some examples it is not so obvious how to embed it into your own application. That's why I put together two examples which should help you doing that.

Example 1: compiling from files

The first example is a simple getting startet example:

package org.svenehrke.example.janino.command;

import org.codehaus.janino.JavaSourceClassLoader;
import org.codehaus.janino.DebuggingInformation;

import java.io.File;

public class JavaSourceClassLoaderExample
{
  public static void main(String[] args) throws Exception
  {
    new JavaSourceClassLoaderExample().execute();
  }

  private void execute() throws Exception
  {
    File janinoSourceDirs = new File("janino-src");
    File[] srcDirs = new File[]{janinoSourceDirs};
    String encoding = null;
    ClassLoader parentClassLoader = getClass().getClassLoader();
    ClassLoader cl = new JavaSourceClassLoader(parentClassLoader, srcDirs, encoding, DebuggingInformation.NONE);

    Command xc = (Command) cl.loadClass("org.example.svenehrke.janino.command.MyCommand").newInstance();
    xc.execute();
  }
}

janinoSourceDirs points to a directory on the disk containing the Java source files to be compiled by Janino (and not by your usual compiler). This is passed to a JavaSourceClassLoader which can then be used to a create a class object to create an instance of it with the usual Class.newInstance() call. Isn't this great? For completeness here is the class Command which is compiled in the usual way and the the file MyCommand.java which is not compiled in the usual way but rather sits in the folder janino-src as a kind of text file (make sure to have it outside your src folder).

package org.svenehrke.example.janino.command;

public interface Command
{
  public void execute();
}
package org.example.svenehrke.janino.command;

import org.svenehrke.example.janino.command.Command;

public class MyCommand implements Command
{
  public void execute()
  {
    System.out.println("MyCommand.execute(): hello from janino");
  }
}

Just to give you a hint on what you can do with this: Try to put the code above in an endless loop so that MyCommand will be reloaded and executed all the time. This will continously output MyCommand.execute(): hello from janino. Then, without stopping the program open the file MyCommand.java with an arbitrary text editor, change the text to be printed and save it. You should notice that the output changed immediately. I found that extremely cool! Of course you wouldn't do it like this in reality but it demonstrates how you can dynamically change, compile and use Java code at runtime.

By the way: if you decide use this in you work and want to leverage the power of your IDE for the Java text files (under the folder janino-src) I recommend to create an extra project using that directory as the source folder just for editing those files.

Example 2: compiling from arbitrary resources

In some situation you would rather not use the filesystem as a source for these dynamic Java source files because you might want to read them from jar files for example or generally from the classpath. This is a bit more complicated because you need to write your own ResourceFinder. Here is how to do it:

package org.svenehrke.example.janino.command;

import org.codehaus.janino.JavaSourceClassLoader;
import org.codehaus.janino.DebuggingInformation;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.apache.commons.io.IOUtils;

import java.util.Map;
import java.util.HashMap;
import java.io.StringWriter;
import java.io.IOException;

public class JavaSourceClassLoaderFromMapExample
{

  public static void main(String[] args) throws Exception
  {
    new JavaSourceClassLoaderFromMapExample().execute();
  }
  private static final String CLASSNAME = "org/example/svenehrke/janino/command/MyCommand";

  private void execute() throws Exception
  {
    String dotClassName = CLASSNAME.replace('/', '.');
    Resource resource = getResource();
    try
    {
      byte[] ba = byteArrayFromResource(resource);
      Map map = new HashMap();
      map.put(CLASSNAME , ba);
      MapResourceFinder mrf = new MapResourceFinder(map);

      ClassLoader parentClassLoader = JavaSourceClassLoaderFromMapExample.class.getClassLoader();
      String encoding = null;
      ClassLoader cl = new JavaSourceClassLoader(parentClassLoader, mrf, encoding, DebuggingInformation.ALL);

      Command cmd = (Command) cl.loadClass(dotClassName).newInstance();
      cmd.execute();
    }
    catch (Exception e)
    {
      throw new RuntimeException(e);
    }
  }

  private byte[] byteArrayFromResource(Resource aResource)
    throws IOException
  {
    StringWriter sw = new StringWriter(8192);
    IOUtils.copy(aResource.getInputStream(), sw);
    return sw.toString().getBytes();
  }

  private Resource getResource()
  {
    return new FileSystemResource("janino-src/org/example/svenehrke/janino/command/MyCommand.java");
  }
}

The idea is to load the Java source code as byte array from an arbitrary resource, put it under a key in a HashMap which then will be used by the JavaSourceClassLoader instead of the filesystem as in the first example. Fo convenience I used some helper classes from Spring and commons-io in this example but it would work without them just as well.

Here some more detailed explanation: routine getResource() returns an object of type Resource (from Spring). Here it returns a FileSystemResource to keep the example simple but it could just as well return a ClassPathResource instance or any other type of Resource. Then next thing which needs to be done is to get a byte array from this resource which is done by routine byteArrayFromResource. This byte array will be put into the map under the key CLASSNAME. It is important that the key contains slashes. The constructor of JavaSourceClassLoader is passed in a MapResourceFinder using this HashMap. When we then load the class it is important to use dots instead of slashes in the classname (dotClassName). Here is the source of MapResourceFinder

package org.svenehrke.example.janino.command;

import org.codehaus.janino.util.resource.Resource;
import org.codehaus.janino.util.resource.ResourceFinder;

import java.io.IOException;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.util.Map;

public class MapResourceFinder extends ResourceFinder
{
  private final Map map;
  private long lastModified = 0L;

  public MapResourceFinder(Map map)
  {
    this.map = map;
  }

  public void setLastModified(long lastModified)
  {
    this.lastModified = lastModified;
  }

  public final Resource findResource(final String resourceName)
  {
    int p = resourceName.indexOf(".java");
    final String s = resourceName.substring(0, p);
    final byte[] ba = (byte[]) this.map.get(s);
    if (ba == null) return null;

    return new Resource()
    {
      public InputStream open() throws IOException
      {
        return new ByteArrayInputStream(ba);
      }

      public String getFileName()
      {
        return s;
      }

      public long lastModified()
      {
        return MapResourceFinder.this.lastModified;
      }
    };
  }
}