Search Flex Samples

Using a signed Java applet as a Flex helper - Part 2





In the previous post, I showed how to create a Java applet, embed it in the same HTML page that wraps a Flex application, and call a public method in the applet from Flex. In this post, I will demonstrate creating a signed Java applet that will help a Flex application interact with the native OS.


Step 1. Create the Java applet


Create the following Java source file. Name it Launcher.java:


import java.awt.*; import java.awt.event.*; import java.applet.*; import java.lang.*; public class Launcher extends Applet { public static void launchEditor( String path ) { String[] cmds = new String[ 2 ]; cmds[ 0 ] = "open"; cmds[ 1 ] = path; try { Runtime.getRuntime().exec( cmds ); } catch( Exception ex ) { System.out.println( "Exception calling Runtime.exec(...) " + ex.getMessage() ); } } }

NOTE: The “open” command only works on Mac OS X. For Windows, you'll need to execute “start.exe” with some options. Since I don't have a Windows machine to test this on, you're, once again, on your own.


Compile the Java source:


javac Launcher.java


which should create the file Launcher.class. Now, you'll need to create a JAR archive of your class:


jar cf Launcher.jar Launcher.class


This should create Launcher.jar.


Step 2. Sign the Launcher JAR file


I'll cover this process in a very cursory manner. There are a lot of tutorials available on self-signing Java applets.


You'll need to create a public/private key pair using the keytool utility in the Java SDK:


keytool -genkey -keystore mykeystore -alias launcheralias


Next, create a self-signed certificate:


keytool -selfcert -keystore mykeystore -alias launcheralias


Finally, sign the jar file:


jarsigner -keystore mykeystore Launcher.jar launcheralias


Step 3. Create a Flex application


In FlexBuilder create a new project called Launcher and replace the contents of Launcher.mxml with:


<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"> <mx:Script> <![CDATA[ private function onClick() : void { ExternalInterface.call( "document.launcherApplet.launchEditor( '[path to a text file]' )" ); } ]]> </mx:Script> <mx:Button label="Launch" click="onClick()"/> </mx:Application>

Replace [path to a text file] with an absolute path to some text file on your system.


Copy the Launcher.jar file created in Step 2 to the bin directory of the Flex project.


Step 4. Adjust the HTML wrapper


Add the following just before the closing </body> tag in the Flex applications' HTML wrapper:


<APPLET id="launcherApplet" archive="Launcher.jar" code="Launcher" align="baseline" width="200" height="200">

Step 5. There is no step 5


Just launch the application. If your applet is signed and installed correctly, you should be presented with a security confirmation dialog. Click “trust” then and click the “Launch” button in the Flex application. This should open your text file in whatever editor is associated with the .txt extension on your computer.


What? It doesn't work? Of course not. Here's the catch:


Even though the applet is signed, and therefore has privileged access to the OS, the thread executing methods called from JavaScript, and any threads it creates, do not share this privileged access.


If you look at the Java console, you should see a line something like this:


Exception calling Runtime.exec(…) access denied (java.io.FilePermission <> execute)


which makes clear why the operation failed.


We can get around this by making our applet considerably more complicated. We override the init() method, and spawn a new thread that is responsible for all operations that require privileged access. Public methods in our applet that are called from Javacript pass their work off to this thread, thus avoiding the problem with reduced privileges. Here's the new applet:


import java.awt.*; import java.awt.event.*; import java.applet.*; import java.lang.*; public class Launcher extends Applet { public void init() { ( new LauncherThread() ).start(); } public void launchEditor( String path ) { synchronized( _threadLock ) { _launchPath = path; _threadLock.notifyAll(); } } private String _launchPath; private Object _threadLock = new Object(); class LauncherThread extends Thread { public void run() { synchronized( _threadLock ) { while( true ) { try { _threadLock.wait(); String[] cmds = new String[ 2 ]; cmds[ 0 ] = "open"; cmds[ 1 ] = _launchPath; Runtime.getRuntime().exec( cmds ); } catch( Exception ex ) { System.out.println( "Exception in LauncherThread " + ex.getMessage() ); } } } } } }

Now, repeat Steps 1 through 4 with this new applet. This time, clicking on the “Launch” button should really open your file in the associated editor.


Some issues to consider


1. How to get method return values back to Flex? Shouldn't be too hard — the thread can put the results into a member variable in the applet class. More thread synchronization to handle, though.


2. How can the applet call back into Flex? It might be nice to let the applet go off and do some asynchronous processing, calling back into the Flex application when it's finished. It should be possible to do this if the applet can call a Javascript function. The Flex application can register a callback using ExternalInterface, which the applet can invoke.


3. How to deal with race conditions at startup? The Java applet may fail to load entirely, or may load some time after the Flex application loads. Or, the Flex application may finish loading after the applet is done. The applet and Flex application need some way of knowing when the other one is prepared to communicate.


4. How to (de)serialize Flex objects? The example I gave just passed a simple string into a Java method, but it's easy to conceive of more complex examples when object serialization would be required. Maybe an open source implementation of the AMF protocol could be included in the applet. The Artemis project uses this technique.

0 comments:

Related Flex Samples

Learn Flex: Flex Samples | Flex Video Tutorials Flex Examples