Skip to main content

Running in the Family

Posted by editor on September 21, 2006 at 8:29 AM PDT


Sooner or later, everything will be callable from Java

A few years back, I needed to call AppleScript from a Java program -- really, what I needed was to send Apple Events -- but with no way to do it programmatically, I was forced to do a Runtime.exec() invocation of osascript. Yuck.

Fortunately, Thomas Künneth's jasconn project uses JDK 6's scripting language support, as defined by JSR-223, to offer a Java-friendly binding to AppleScript. It only works with the pre-release JDK 6 available from the Apple Developer Connection (NDA genuflection required) and of course is only for Macs, but I thought I'd give it a spin yesterday.

It's a pleasure to report that jasconn worked well. What I did was to take an AppleScript I use to set my iChat status as the number of e-mails in my O'Reilly INBOX, so co-workers can see how many blog requests, article proposals, and news blurbs I have yet to dig through before I'm able to take on anything new. Here's the script:

global messageCount
tell application "Mail"
        set oReillyAccount to account named "O'Reilly"
        set oReillyBox to mailbox "INBOX" of oReillyAccount
        set oReillyMessages to messages of oReillyBox
        (* set oReillyCount to length of oReillyMessages *)
        (* only count non-deleted messages *)
        set messageCount to 0
        repeat with aMessage in oReillyMessages
                if (deleted status of aMessage is not true) then
                        set messageCount to (messageCount + 1)
                end if
        end repeat
end tell

tell application "iChat"
        set status message to "O'R Inbox at " & messageCount
end tell

Normally, this sits in my ~/Library/Scripts, waiting for me to invoke it from the optional script menu. For kicks, I decided to wire it up to a trivial Swing GUI:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import de.thomaskuenneth.jasconn.*;
import javax.script.*;

public class JASConnTest extends JFrame implements ActionListener {

    JButton button;
    ScriptEngine engine;
    String script;

    public JASConnTest() {
        super ("JASConn Test");
        getContentPane().setLayout (new FlowLayout());
        button = new JButton ("Update iChat");
        button.addActionListener (this);
        getContentPane().add (button);
        try {
            setUpScriptEngine();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void setUpScriptEngine() throws IOException {
        // get jasconn
        ScriptEngineManager sem = new ScriptEngineManager();
        engine = sem.getEngineByName ("jasconn");
        if (engine == null)
            throw new RuntimeException ("couldn't find jasconn engine");
        // read script into a string
        char[] buffy = new char[1000];
        StringBuffer sb = new StringBuffer();
        BufferedReader reader =
            new BufferedReader (new FileReader ("OReillyInboxAt.applescript"));
        int bytesRead = 0;
        while ( (bytesRead = reader.read (buffy)) > 0) {
            sb.append (buffy, 0, bytesRead);
        }
        script = sb.toString();
    }

    public static void main (String[] arrrImAPirate) {
        Frame f = new JASConnTest();
        f.pack();
        f.setVisible(true);
    }

    public void actionPerformed (ActionEvent e) {
        if (e.getSource() == button) {
            try {
                engine.eval (script);
            } catch (ScriptException  se) {
                se.printStackTrace();
            }
        }
    }
}

Notice how the script has an .applescript extension, indicating that it has been saved as plain-text. I'm not sure whether compiled .scpt files would work. Also notice that this reads the entire script into a String and caches it. That's one option with using ScriptEngine.eval(); another is to give it a Reader, presumably pointed to the flat file. Call it a premature optimization, but reading the whole script in and keeping it in memory is a huge performance boost over letting the native script menu read, parse, and execute the script each time.

Anyways, compile and run with the full long path (you have to point to /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Commands to do this, as anyone who's juggled Java versions on Mac OS X knows. Here's the resulting GUI:

jasconn-test-frame.png

And when I click the button, here's the result in iChat:

jasconn-ichat-status.png

This is one of those unanticipated bonuses of JSR-223. It's not about whether other languages are "better" or "worse" than Java, but what they can do. AppleScript is a profoundly nifty way of interacting with native applications on the system, and jasconn exposes them to Java. Nobody planned that when they wrote the JSR-223 draft, any more than they anticipated, say, websites that would let you upload and run server-side JavaScript with Rhino. But that's what this feature enables. And a lot of people are going to find cool things to do with it.

So, would you like to bring your favorite language to Java? I'm glad you asked...

In today's Feature Article, Thomas shows you the nuts-and-bolts of
Making Scripting Languages JSR-223-Aware, in which he writes:

Establishing a basic link between Java and existing scripting languages using JSR 223 is quite easy. The number of interfaces you need to implement is fairly small. Additionally, Sun provides useful helper classes that you can take advantage of in your implementation.

Read on for the details of making scripting languages callable from Java.


In Java Today,

Artima takes note of JSR 306 in the discussion New JSR Aims To Change How Java Standards are Defined. "The recently submitted JSR 306, 'Towards a New Version of the JCP,' aims to refine how the Java Community Process (JCP) creates and manages Java standards. Artima spoke with Onno Kluyt, chair of the JCP, about the proposed changes. [...] The expert group for JSR 306 consists of JCP executive committee member companies and individuals. Artima spoke with Sun's JCP Chair Onno Kluyt, and JCP program manager Heather VanCura, about the proposed changes."

Last week's 10th Jini Community Meeting was a big success, and those who didn't attend can start catching up with some of the materials on the jini.org wiki. The 10th JCM Live page is being left as an informal collection of conference blogs, photos, and more. The 10th JCM Sessions page lists all the sessions and lightning talks from the meeting, and many of them already link to PDF slides and/or QuickTime video of the talks.

"Concurrent or multi-threaded software is not a new technology but its importance has been accelerating. This is primarily due to the low cost of multi-core CPUs that are becoming common in even the most basic machines." But how do you take advantage of concurrency? Nathan Tippy's article Discover the Elegant Simplicity of JSR 166 offers an introduction to a handful of the features provided by the java.util.concurrent packages.


Joshua