Wednesday, April 23, 2008

Scriptconsole4j: Embed a scripting console in your Java App.

Using scripting languages in Java became very easy. The JSR 223 standardizes the usage of scripting languages in the Java Platform; and Java 6 already ships with built-in support to JavaScript (Mozilla Rhino engine).

If you need to embed a multi-language enabled scripting “visual console” to your application you may try the scriptconsole4j.

I was developing a Java application where I needed to do some coding on the fly during runtime, change variable values, run snippets of code, etc. So, I’ve created a simple scripting console to use on that application. I don’t know if something like that already existed, but my code and its usage are really simple.

I’ve extracted the classes and made a few changes on it and created a separate reusable jar library (the scriptconsole4j). It is hosted at http://code.google.com/p/scriptconsole4j/

There are two ways to use it:

  • A JPanel with scripting functionality that can be embedded in another application (e.g. you can add a scripting window, or a scripting console to your app and access some of your application variables through the scripting window)
  • A standalone frame that embeds the above scripting console (e.g. you can practice the scripting language of your choice with it)

This scripting console provides a standard output variable, but allows variables from your own application to be available through the scripting console.
The combobox shows all available scripting engines on the running JVM. By default, Java 6 comes with JavaScript.

By doing the following as in the example, it is possible to add you custom(s) variable(s):
import scriptconsole4j.*;
...

JFrame frame = new JFrame("[Scriptconsole4j] Default Window");
//No textual description available. The console will just display the classname as info
ScriptContextVariable myObjectVar = new ScriptContextVariable(new MyObject(),"myobj","");
//The scripting panel can take several ScriptContextVariable as parameter
ScriptingPanel panel = new ScriptingPanel(myObjectVar);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
frame.add(panel);
frame.setVisible(true);
You can add any variable that you want and use it from the scripting panel. In the above code we add an object of type MyObject that will be available in the console with the alias of "myobj". There is no big deal here, since the JSR 223 allows context objects. However, we wrap it and put some additional info so other can see what are the variables that we intentionally want to let available to the console, and hopefully some (optional) textual description of it.


Other JSR 223 compliant engines of existing scripting languages are available at http://scripting.java.net .The download on that site provides the JSR 223 engines but it is still necessary to make additional downloads to the scripting libraries themselves.

Several improvements still need to be done in the console (e.g. ability to load scripts, script embelisher, undo support), but this little panel brought a lot of flexibility for me during development time of my apps.

The first release can be downloaded here.

I hope you can give it a try to see if it helps you too.

Wednesday, April 2, 2008

"Hacking" JHat API to integrate it to my application

Those that already used jhat may say: "JHat API? I thought it was just a java tool that comes with JDK 6."
I'll explain.

JHat (Java Heap Analysis Tool) is an experimental tool that comes with JDK 6. It can parse a heap dump file (HPROF format) and provide a simple http server (port 7000 as default) where we can execute queries in a simple SQL like language that has some predefined functions.


No big deal so far. All that is documented. A while ago I needed to integrate a memory inspection tool in a Java application that I was developing. I know there are few tools to do that externally but I wanted do memory inspection from a menu option in my application.

Although jhat is available as a command line tool, actually its classes are in the tools.jar file. So, I thought I could try do integrate that with my app. I tried to google some javadocs on jhat. Couldn’t find it. The tool is experimental, I forgot that.

Thanks to decompilers I could see how to start up the query engine. I extracted the code and could adapt it to my needs in my application.

I’ve tried to write the examples in a more didactic method in order to be more understandable. I hope I’ve put it in an easy way.

In order to run the example you would need an HPROF file. There are some ways to obtain a heap dump. The easiest (I think) is to run jmap and get the file dump.

Memory inspection done this way is not real time, since we are analyzing a snapshot of memory. But that helped me a lot. Remember that this is a heavy process that may consume lots of memory.

  1. Run jps to find out the PID of the Java application to take the memory snapshot
  2. Now run: jmap –dump:fomat=b,file=myheapdump.bin
  3. Now: java JHatExample myheapdump.bin

In case of out of memory errors when running the example use the –Xmx option:

java –Xmx512m JHatQueryExample myheapdump.bin

The example consists in three classes:
  • JHatExample: The GUI code
  • JHatWrapper: A class that wraps the access to the jhat API
  • MyQueryVisitor: Implementation of the jhat’s ObjectVisitor interface that provides a call back method on the results of the query.
Running the code will show the following GUI:

With the same query from the picture, if you click execute it will show all referrers to all instances of java.io.File.
Actually the way I print the result of the query is quite confusing...
If anybody ever happen to try to run the code, there is a good link on constructing queries in Sundarajan's blog with nice examples. The code I provided makes some calls to the javascript library that is provided with JHat.
If you uncompress the tools.jar file you may find that library and the help page in:
tools.jar\com\sun\tools\hat\resources\

The purpose of this code is to show the possibility to integrate jhat usage into your application. Normally it is used as a stand alone tool. The stand alone tool is ready to use, but is a side tool that you have to open. What I did in my app (not the one shown here) was to automate the memory dump via a Runtime.exec call to jmap, and then the resulting file was used as a parameter to instatiate the jhat engine.

Since jhat is an experimental tool, it may completely change or become unavailable on Java 7. Use it like I did at your own risk.

Have fun playing with the code!

The GUI Code:

import java.awt.*;
import java.io.File;
import javax.swing.*;

public class JHatExample extends JFrame {
private JHatWrapper jhat;

public JHatExample(JHatWrapper jhat) {
super("JHat wrapper");
this.jhat = jhat;
initComponents();
}


private void initComponents() {
final JTextArea queryText = new JTextArea();
JPanel buttonPanel = new JPanel();
JButton executeBtn = new JButton("Execute");
buttonPanel.add(executeBtn);
this.add(new JScrollPane(queryText),BorderLayout.CENTER);
this.add(buttonPanel,BorderLayout.SOUTH);
executeBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
jhat.query(queryText.getText());
} catch (Exception e1) {
JOptionPane.showMessageDialog(JHatExample.this, "Error executing query");
}
}
});
}

public static void main(String[] args) throws Exception {
if (args.length == 0 || ! new File(args[0]).exists()) {
System.out.println("The program must take a valid file path as parameter");
return;
}
JHatWrapper jhat = new JHatWrapper(args[0]);
JHatExample frame = new JHatExample(jhat);
frame.setSize(400,200);
frame.setVisible(true);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
}

Now the wrapper class:
import java.io.*;
import com.sun.tools.hat.internal.model.Snapshot;
import com.sun.tools.hat.internal.oql.OQLEngine;
import com.sun.tools.hat.internal.parser.HprofReader;
import com.sun.tools.hat.internal.parser.PositionDataInputStream;

public class JHatWrapper {
private OQLEngine engine;

public JHatWrapper(String s) throws Exception {
PositionDataInputStream positiondatainputstream =
new PositionDataInputStream(new BufferedInputStream(new FileInputStream(s)));
int l;
Snapshot snapshot;
l = positiondatainputstream.readInt();
//I "inferred" the meaning of the parameters below by comparing the decompiled code
//and jhat's command line parameters
int debuglevel = 0;
//Don't know what this is...
int j =1;
HprofReader hprofreader = new HprofReader(s, positiondatainputstream,
j, true, debuglevel);
snapshot = hprofreader.read();
positiondatainputstream.close();
//resolve references
snapshot.resolve(true);
engine = new OQLEngine(snapshot);
}

public void query(String query) throws Exception {
engine.executeQuery(query, new MyQueryVisitor(engine) );
}
}

And the Visitor code:

import java.util.Enumeration;

import com.sun.tools.hat.internal.model.JavaHeapObject;
import com.sun.tools.hat.internal.oql.OQLEngine;
import com.sun.tools.hat.internal.oql.ObjectVisitor;

public class MyQueryVisitor implements ObjectVisitor {
private OQLEngine engine;

public MyQueryVisitor(OQLEngine engine) {
this.engine = engine;
}

public boolean visit(Object obj) {
try {
Object iterator = engine.call("wrapIterator",new Object[]{obj});
//Handles result items that are instance of enumeration. Ex: select referrers(x) from java.io.File x
if (iterator instanceof Enumeration) {
System.out.println("enum");
handleEnumeration((Enumeration)iterator);
} else {
//handles single result items. Ex: select x from java.io.File x
JavaHeapObject javaObj = (JavaHeapObject)engine.call("unwrapJavaObject",new Object[]{obj});
handleEnumeration(javaObj.getReferers());
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
void handleEnumeration(Enumeration en) {
while (en.hasMoreElements()) {
try {
Object obj = en.nextElement();
JavaHeapObject unwrapped = (JavaHeapObject)engine.call("unwrapJavaObject",new Object[]{obj});
System.out.println(unwrapped);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

Yet another Java blog

Ok, one grain of sand in the desert of blogs that talk about Java...
My intention is to leave some (useful) contribution. I frequently google for things to help me when I develop.
Sometimes I go to forums and mailing lists to make questions but usually I never contribute back helping other people. What a leech!
I hope my posts can help somebody.