HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

Introduction
Large-scale distributed applications and shells often require the execution of external modules. Because the modules are not known at compilation time, this execution must be dynamic. For example, upon entering a command at the UNIX prompt, the shell must find, load, and execute the requested program or application. Although several methods have been found for loading (and executing) dynamic Java classes, launching a Java Application from within another Java program (applet or application) remains a difficult task.

This article presents a new method of loading and executing Java applications from already running Java programs. We will briefly describe the general structure of a Java Application and point out the challenges and the goals that guided our design of the Dynamic Java Application Loader. A more detailed description and a brief discussion of design issues of native libraries are also presented. We will point out some of the advantages of the methods proposed in this article. And finally, we'll present conclusions and point out topics for future research in this area.

Java Applications: An Overview
Every Java program is classified as either an application or an applet, based on their execution content. While applets require the presence of a Java-enabled browser, applications are most often launched from the command line, and run inside the Java runtime environment [1]. This section contains a short introduction to Java applications.

Every Java class can be a potential application if it includes a static main() method. This method must be declared public, and it must take an array of Strings as an argument. This array contains the command line arguments passed to the application by the executing shell. The length field of the argument variable represents the actual number of arguments, as illustrated in the example below:

public class JavaApplication {
public static void main( String args[] ) {
System.out.println( args.length + " arguments passed in." );
// ...
}
}

When this code is compiled with the Java compiler (javac), a JavaApplication.class file is generated. When the Java interpreter is invoked with this class name, it searches the class definition contained in the .class file for the main() method. If the method is found, the run-time environment first checks whether the signature of this function is declared static, and if it is publicly accessible. Next, the interpreter constructs the command line array, and invokes the main() method of the specified class. Since this method is declared static, the class is not instantiated [3].

There are of course advantages and disadvantages to this approach. Because the class containing the main() method is not instantiated, this implementation allows the programmer to include some necessary initialization before an instance of the class is brought into memory. The problem, however, is that static methods cannot be overridden by derived classes. Therefore, application classes cannot be used for further derivation.

Dynamic Application Loading
In an earlier article [2] we presented a technique for loading classes dynamically. The basic approach was loading the specified class-content using the default class loader, and then instantiating this new class by calling Class.newInstance().

Although the approach in [2] is promising, it has limitations; it will not work for loading Java applications dynamically. First, since main() is declared static, the interpreter will be unable to resolve the correct method for derived classes. Second, and more importantly, the class need not be instantiated in order to invoke its main() method. But without instantiating a class, its type is unknown. (Actually, because every class in Java is derived from Object, it can be referenced by this type. But because Object does not have a main() member function, this approach does not offer a solution for the problem considered in this paper.)

The solution is to use the method described in [2] for loading the class content dynamically, and then using native methods to invoke the static main() method. A disadvantage of using native methods is that the code loses its platform independence since the native methods are usually written in C, and contain platform-specific code. Considering the fact that a large portion of the Java system and language library classes are implemented using native libraries, this approach seems to be reasonable.

When launching Java applications from other Java programs, it is up to the programmer to construct and pass the command line arguments to the new application. The following pseudo-code fragment outlines the necessary steps for loading and executing Java applications. Some of the procedures illustrated in this example will be discussed in further detail in the next section.

public class Shell {
public void start( String applicationName ) {
String args[] = constructApplicationArguments();
Class cl = loadDynamicClass( applicationName );
nativeInvokeMain( cl , args );
}
}

In the above example, the start() method is used to launch the new application, whose name is specified by the function's argument. The first step is to construct the command line arguments for the application. If there are no command line arguments to be passed on to the new application, the calling program should still construct an empty string array instead of passing null. The reason for this is that most applications will check the number of arguments before parsing the command line by calling args.length (where args[] contains the command line arguments for the application); if args is null, this will raise a NullPointerException. Next, the application's class is loaded into the environment using the default class loader. This procedure only obtains the class definition information; it will not instantiate the new application. Finally, we make a call to our native function to invoke the static main() method of the newly loaded class.

Dynamic Application Loader: The Design
This section introduces the design issues involved with using native libraries, and importing native methods into the Java runtime environment.

The Loader
The first step is to design the Loader class, which will be used to load and execute Java applications. Before designing the native methods that will actually carry out this task, we must declare a skeleton Java class, and generate its header and stub files:

public class Loader {
static {
System.loadLibrary("Loader");
}
public native void execute( Class c , String args[] );
}

The statement enclosed within the static block is simply an initializer. It will be called only once, when the interpreter first encounters a reference to the Loader class. Its sole purpose is to link the dynamic native library to the Java runtime environment. The next line declares a method that takes a class reference, and an array of strings. The native keyword in the method declaration tells the compiler that this method will be implemented by the native dynamic library.

Generating Header and Stub Files
The next step is to generate header and stub files from the declaration of the Loader class described in the previous sections. Header and stub files are partial C header and source files, needed to create a dynamic library that contains our native execute function.

The Java Development Kit (JDK) includes a tool called javah that is used for generating header and stub files as follows:

javah Loader /* Generates Loader.h */
javah -stubs Loader /* Creates Loader.c */

The generated header file contains the declaration of the execute function previously declared in Loader.Java as shown below:

extern void Loader_execute(struct HLoader *,
struct Hjava_lang_Class *,
HArrayOfString *);

This C function prototype declares a function called Loader_execute() that takes three parameters: a reference to a Loader class, a reference to a struct (or a record) that contains the class definition for the application class, and an array of strings that will be used as command line arguments for the application.

Writing Native Methods
The header and stub files are machine generated and they should not be edited. A third file is needed to hold the actual definition of the Loader_execute function. The name of this module is arbitrary, we called it libLoader.c. This module will contain the declaration of the native method(s) of our library:

#include "Loader.h"

void Loader_execute(struct HLoader *this,
struct Hjava_lang_Class *class,
HArrayOfString *args ) {
execute_Java_static_method(0, unhand(class), "main",
"([Ljava/lang/String;)V", args);
}

The execute_Java_static_method is part of the standard Java native library, and has been included through the Loader header file. The second argument is a reference to the class definition of the new application. This is followed by the name of the function to be invoked, main in this case. The fourth parameter defines the signature of the method, followed by the command line arguments constructed earlier.

Putting it All Together
Before the Loader class can be put to work, the native library must be compiled and the resulting dynamic link library must be placed in a directory that is specified in the LD_LIBRARY_PATH environment variable. The example below has been tested with the GNU C-compiler version 2.7.2:

gcc -c -G Loader.c
gcc -o libLoader.so -G Loader.o libLoader.c

The -o option specifies the output file name. This name can be anything, as long as it matches the filename used in the static initialization block of the Loader class. We chose the .so extension to follow UNIX standards; Window 95 and NT implementations should use DLL.

Using the Loader
This section discusses two advantages offered by the approach described in the previous sections. First, it offers control over the system-wide Security Manager, and second, it allows the programmer to begin the execution of an application at any arbitrary static function, other than main.

Controlling the Security Manager
Applications may install a user defined security manager by calling the System.setSecurityManager() method. The only restriction is that this method can only be called once during the lifetime of the application. To enforce certain security restrictions on applications, the Shell class should set the system-wide security manager. Because applications invoked by the Shell are running in the same environment, they must obey the security restrictions imposed by this manager class. If the new application attempts to reset the security manager, it will receive a SecurityException and will terminate.

Starting at MyMain
One of the advantages of the native loader is that with a small modification, applications can be started at arbitrary execution points. In the listing of the libLoader.c module, the third parameter passed into the execute_Java_static_method() was the name of the function, where the execution should begin. Our example can easily be extended to take a variable string argument that specifies the entry point for the execution. The following code fragment illustrates the native Java declaration of this function:

public native void execute( Class c, String entry , String args[] );
public void execute( Class c , String args[] ) {
execute( c, "main" , args );
}

Conclusion
In this paper, we presented a new approach for loading and executing Java applications from already running Java programs. Because applications must be invoked using their static main() method, we used native code to implement this procedure. Extending the native loader allows the programmer to enforce system-wide security restrictions over all applications running in the system.

In [1] we discussed the general design of including Java applets into applications. This article deals with launching Java applications from within applications. In further study, we are planning to generalize this approach to include Applets and stand-alone Applications into Java Applets.

Related Publications

  1. Sashi Lazar and Deepinder Sidhu, "Using Applets in Java Application," Java Developer's Journal, Volume 1, Issue 2, 1996.
  2. Sashi Lazar, "Writing Java Applications for Dynamic Content Handling," Java Developer's Journal, Volume 1, Issue 1, 1996.
  3. Laura Lemay and Charles L. Perkins, "Teach Yourself Java in 21 days". Sams Net Publishing, 1996
  4. Ed Tittlel and Mark Gaither, "60 Minute Guide to Java". Internet World. IDG Books Worldwide, Inc. 1995
  5. Sashi Lazar, "Building AWT Applications using Java". Independent research paper, Department of Computer Science, University of Maryland Baltimore County. March 1996.
  6. Sun Microsystems, "The Java Language Specification". Sun Microsystems, product manual included in JDK version 1.0. Sun Microsystems 1995.
  7. James Gosling and Henry McGilton. "The Java Language Overview: A White Paper". Sun Microsystems Technical Report, May 1995.
This research was supported in part by the Maryland Center for Telecommunications Research (MCTR) at the University of Maryland Baltimore County with funds received from IBM, Sprint, Sun Microsystems, and the Department of Defense. The views and conclusions contained in this document are those of the authors and should not be interpreted as representing the official policies, either expressed or implied, of any sponsor of MCTR.

About the Authors
Sashi Lazar is working on his Ph.D. in Computer Science at the University of Maryland, Baltimore County. He is currently with the Maryland Center for Telecommunication Research sponsored by Sun Microsystems. His work is tightly coupled with two other related topics: Internet security and agent based knowledge retrieval. As part of his research, he is responsible for all Java applications, security, and protocol design.

Dr. Sidhu is a Professor with the Computer Science and Electrical Engineering Department of the University of Maryland Baltimore County (UMBC). He is also the Director of the Maryland Center for Telecommunications Research (MCTR) at UMBC. Dr. Sidhu received his Ph.D. degree in Computer Science and Theoretical Physics from the State University.

 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.