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

By necessity, the operating system loads each VM into its own separate process slot. Consequently, each VM is forced to duplicate the other's initialization efforts and resource allocations. Specifically, each VM is required to load the JDK core classes into their respective heap spaces. Running several Java applications in the same VM would eliminate this duplication of effort. There's an analogy in the world of operating systems.

System engineers have long recognized that portions of statically linked executables contain read-only text that's potentially shareable. They knew that certain performance benefits could be realized if processes could share this text. This knowledge motivated them to develop shared-text libraries. These libraries are loaded only once and are dynamically linked to executables. Nowadays, most operating systems support the concept of a shared text library (or DLL - dynamic-link library). For example, dynamically linked UNIX processes make references to the shared library libc.so. The OS ensures that these processes share the text portion of that library.

By introducing shared libraries, system engineers reduced the overall requirements for system memory. One side effect: the scheme also places less stress on the IO channels and other system resources. This example hints at performance benefits that might be available if you could run several Java applications in the same VM.

Returning to the original thread of discussion and the question, "How do you launch multiple applications?", I suggested a way you might easily build this capability. Even though I was confident that my post was correct, I had never actually built an application launcher. A few seconds after I hit the send button I decided to stick around and read my own advice. I read my posting and thought, "Is it really that easy? Maybe I'd better check out my own advice." What follows are the lessons I learned as I "ate my own dog food."

What I'm Looking For
I'd like to have the flexibility to run several Java 2 applications concurrently in the same JVM. These applications need to function in a secure environment that isolates them from each other to the greatest extent possible. The use of this multiple application launcher (MAL) should be transparent to the application. Finally, all applications should be able to share common read-only resources. Let's start by investigating the current launch technology to see what we can learn and possibly reuse.

Launching a Java Application
MAL needs to mimic the behavior of the standard application launcher. I began my voyage of discovery by reviewing the way a Java application is loaded and then executed. I quickly discovered that starting up a VM initiates a complex sequence of events. Once the initialization sequence has completed, I'm left with a VM that's ready to load a Java class and invoke a standard entry point method. (The complete source code for this article can be downloaded below.)

By default the first optional parameter found on the command line specifies the Java class that implements the entry-point method. The launcher uses the java.class.path property to find and then load this class. Reflection is used to find the entry-point method. Since it's well documented, the method needs to be declared as public static void main(String[] args). Once it's loaded and a reference to the method main has been resolved, the launcher will invoke that method. The VM will continue to function until System.exit() or Runtime.halt() is called. The launcher is the most likely candidate to make that call.

From this description, I started to see the first set of problems that needed to be addressed. The default launcher required the application's classes to be on the system's classpath. If I don't plan on shutting down the VM, I'll need to know about every application that will be run so I can configure the classpath before the VM is started. If I do introduce a new application, I'll be required to shut down the VM, reset the classpath, and then restart the VM. But the original post was about running applications in a server environment. Forcing a VM shutdown in this environment would negate the reason for using MAL.

There's another related problem. The consequence of being able to use only a single classpath is that MAL can't run different versions of the same application. There seems to be no practical way to overcome this difficulty using a single classpath.

Both of these scenarios point to a new requirement. I should be able to dynamically extend the VM's classpath in a secure manner. I need a way to isolate these "on-demand" extensions of the classpath to the launching of the application in question. Since this discussion is centered on classpath and class loading, let's turn our attention to the Java 2 ClassLoader model.

Java 2 ClassLoaders (Part 1)
Java 2 introduced a new class-loading architecture, commonly known as the delegation model. At its basis is the notion that ClassLoaders can be chained. In this chain, every ClassLoader has a parent and a ClassLoader's parent will be given the first chance to load a class. The only exception to this rule is the bootstrap ClassLoader. It's implemented in the VM and has no parent. One other important rule: a child can delegate to its parent but a parent can't delegate to any of its children. Consequently, a ClassLoader can look for a class in its parent but not in any of its children.

In adapting the JVM to the delegation model, three new ClassLoaders were introduced (see Figure 1). The first ClassLoader to be created is the bootstrap one. As previously stated, it's implemented in the VM and has no parent. Its role is to load core JDK classes found in the JAR files in the lib subdirectory of the JDK distribution tree (or set with the -Xbootclasspath parameter). The extension ClassLoader, which is written entirely in Java, loads from the directories specified by the system property java.ext.dirs. The default value for this property is the lib/ext subdirectory found in the JDK distribution. Its role is to load classes that extend the capability of the core JDK distribution. As shown in Figure 1, the bootstrap ClassLoader is the parent of the extension ClassLoader. Finally, there's the application ClassLoader whose parent is the extension ClassLoader. Its role is to read classes from the system classpath (the property name is java.class.path).

Figure 1
Figure 1

The delegation model causes core JDK classes to be loaded in isolation from the extension classes, which are in turn loaded in isolation from application classes. This is a great discovery, as it would appear that I could use ClassLoaders to isolate an application's classes. In addition, constructing a ClassLoader on demand will allow me to extend the classpath on demand. Now the question is: Do I buy or do I build a ClassLoader? I much prefer to buy, so let's see what the JDK has to offer in the way of ClassLoaders.

Java 2 ClassLoaders (Part 2)
At the top of the ClassLoader hierarchy sits the abstract class java.lang.ClassLoader. It contains all the behavior needed to convert an array of bytes into an instance of a class. It has one known subclass,java.security.SecureClassLoader. The SecureClassLoader adds support for the interactions with CodeSource, ProtectedDomain, and SecurityManager. A few of the services they provide include ensuring that class files are loaded by trusted sources only and that the bytecode has not been tampered with. SecureClassLoader has one known subclass, URLClassLoader. Figure 2 provides the Javadoc for this class.

Figure 2
Figure 2

This is great; the JDK comes with a ClassLoader that suits my needs. I can use the URLClassLoader to dynamically extend the system's classpath and, in the same stroke, isolate each application from any others that may be running. To unload an application I can simply unload the ClassLoader. In doing so, I cause no harm to other applications that may be running. Now that I have found a suitable ClassLoader, I can take the first step toward creating an abstraction of a Java application.

The Application Class
Let's start this exercise by writing down a description of a Java application. The classical Java application is a collection of one or more Java classes that cooperate to carry out some important function. As was previously alluded to, the process of launching a Java application can be broken down into four steps:

  1. Finding, then loading, the class that implements the standard entry point, main
  2. Finding, then invoking, the main method in the previously mentioned class
  3. Waiting for the application to complete
  4. Catching and reporting on any exceptions that the application lets slip through
This list specifies the behaviors that need to be implemented. Let's implement them and the supporting state one step at a time into a class called Application. The first step states that the target class needs to be located and loaded. The code snippet demonstrates the technique commonly used to dynamically load a class.

Class targetClass = Class.forName(entryPointClassName);

A quick look at the Javadocs reveals that the method forName() loads the target class using the application ClassLoader. But I already know that none of our application classes will be on the application classpath. This call will throw a ClassNotFoundException. Okay, I've written only one line of code and already there's a problem, great! Let's press onward. Digging a little further into the Javadoc reveals that forName has been overloaded in the JDK 1.2. The new signature is forName(String className, boolean runStaticInitializer, ClassLoader classloader). This method finds the class named by className using ClassLoader. After the class is loaded, the Boolean determines if the static initializer should be executed. Since each application will have its own ClassLoader, using the new method signature allows us to specify it instead of using the system ClassLoader. Add a field ClassLoader and the supporting accessors to the application. The new code fragment solves the classloading problem.

Class targetClass = Class.forName( entryPointClassName,
true, this.getClassLoader());

I'll see how to set up the ClassLoader later on. For now, I can progress to step two, the discovery and invocation of the main method.

Following the loading of the main class, reflection is used to find the main method. The code for resolveMain() can be found in Listing 1. The JDK tools documentation specifies that the main method should be declared static and public. A check of the method modifier flags confirms that the correct method has been found.

The next step is to invoke the main method. Now, if I invoke main right away, my thread would be tied up running the application and I wouldn't be able to launch another application. I can avoid all this by executing each application in its own thread. Semantically, an application is a runnable entity, so it makes sense to have the application implement the Runnable interface. The Runnable interface requires that I implement a run method. If the main method is invoked in the run method, the main thread will be free to launch other applications.

Next question: What happens if the application throws an exception? The run method doesn't include any support for this, but it's actually a good thing because any application that throws an exception would force the launcher to handle it immediately. A more flexible approach is to have the application catch and store any exception that has been thrown. The launcher can then deal with the exception on its own terms. You can find the run method in Listing 1.

The run method includes behavior to help the application keep track of the running state. There are several ways I can do this. I can ask the thread if it's still alive. I don't care if the thread is running, I care if the application is running. Though one implies the other, it still seems more sensible to ask the application if it's running. The monitor would have to ask the application for the thread and I don't feel comfortable exporting internal state unless I have to. In this case, there seems to be no need to export the thread from the application. Thus, I've added a Boolean running to the application. Though access to and the setting of Booleans is guaranteed to be atomic, I still use synchronized to normalize all access to running. This becomes more important later on when setRunning starts to play an important role in thread control. When the run method ends, I always want running to be set to false. Delegating that method call to a finally block ensures that this always happens.

Let's give the responsibility for launch to the application. Since all the launch data is contained in the application, it only makes sense to put the launch method there. Again, the launch method is shown in Listing 1.

The launch method accepts a string array as arguments to be passed to the main method. The problem is, the run method doesn't accept any arguments. Adding an args field to the application solves this problem. The method goes on to define a thread for the application. After setting the context ClassLoader (to be explained later), it calls start() and the application is finally running. Now I'm left with implementing the last two steps.

First, I need the main thread to wait for the application to complete before letting it exit. Second, I need to report on any exceptions that may have been thrown and not caught by the application. These steps will be delegated to the class that created the application and then called the launch method. The private method setRunning (see Listing 1) sets the value for the field. If the value is set to false, it makes a call to notifyAll(). This releases any thread that may be blocked in the waitFor() method.

The Context ClassLoader
In the launch method, I took the mysterious step of setting the thread's context ClassLoader. Now I'll discuss how this fits into the delegation model and subsequently MAL. Recall from our discussion of ClassLoader chaining that a parent couldn't see classes in a child ClassLoader. It turns out that this restriction would be a showstopper if it were not for the context ClassLoader.

Why did I load each application in its own ClassLoader? This isolates the application and although this isolation is desired, it can also be a hindrance. Consider the case in which a class in a parent ClassLoader references a class's child. For example, I may use a component such as an O/R mapping tool loaded on the system classpath to persist an application class. By design, the applications classes are not visible to the O/R mapping tool. Consequently, attempting to reference one will cause a ClassNotFoundException to be thrown. I also must be careful if I add the tool to the application's classpath, as the ClassLoader plays a role in object identity. The same classes loaded from the same source into two different ClassLoaders will be considered different classes. Performing an operation in the wrong ClassLoader may cause a (terribly confusing) ClassNotFoundException to be thrown (or worse).

Each of these scenarios suggests that I need something more: a ClassLoader that functions within the application's context. It's not often that I get to use the two terms added flexibility and added security in a positive way in the same sentence but that's exactly what the Java 2 class loading model offers. By adding the field contextClassLoader and appropriate accessors to the class thread, the designers help support the isolation (and hence security) I require without breaking dynamic class loading schemes.

By default every new thread takes on the contextClassLoader of the thread it was created in. In my case, the contextClassLoader of the thread that creates an application thread is the application ClassLoader. But the only ClassLoader that knows how to load an application's classes is the application's ClassLoader. If any component needs to load an Application class, it will need access to the application ClassLoader. Considering that the most likely thread to trigger the loading of an Application class will be the one that was created by the application, this works out perfectly. If you inspect the method jdkJava2Style in TestComponent (see Listing 2), you'll notice that it passes the context ClassLoader from the current thread into the forName method. This allows forName to load the class using the application ClassLoader. In effect, you've given the parent a controlled peek into one of its children.

Completing the Isolation of the Application's Execution Environment
The constructor for a new URLClassLoader takes an array of java.net.URL as an argument. The classpath for the ClassLoader is derived from this URL array. Note that once instantiated, the classpath for a URLClassLoader can't be changed. Since the class URL is instrumental in this application, it warrants closer scrutiny.

The constructor for the URL class accepts a string representation of the URL. The salient point here is that URLClassLoader assumes that the URL points to a directory or a JAR file. All paths ending with a "/" are assumed to be a directories. All others are assumed to be JAR files.

The constructor for an application uses a URL array to define the classpath for its ClassLoader. By placing the responsibility to construct an array of well-formed URLs on the caller, I haven't imposed any restrictions on how the information is to be collected.

Finally, any application that calls System.exit() or Runtime.halt() will cause the VM to exit. Clearly, I need some way to prevent this from happening. Fortunately, before the main body of these methods is executed, it checks with the SecurityManager to ensure that the caller has permission to make that call. Next I'll investigate how how to employ the SecurityManager to prevent applications from shutting down the VM.

The Java 2 Security Model
The Java 2 security model is a large, complex topic that falls well outside the scope of this article. I'll introduce only a few relevant portions of the subject.

As of the JDK 1.2, code must be granted permission to access a system resource. To ensure permission has been granted, JDK classes call the check methods found in the class java.lang.SecurityManager. Looking deeper into the documentation I found the runtime permission, exitVM, that controls VM shutdown. Wow, can I solve this problem by configuring this runtime permission? Well, if I make this change, neither the application nor MAL would be able to call System.exit(). I need to do something else. Again on the buy/build trail, I looked through the JDK to see what I might learn or reuse. This time, my search failed to turn up a suitable candidate, so that leaves the build option.

Constructing a SecurityManager
In the documentation for SecurityManager is a description for a checkExit method. It checks to see if the code has been granted the exitVM permission. If I override this method, I can check to see whether or not its MAL or an application made the exit call. The implementation of my specialization of SecurityManager can be seen in Listing 3.

MAL makes a call to System.setSecurityManager in a static initializer to install the specialized SecurityManager. There still are some interesting side effects that travel with this solution. For instance, once installed, a SecurityManager can't be replaced. That's good because I don't want an application to install its own SecurityManager. However, it's also bad because if any application does try to install a SecurityManager, it will fail. Could this be one type of application that MAL may not be able to deal with?

Running a Test Application
I crafted a small test application that consists of a component and an application. The component defines the interface TestComponentInterface and class TestComponent. The application contains a single class named TestApplication1, which implements TestComponentInterface. TestComponent uses reflection to create a new instance of TestApplication1 and then calls the method defined in TestComponent- Interface.

TestComponent defines two methods, jdk11Style() and jdkJava2Style(). As expected, the former uses the single argument Class.forName() and the latter uses the three-argument Class.forName() method. Each of these methods accepts two parameters: one specifies the class implementing TestComponentInterface, and the other is used as an argument for TestComponentInterface.run(). A test application satisfying these requirements was crafted. The test calls both jdk11Style() and jdkJava2Style(). Running the tests requires that TestComponent be included in TestApplication's classpath. Both jdk11Style() and jdkJava2Style() run as expected. I haven't yet justified the need for the more complex three-argument Class.forName() method. This will be justified later on.

The first time I ran a test, I encountered a SecurityException (see Listing 4). Okay, I'm only overriding checkExit so what's the problem? I replaced MultiApplicationSecurityManager with SecurityManager and reran the tests. Same result. Okay, this helps because I now know that my security manager is not the source of the problem.

As it turns out, by default, a SecurityManager is not installed. When this happens, all check methods pass. If a SecurityManager is installed, its default behavior is to grant the permission specified in the files java.home/lib/security/ java.policy and user.home/.java.policy. Well, there was no .java.policy file in my user.home directory and the former file grants only a limited number of permissions. To fix the problem, I used the policytool to create a .java.policy file (see Listing 5). Now my tests run as expected.

To see how much memory can be saved, let's first see how much memory is normally consumed using the traditional launcher. Four tests were defined. The first three tests execute one, two, and three instances of the test application, each in its own VM. The last test executes three applications in the same VM using the MAL. Each test was run three times in the JDK 1.3.0 on my 128MB W2K laptop. I configured the system tool perfmon to monitor the free memory counter.

Since I know that the OS shares resources, the tests needed to account for the effects of sharing. The first test established a baseline memory usage. Tests 2 and 3 provided the incremental amount of memory required to support the running of a second and third instance of the VM. Table 1 illustrates the memory requirement of each test.

Table 1
Table 1

Figure 3 provides a graph of the free memory counter over time. The graph displays the results of two different runs. I first ran three applications, each in its own VM. After free memory had returned to prerun levels, I then ran three applications all in the same VM. Each downward step is the result of a new VM grabbing a chunk of memory. It's interesting to note that in the first run, memory is consumed in three steps (one for each VM start) and recovered in four. In addition, the memory requirements of the first VM are greater than that of the two successive VMs. As each VM halts, memory is returned to the OS. After all the VMs have exited, the OS is now free to recover the shared text segment. Once this is completed, free memory returns to prerun levels.

Figure 3
Figure 3

In the latter run, we can see the expected decrease in demand for memory. Once again, witness the OS recovering the shared text segment as a separate step.

Though the technique does save memory, the results are not as big as I'd hoped. But there's another component to consider, time. By having a VM prestarted, it should be much quicker to launch applications. I'll leave the verification of that assertion as an exercise for the reader. I'll spend the remainder of this article spinning MAL through a second iteration.

Sharing Components
Recall that during the discussion of the contextClassLoader, the use of third-party components such as O/R mapping tools was brought up. It's common for these tools to use a pluggable integration strategy. In the current implementation of the launcher, I must choose between placing third-party components on the system classpath or on the application classpath. If I choose the system classpath, then applications can share it, but I won't be able to use multiple versions of the component nor will I be able to dynamically define it. If I wish to dynamically define the component, I need to place it on the application classpath. In making this choice, I've eliminated the possibility of sharing the component. Can I have my cake and eat it too? Can I dynamically define a sharable component? Stay tuned.

First let's consider some of the common elements and differences between a component and an application. An application is runnable; a component is not. An application must support the standard entry point. A component defines its own entry points, which may or may not be set according to a standard. An application is launched; a component is loaded. Applications and components can be loaded into their own ClassLoader and each requires its own classpath entry.

I'll use this initial set of observations to see if I can motivate a refactoring of MAL to include a Component class. Since the application looks like a specialization of the component, I'll start by having the application extend the component. As before, the application still implements Runnable and it maintains the behavior to monitor and control threading. All of the classpath and class-loading behavior can be pushed up into the component superclass. Once I finish the refactoring, I can focus on what new behavior needs to be added.

What's new in this version of the launcher is that applications and components can be dependent on other components. To gain a deeper understanding of what this means, consider the following example: I have an application, A, that uses a third-party O/R mapping tool, C. If I load both A and C in different ClassLoaders, I need to somehow ensure that A and C can see each other's classes. Chaining the ClassLoader containing A to the one containing C can solve the first half of the problem. If you look to the constructor in the component and the application, you can see an extra parameter has been added. The body of the constructor in the component contains the extra logic needed to create a ClassLoader with a parent other than the system ClassLoader.

To make A visible to C, I need to consider how C interacts with A. The interactions between TestComponent and TestApplication represent a typical interaction. In the jdkStyle methods, TestComponent tries to dynamically load TestApplication. In the jdk11Style method, first the component's, then the system's, ClassLoader is used. But I know that these ClassLoaders aren't visible to TestComponent so the method will throw a ClassNotFoundException (see Listing 6). In the jdkJava2Style method, the first ClassLoader contacted is the thread's context ClassLoader. In this case, the thread was started in Application.launch(). If we look back at that method, we can see that the thread's context ClassLoader is set to that of the applications'. Since the jdkJava2Style method uses the threads context ClassLoader, our problem is solved - C is visible to A. The jdkJava2Style method runs without incident.

The ComponentCache
The ComponentCache is a singleton that defines a repository for components. It's necessary to "pin" components into memory for the period of time when they're not referenced by anything else. The ComponentCache also acts as a "database" of components. This means that once loaded, the component will remain in memory for the lifetime of the VM. This brings up a number of interesting GC-related questions, which I won't talk about here.

Further Refinements
A number of further refinements could be made to improve the launcher. The first would be to use a ThreadGroup to contain each application's threads into a single bundle. Second, the struggle with the SecurityManager clearly demonstrated that static and singleton implementations can wreak havoc with multithreaded applications. The System class contains a number of statics and singletons that reinforce assertion. Although I use singletons (as evidenced by ComponentCache), I tend to view them with a degree of skepticism. Is there something missing from the design? This is the first question that pops into my mind when I see one.

The System class allows access to a number of potentially nonshareable resources. Is there a design element that's missing? It's clear that MAL places different strains on the JDK than those originating from the traditional launcher. In a sense, MAL is trying to be an execution context. In this role, MAL falls short in several key areas.

The first shortcoming can be seen with the SecurityManager implementation. Though the Java 2 security model is much improved, it still doesn't totally eliminate the need for a customized SecurityManager. Applications that attempt to install their own SecurityManager won't be able to do so. I could have implemented a more complex custom SecurityManager, but this wouldn't have been necessary if I had an execution context in which I could install a regular SecurityManager. The next shortcoming is with System.properties.

It's quite common for applications and components to set properties in the System class. You could argue that if developers take care when naming properties, name-space collisions could be avoided. What happens when you wish to run two different versions of the same application? Different versions of the same application will most likely use the same property names. Again, I could provide an implementation that segregates properties by application, but a better solution would be to have these properties isolated into separate execution contexts.

Next, I'll talk about System.in, System.out, and System.err. I find exceptions difficult enough to deal with without having the added challenge of collating the output from several different applications. It would be much better if each application had its own console. Yet another problem: What if an application tries to install its own in, out, or err class? This problem is not so easily solved, as it implies MAL may have to make changes to a system.

I could rewrite the System class to remove some of this contention, but the Java licensing agreement (for good reasons) would prevent me from distributing these modifications. Most vendors that do require changes to base classes (some profilers modify Object) use a bytecode injection to distribute the change without violating the license agreement. This is a fairly complex technique that requires the use of a specialized ClassLoader. These are only a few of the traps of using statics and singletons in a multithreaded environment.

Summary
I've shown how you can construct a launcher to execute multiple applications. As demonstrated, this technique did reduce the overall demand for system resources. In introducing this technique, I've also introduced a practical application of the Java 2 ClassLoader model. This model is gaining wider acceptance as demonstrated by J2EE container implementers and, most recently, by IBM's new IDE project, Eclipse. Eclipse will load each plug-in into a separate ClassLoader. I've also learned to be more careful when posting advice to a discussion group.

Author Bio
Kirk Pepperdine has more than 10 years of experience in OO technologies. In addition to his work in the area of performance tuning, Kirk has focused on building middleware for distributed applications. [email protected]

	


Listing 1

package com.jpt.mal.application;

import java.lang.reflect.*;
import java.net.*;

public class Application implements Runnable {

  protected URLClassLoader classLoader;
  protected URL[] classPath;
  protected String className;
  protected Object[] args;
  protected Exception applicationException;
  protected boolean running;


  public Application(String aClass, URL[] aClassPath) {
    className = aClass;
    classPath = classPath;
    running = false;
    classLoader = new URLClassLoader(
        classPath,
        Thread.currentThread() .getContextClassLoader());
    args = new Object[1];
  }

  public Exception getApplicationException() {
    return applicationException;
  }

  public void launch(String[] args) {
    if (isRunning()) return;
    this.args[0] = args;
    applicationException = null;
    Thread thread = new Thread( this,
                           "Running " +
                           className);
    thread.setContextClassLoader(this.classLoader);
    thread.start();
    Thread.yield();
  }

  // Runnable targets
	public void run() {

    setRunning(true);
		try {
      this.resolveMain().invoke(null, args);
    } catch (Exception ex) {
      applicationException = ex;
    } finally {
      setRunning(false);
    }
  }

  private Method resolveMain()
    throws ClassNotFoundException,
    NoSuchMethodException {
    Class targetClass = 										this.classLoader.loadClass(className);
    Class[] arg_types = { String[].class };
    Method main = targetClass.getMethod("main", arg_types);
    int mask = Modifier.STATIC ^ Modifier.PUBLIC;
    if ((main.getModifiers() & mask) != mask)
      throw new NoSuchMethodException(
      "Cannot find method public static void main(String[]) in class "+ className);
      return main;
    }

  private synchronized void setRunning(boolean value) {
    this.running = value;
    notifyAll();
  }

  public synchronized boolean isRunning() { return running; }

 public synchronized void waitFor() {
    while (isRunning())
      try {
        wait();
      } catch (InterruptedException ie) {}
  }
}



Listing 2

package com.jpt.testcomp;

public class TestComponent {

  public TestComponent() {}

  public void runJava2Style(String implementation, String arg)
    throws ClassNotFoundException,
    InstantiationException,
    IllegalAccessException {

    Class clazz = Class.forName( implementation, true,
                Thread.currentThread().getContextClassLoader());
    TestComponentInterface tci = (TestComponentInterface)
                             clazz.newInstance();
    tci.run(arg);
  }

  public void runJDK11Style(String implementation, String arg)
    throws ClassNotFoundException,
          InstantiationException,
          IllegalAccessException {
    Class clazz = Class.forName( implementation);
    TestComponentInterface tci = (TestComponentInterface)
                             clazz.newInstance();
    tci.run(arg);
  }
}



Listing 3

public class MultiApplicationSecurityManager extends SecurityManager 
  throws SecurityException {

public void checkExit(int status) {

  if ( Thread.currentThread().getContextClassLoader() !=
     ClassLoader.getSystemClassLoader())
    throw new SecurityException();

  super.checkExit(status);

}



Listing 4

java.security.AccessControlException: access denied (java.lang.Runtime
			Permission createClassLoader)
	at java.security.AccessControlContext.checkPermission(AccessControlContext.
			java:272)
	at java.security.AccessController.checkPermission(AccessController.
			java:399)
	at java.lang.SecurityManager.checkPermission(SecurityManager.java:545)
	at java.lang.SecurityManager.checkCreateClassLoader(SecurityManager.
			java:610)
	at java.lang.ClassLoader.<init>(ClassLoader.java:203)
	at java.security.SecureClassLoader.<init>(SecureClassLoader.java:56)
	at java.net.URLClassLoader.<init>(URLClassLoader.java:82)
	at com.jpt.application.Application.<init>(./src/com/jpt/application
			/Application.java:20)
	at com.jpt.ui.JShell.launch(./src/com/jpt/ui/JShell.java:60)
	at com.jpt.ui.JShell.main(./src/com/jpt/ui/JShell.java:72)
Exception in thread "main" Process terminated with exit code 1



Listing 5

/* AUTOMATICALLY GENERATED ON Sun Feb 03 15:43:44 EST 2002*/
/* DO NOT EDIT */

grant {
 permission java.security.AllPermission;
};


Listing 6

Running JDK 1.1 Style on com.jpt.testapp.TestApplication1
Expected Exception:com.jpt.testapp.TestApplication1java.lang.ClassNotFoundException: com.jpt.testapp.TestApplication1
	at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:297)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:253)Running JDK 1.1 Style on com.jpt.testapp.TestApplication2
	at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:313)
	at java.lang.Class.forName0(Native Method)
	at java.lang.
Expected Exception:com.jpt.testapp.TestApplication2
Class.forName(Class.java:120)
	at com.jpt.testcomp.TestComponent.runJDK11Style(Unknown Source)
	at com.jpt.testapp.TestApplication1.start(Unknown Source)Running Java2 Style oncom.jpt.testapp.TestApplication1
	at com.jpt.testapp.TestApplication1.main(Unknown Source)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.jpt.mal
com.jpt.testapp.TestApplication1:0 sleeping 
.component.Application.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:484)
java.lang.ClassNotFoundException: com.jpt.testapp.TestApplication2Running JDK 1.1 Style on com.jpt.testapp.TestApplication3
	at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
	at java.security.AccessController.doPrivileged(Native Method)
	a
Expected Exception:com.jpt.testapp.TestApplication3
t java.net.URLClassLoader.findClass(URLClassLoader.java:188)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:297)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:253)
	at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:313)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:120)
	at com.jpt.testcomp.TestComponent.runJDK11Style(Unknown Source)
	at com.jpt.testapp.TestApplicaRunning Java2 Style oncom.jpt.testapp.TestApplication2
com.jpt.testapp.TestApplication2:0 sleeping 
tion2.start(Unknown Source)
	at com.jpt.testapp.TestApplication2.main(Unknown Source)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.jpt.mal.component.Application.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:484)
java.lang.ClassNotFoundException: com.jpt.testapp.TestApplication3
	at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:297)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:253)
	at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:313)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:120)
	at com.jpt.testcomp.TestComponent.runJDK11Style(Unknown Source)
	at com.jpt.testapp.TestApplicaRunning Java2 Style oncom.jpt.testapp.TestApplication3
com.jpt.testapp.TestApplication3:0 sleeping 
tion3.start(Unknown Source)
	at com.jpt.testapp.TestApplication3.main(Unknown Source)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.jpt.mal.component.Application.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:484)
com.jpt.testapp.TestApplication1:1 sleeping 
com.jpt.testapp.TestApplication2:1 sleeping 
com.jpt.testapp.TestApplication3:1 sleeping 
com.jpt.testapp.TestApplication1:2 sleeping 
com.jpt.testapp.TestApplication2:2 sleeping 
com.jpt.testapp.TestApplication3:2 sleeping 
com.jpt.testapp.TestApplication1 done!
com.jpt.testapp.TestApplication2 done!
com.jpt.testapp.TestApplication3 done!


Additional Source Files (~ 33.7 KB ~Zip File Format)

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.