Wizard API updated!
Tim Boudreau has released a new version of the Swing Wizard library (version 0.997) that fixes the WizardException bug reported in JavaWorld's recent Open Source Java Project profile. The article's examples have been reworked to test out the new, improved WizardException. Thanks, Tim, for this helpful fix!
Open Source Java Projects: The Wizard API

Newsletter sign-up

Sign up for our technology specific newsletters.

Enterprise Java
View all newsletters

Email Address:

Escape the sandbox: Access native methods from an applet

Find out how you can directly invoke the Win32 API -- the IE-free way

Applet developers deploying on a Win32 platform sometimes find it necessary to invoke native Win32 methods from within an applet. Not realizing that other options are available, they may turn to Active X or JDirect, both of which rely on Internet Explorer on the client side. While this may be acceptable for some controlled intranets, in many cases it creates an impossible restriction. In this article, I'll show how to implement the same functionality using a Netscape client.

Previous articles (see Resources) have covered both creating signed applets that can access resources "outside the sandbox" and using native methods from within Java code using JNI. In this article, we're going to combine these two concepts to effectively create a Microsoft-free version of JDirect. We'll also use LiveConnect (built into Netscape Communicator) to invoke native methods directly from JavaScript.

While our example covers the Win32 API explicitly, you can easily extend the approach to run native code from an applet on any platform running the Netscape browser -- just create a different version of the native library.

Two features of Netscape's applet security model make implementing this functionality tricky. First, the native library must reside on the local drive on the client PC. While sensible, this policy requires that a DLL created using JNI be downloaded to the client.

An even trickier feature requires any class that directly invokes a native method to be loaded by the system class loader rather than by the applet class loader. Netscape looks for classes in its own classpath first and loads classes found there with its system class loader. If it can't find a class there, it looks in the applet's codebase. If a class is loaded from the codebase, it gets loaded by an AppletClassLoader and thus has fewer privileges. Therefore, the class that invokes native methods must reside in Netscape's classpath by the time it's first loaded by any applet.

In our example, we'll invoke two very simple Win32 methods called GetUserName() and GetComputerName(). GetUserName() returns the Windows user name, and GetComputerName() returns the Windows computerID. We can use this approach with any methods in the Win32 API. I've broken down our task into the following steps:

  1. Create the native library and Java "wrapper" class that uses JNI
  2. Create the Java applet class
  3. Create a signed jar file
  4. Create HTML code with JavaScript
  5. Deploy the applet


Create the native library and Java JNI class

The class file that invokes native methods needs to be downloaded to the client machine when the applet is started. This step is just standard JNI and is well described in a number of references (see Resources). This UserInfo class doesn't need to do much -- it simply contains two native methods declarations:

     public class UserInfo {
      public native String getUserName() throws Error;
       public native String getComputerName() throws Error;
     }


To create our native interface, we need to compile UserInfo, then run the javah utility (on the Windows platform) on the generated class file to produce a C header file. This file is produced automatically and shouldn't require any editing on your part.

JNI requires that we use the function prototypes in this header file to develop our native library. So, we import this header file into a Windows C development environment (I used Microsoft Visual C++ version 5.0) and create a C/C++ file that implements the methods defined in the native interface (in this case just invocations of the two Win32 methods described above). The C code looks like this:

     #include <windows.h>
     #include <jni.h>
     /*
      * Class:     UserInfo
      * Method:    getUserName
      * Signature: ()Ljava/lang/String;
      */

JNIEXPORT jstring JNICALL Java_UserInfo_getUserName (JNIEnv * env, jobject obj) { char *ptr; char nameBuff[255]; long length; ptr = nameBuff; GetUserName(ptr, &length ); return (*env)->NewStringUTF(env, ptr); }
/* * Class: UserInfo * Method: getComputerName * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_UserInfo_getComputerName (JNIEnv * env, jobject obj) { char *ptr; char nameBuff[255]; long length; ptr = nameBuff; GetComputerName(ptr, &length ); return (*env)->NewStringUTF(env, ptr); }


(Makes you glad you're a Java coder, doesn't it!)

Now use the create DLL option in Visual C++ to create a standalone DLL.

Whew! The hard part's over with. Now we can go back to writing Java.

Create Java applet classes

Netscape's security model, per our discussion above, requires that things occur in a particular order. Here's the sequence of events we need to follow:

  1. Load applet class
  2. Download DLL to client
  3. Download native method wrapper class to client
  4. Load wrapper class
  5. Load native DLL
  6. Invoke native methods


The UserInfoApplet class does the real work here (see Resources to download the source). The methods installDLL( ) (shown below) and installJNIClass( ) download the DLL and the UserInfo class. Both of these routines use the copyURL() method to copy from the specified URL to a specified location on the client machine. I adapted this method from the clever copyURL() utility described in "Java Tip 19: Java makes it easy to copy files from a Web site." Because these classes must request special privileges from the applet user, we must use the enablePrivilege() method from the Netscape Capabilities API (as shown in the following snippet). This method causes the browser to display a dialog, containing information about the applet signer (me, in this case) to the user.

Netscape's security dialog requests permission to venture "outside the sandbox"

In the case below, the user will be asked to allow access to the local filesystem.

  public void installDLL ( ) {
     try {
     PrivilegeManager.enablePrivilege("UniversalFileAccess");
     } catch (Exception e) { 
     System.out.println ("Can't enable file Privilege!!!"); 
     e.printStackTrace();
     }

dllDest = findTempDir() + "userinfo.dll"; System.out.println("Copying " + urlSource + dllFileName + " to " + dllDest + " . . . " ); copyURL ( urlSource + dllFileName, dllDest ); }



A similar method, installJniWrapperClass(), installs the Java wrapper class in Netscape's classpath. To comply with Netscape's security model, I've used Netscape's CLASSPATH directory as the destination for the Java class file. The utility method parseClassPath() supports this approach.

Notice that I used a temporary Windows directory for the DLL destination (supported by the findTempDir() method). This is all right as long as you use System.load(), which uses an absolute path, to load the library. If you use System.loadlibrary() instead, the system will search the path specified by the Windows PATH directory for the DLL, and you must use a directory in this path as the destination. Using System.load() with an absolute path is probably a more portable approach.

The loadLibrary() method, which actually loads the DLL, must also venture outside the sandbox and, therefore, must also use the Capabilities API to ask for permission.

       public void loadLibrary () {
          try {
          PrivilegeManager.enablePrivilege("UniversalLinkAccess");
          } catch (Exception e) {
          System.out.println ("Can't enable link Privilege!!!");
          e.printStackTrace();
          }

try { System.out.println ("loading library " + dllDest + " . . . "); System.load ( dllDest ); } catch (Exception e) { System.out.println ("Can't load library!!!"); e.printStackTrace(); } catch (Error err) { System.out.println ("ERROR: Can't load library!!!"); err.printStackTrace(); } }


Once you've downloaded the required files and loaded your native library, you can use your native methods, as in the example destroy() below. Note that we create an instance of the wrapper class each time the method is invoked -- the wrapper class shouldn't be accessed until it exists in Netscape's classpath. If you declare the wrapper class as an instance variable in the applet, it will be referenced when the applet is loaded -- and before the wrapper class has been downloaded and made available -- which will cause exceptions.

  
  public String getUserName() {
     String name;
     // we can't reference UserInfo class until AFTER it exists on
     // local classpath!
     UserInfo info = new UserInfo();
     try {
     PrivilegeManager.enablePrivilege("UniversalLinkAccess");
     } catch (Exception e) { 
     System.out.println ("Can't enable link Privilege!!!"); 
     e.printStackTrace();
     }

try { name = info.getUserName(); } catch (Error er) { er.printStackTrace(); return ("Java Error on getUserName!") ; } return name; }


Of course, you can extend the Java classes above to use any Win32 API methods, or methods from any native API.

For security reasons, the source URL for downloaded files should be hardcoded in the applet class. While using applet param tags might seem to be the easiest and most configurable approach, this opens a big security risk. A malicious user could copy the signed jar and simply specify a different URL for the DLL or class file. The substitute file could then operate with all the privileges granted to the original applet.

Whether you choose to leave the downloaded files on the client, or remove them when the applet is complete (probably in the applet's destroy() method) is up to you and your particular deployment approach.

1 | 2 |  Next >
Resources
  • Complete source code listing of UserInfoApplet, plus an example batch file to build the applet and create a signed jar http://www.javaworld.com/jw-10-1998/apptowin32/jw-10-apptowin32.zip
  • Sun's Java Native Interface Specification, tutorial, and FAQ provide a very good overview of native methods http://www.javasoft.com/products/jdk/1.1/docs/guide/jni/index.html
  • See Netscape's list of documentation related to object signing including tutorials, resources, tools, and FAQs http://developer.netscape.com/docs/manuals/signedobj/overview.html
  • Read Rinaldo Di Giorgio's "Use native methods to expand the Java environment" (JavaWorld, July 1997) for a good introduction to using native methods from within Java code using JNI http://www.javaworld.com/jw-07-1997/jw-07-javadev.html
  • Bret Sommers's "Outside the Sandbox" (Java Report Online, February 1998) provides an excellent tutorial describing signed applet creation http://www.javareport.com/html/features/archive/9802/somers.shtml
  • Java Tip 19"Java makes it easy to copy files from a Web site" (JavaWorld, December 1996) describes a clever utility for copying files from a URL http://www.javaworld.com/javaworld/javatips/jw-javatip19.html
  • VeriSign offers Netscape Class 2 and Class 3 SPCs http://www.verisign.com
  • Thawte Certification also offers Class 3 SPCs http://www.thawte.com
  • For a very complete treatment of the Java Native Interface, see Essential JNI by Rob Gordon (Prentice Hall, ISBN0-13-679895-0)