Wikipedia Affiliate Button

четвер, 30 квітня 2009 р.

Calling java from XUL applications

XULRunner is a technology from Mozilla that allows to create cross-platform GUI applications just as easy as a websites. Today I was trying to use my java libraries inside of a XUL application. This is basically to be able to load a jar file and call java functions with my javascript code. It turns out that the only ( and the best ) way to accomplish this is by using LiveConnect, a feature of java plugin that allows to 'talk' to java. LiveConnect among other things is responsible for communication with java applets. With LiveConnect one can instanciate arbitrary java class and use it in javascript. Any java objects returned from method calls will be accessible in javascript as well as any javascript objects passed to java methods will be accessible in java. To achieve this effect LiveConnect does two-way wrapping of objects, i.e. it wraps java object with javascript and the other way around. More precisely, to instanciate a JVM object simply do this:
var javaObject = new java.lang.String('Hello');
So to load classes from a library simply instanciate a classloader and then use it to instanciate the classes. Something like this:
  function getAppPath(appName) {
    var chromeRegistry = Components
      .classes["@mozilla.org/chrome/chrome-registry;1"]
      .getService(Components.interfaces.nsIChromeRegistry);
            
    var uri =
      Components.classes["@mozilla.org/network/standard-url;1"]
        .createInstance(Components.interfaces.nsIURI);
    
    uri.spec = "chrome://" + appName + "/content/";
    
    var path = chromeRegistry.convertChromeURL(uri);
    if (typeof(path) == "object") {
        path = path.spec;
    }
    
    path = path.substring(0, path.indexOf("/chrome/") + 1);
    
    return path;
  };

  var basePath = getAppPath('myapp');
  var url = new java.net.URL(basePath + 'java/my.jar');
  var cl = new java.net.URLClassLoader( [ url ] );
  
  var aClass = java.lang.Class.forName("com.stan.XULDemo", true, cl);

  var inst = aClass.newInstance();

  // Calling 'sum' function from java
  var a = inst.sum(2, 3); 
  alert(a);  // Will alert '5'

  // A function which we'll pass to java
  // and have java call it later with a parameter
  var callback = function(str) {
    alert(str);
  };

  inst.callMeLater(callback, 'test');
The code of com.stan.XULDemo.java will be the following:
package com.stan;

public calss XULDemo {
  public int sum(int a, int b) {
    return a + b;
  }
  
  public void callMeLater(final JSObject func, final String text) {
    new Thread() {
      public void run() {
        try {
          Thread.sleep(5000);  // Sleep 5 seconds
        } catch(Exception e) { }
        
        // We are passed a javascript Function Object 
        // which has method 'call' used to actually call the
        // function. The first parameter is 'this',
        // followed by a set
        // of actual function parameters
        func.call("call", new Object[] { null, text });
      }
    }.start();
  }
}
This works out of the box with XULRunner. Note. Currently there are troubles with LiveConnect 1.9 on Mac (jdk 1.6 u3) which I am looking at right now.

8 коментарів:

Csam0003 сказав...

hi Stanislav,

I am trying to bridge java and javascript using Liveconnect. everything works well except when I include a method that creates a new instance of something, LiveConnect crashes and I get the following error:


Ignored exception: java.security.PrivilegedActionException: java.lang.reflect.InvocationTargetException


Any idea why?

thank you for your time and patience

Kind regards

Chris

Stanislav Vitvitskiy сказав...

By default the java plugin runs your code in very restrictive environment (the one that is used to run applets).

For your development purposes it will be enough to tweak the policy of the system JRE. To do this add the following block:

grant codeBase "file:/<path to your jars>/-" {
permission java.security.AllPermission;
};

to <jre-home>/lib/security/java.policy file.
For example this block will give all permissions to all .jar and .class files loaded from windows disk C:

grant codeBase "file:/c:/-" {
permission java.security.AllPermission;
};

For production you'll most likely want to bundle JRE with your software to guard from any surprises. I am currently researching on the most elegant way of doing this and will cover this topic in the future posts.

Stanislav Vitvitskiy сказав...

Also, for java versions earlier then 1.6.0_12 you can't use array syntax in javascript.

So you'll have to replace these 2 lines:

var url = new java.net.URL(basePath + 'java/my.jar');
var cl = new java.net.URLClassLoader( [ url ] );

With these:

var urlClasz = java.lang.Class.forName('java.net.URL');
var urlArray = java.lang.reflect.Array.newInstance(urlClasz, 1);
urlArray[0] = new java.net.URL(basePath + jarPath);
var cl = new java.net.URLClassLoader( urlArray );

Mike Desjardins сказав...

Great intro! I don't suppose you ever had any luck getting this to work on a Mac, did you? I'm trying to get my XUL Runner app (http://getbuzzbird.com) to call java code, and it's causing a low-level exception and crashing as soon as I try to do anything with the classloader. Have you gotten it to work?

Thanks!

Stanislav Vitvitskiy сказав...

Mike,

Actually it is possible for Mac (I have Leo 10.5.8) but you're right there are some pitfalls.

First, you'll need a different java plugin ( the default doesn't work). Check this link: http://javaplugin.sourceforge.net/, the instructions are pretty straightforward.
Then, because XULRunner is 32 bit you'll need to have 32 bit java in your system. On my Mac, this is Java 1.5, so I have to use this one. Similarly 64 bit XULRunner will work only with 64 bit java. So you'll need to take care and check this. There's always an option to build SoyLatte ( a BSD port of OpenJDK ), but I personally didn't follow this path.
Also, you'll most probably have to actually install your app ( and not just launch ) to actually see the menu bar. I am not sure whether this is fixed in the latest versions of XULRunner.

Tom сказав...

Good to know someone I could share my ideas.
Order Zyprexa Online

Leon Victor сказав...

Your concept is very good to call java from XUL applications. I will try and give you reply as soon as possible.

Unknown сказав...

Is this still applicable today? I was hoping to use my java libraries as well from XULRunner and from Firefox Addons etc.