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
 

As the popularity of Java increases, many information services departments are embracing Java as the solution to their cross-platform challenges. As this trend progresses, many developers will be faced with the challenge of migrating their Java servers from UNIX to an NT boot-time environment. Administrators tend to take the attitude that since Java is a cross-platform language, this migration should be effortless. Unfortunately, this isn't the case. The thread management of child threads in the current Java Virtual Machine from Microsoft can cause problems for those developers migrating from a UNIX environment. This article explores one workaround for the thread management and garbage collection differences between the Sun and Microsoft JVMs. Microsoft recently agreed to implement a court decision to make their JVM ISO-compliant. It remains to be seen whether this will affect the behavior of Microsoft's proprietary Service class.

Considerable trial and error is associated with the migration of Java server classes to the NT environment. In the UNIX operating system it's possible simply to fork off a Java process in a script that's run at boot time. (The particular script, and the associated operating system messages that need to be caught, depends on the flavor of UNIX involved.) Under the NT operating system no fork is available, so to start a process at boot time, an NT service must be created and registered with the service's control panel. The problem inherent in this approach is that the NT OS doesn't allow fine control over the child process once it's been started. If a Java class utilizes threads, you have to override the stop method of the extended Service class to isolate and stop your threads manually. This task is made more complex because there's no definitive way to isolate your threads from the operating system threads other than their placement on the stack. Consequently, other processes started after your server may interfere with the stopping of your service. Recent versions of the Microsoft JVM have addressed this problem, but to ensure proper garbage collection, manual thread management within services is still necessary.

To create an NT service, you have to create a Java class file that extends the coms.ms.service.Service class provided by Microsoft in its Software Developers Kit for Java. This class is located in C:\SDK\NTService\service.zip in version 1.02 and C:\SDK-Java.202\Bin\jntsvc\service.zip in version 2.02 of the SDK files installed by the Microsoft SDK-Java installer. Do not attempt to unzip this file and examine the Java files. All the documentation you should need to extend the Service class is contained in the SDK-Java documents and in this article. The SDK-Java is available on Microsoft's Web site at www.microsoft.com\java\. This executable should be downloaded and installed on the system that will run the Java service. Be sure to install the Internet Explorer Java support as part of the SDK installation because the support actually installs the Java Virtual Machine. The JVM is necessary because the NT operating system won't try to access the normal Java executable located on the path; instead, it will invoke the jview executable resident within the Microsoft SDK. Microsoft's proprietary compiler and JVM contain custom hooks into the OS that are not part of the ISO standard. It remains to be seen whether these hooks will be deprecated as part of the move toward ISO compliance by Microsoft. The services control panel now defaults to the Microsoft proprietary JVM, called jview.

The Service class of the coms.ms.service package provides methods to allow the NT OS to start, stop and pause a service. However, the JVM doesn't allow the NT services driver to access child threads of the parent class automatically. The ramifications of this are that if you create a wrapper class that extends service, you have to manually instantiate, start, pause and stop any child threads from within the wrapper class itself. Any child threads instantiated or started from within the Java class being wrappered won't be accessible from within the NT services framework. Once a child thread has been instantiated and started, the NT OS no longer knows that the child thread is grouped with its parent.

Any attempts to stop or pause a service that contains child threads without taking this into consideration will result in the OS's returning an error stating that it is unable to stop the service. To avoid this, the wrapper class in its stop method has to isolate and stop any child threads. Note that this is true for wrapper classes, while classes that directly extend the coms.ms.service.Service class will stop, usually without error, in version 2.02 and above. For previous versions, and to ensure proper functionality if wrappering classes are involved, manual thread grouping should still be utilized.

Listing 1 is an example of how you might extend the coms.ms.service.Service class to create a wrapper class that can be registered as an NT service, and will call the public static void main(String[] args) method of your Java class. To use this example code, replace YOURSERVICE with the class name of the application you wish to wrapper.

Once your Java file has been created, it needs to be compiled to bytecode. Once you have a Linking.class, move the Linking.class file to the C:\SDK-Java\NTService\ or C:\SDK-Java.202\Bin\jntsvc\ directory that was created when you ran the Microsoft SDK-Java executable. You now have a wrapper class that can be registered as an NT service.

To ensure that your target system is configured properly so you can register and use the Java service class you've created, follow the steps below.

  1. Make sure there is a local (on hard drive) copy of any classes used or imported by the YOURSERVICE class; NT services aren't started in any particular order, and the networking connections may or may not be functional when the JVM attempts to start the Linking.class.
  2. Place the service.zip file and the path to the tree of classes imported by YOURSELF in the CLASSPATH environmental variable in the system environment settings. (This file is located in C:\SDK\NTService\service.zip in version 1.02 and in C:\SDK-Java. 202\Bin\jntsvc\service.zip in version 2.02 of the SDK.) These settings can be assessed by opening the Control Panels Dialog from the Start menu and double-clicking on the System icon, which will bring up the system parameters. Clicking on the Environment Tab will access both the systemwide and the individual user parameters. You'll need to modify the system CLASSPATH parameter.
  3. (If you're using version 2.02 or higher of the SDK, you can skip this step as the jntsvc registration program will modify the registry for your service.) The last step is to modify the JVM's CLASSPATH variable, because the standard Java CLASSPATH environmental variable isn't recognized by the JVM. The JVM CLASSPATH variable is explicitly defined within the JVM's set of environmental variables. Therefore it's necessary to directly modify the CLASSPATH variable that the JVM places in the system registry as follows:
  • Access the MS-DOS Command Prompt from the Start menu. In the Command Prompt window type regedit - this brings up the system registry.
  • Open the folder HKey_Local_Machine by clicking on the plus (+) sign next to the folder. Open the subfolder software and the JVM folder in the same manner. The path in the bottom left-hand corner of the System Registry Editor window should now read:

    My Computer\HKey_Local_Machine\Software \Microsoft\JavaVM

    Now open the CLASSPATH environmental variable for editing by double-clicking on the small book icon directly left of the word CLASSPATH.

  • Then remove the period at the end of the current entry and add to the current path the path to the class tree that YOURSERVICE imports. Make sure that C:\SDK-Java\NTService\service.zip and C:\SDK-Java\NTService\ are included in the CLASSPATH. Path additions in the NT OS are separated by a ;. Remove the period at the end of the CLASSPATH before making additions, and don't add any unnecessary punctuation marks!
  • Close the System Registry Editor.
The final step in the migration process is to register the service class with the NT Services Control Panel.

Registering the Service
For version 1.02 (this is also explained in the SDK-Java documentation):

  • In the C:\SDK-Java\NTService\i386\ directory run the svcsetup program with the command line svcsetup ServiceName Linking -classpath C:\SDK- Java\NTService\.
  • From the Start menu open the control panels, then the Services Control Panel.
  • Select the ServiceName Service and click on the STARTUP button; select automatic, and allow interaction with the desktop.
  • Select HWprofiles, and select enable.
  • Reboot your computer.
For version 2.02:
  • In the C:\SDK-2.02\Bin\jntsvc\ directory run the jntsvc program with the command line jntsvc /out:ServiceName.exe/main:ServiceName ServiceName.class.
  • Run ServiceName.exe\install to register the Service.
  • Further documentation on use of the jntsvc should be available at C:\SDK-Java.202\Docs\SDKJDoc\def_cerv.htm provided that you installed the SDK to the default root.
  • From the Start menu open the control panels, then the Services Control Panel.
  • Select the ServiceName Service and click on the STARTUP button; select automatic, and allow interaction with the desktop.
  • Select HWprofiles, and select enable.
  • Reboot your computer.
Testing and Evaluation
There are two ways to see whether these procedures have been successful. The first and easiest is to open the Control Panel again and see if the service has started. If so, you've been successful. If not, an error has occurred. You can bring up the Event Viewer for the NT OS by looking under the Start menu's Administrative Tools (common) area and selecting Event Viewer.

Set the Events to the Application log, and refresh the view. This allows you to examine the debug lines from the linking class so you can debug your classes or linking structures. Note: If the Event Viewer Application log says it can't find your class, make sure both CLASSPATH variables are set to include the paths to your linking class and all classes you wish to import.

Many companies are pursuing Java in an attempt to reduce their operating budgets and increase their efficiency. Java is promising cross-platform compatibility. Most of the problems associated with the migration of Java can be attributed to its youth as a language. While developers feel joy about a "write once, run anywhere" ability, it's short-lived when the virtual machines don't conform to the same specifications. While Sun has made - and continues to pursue - true cross-platform compatibility among all JVMs, not all have achieved a workable level of compliance. As a result of litigation, Microsoft is migrating its JVM into compliance with the ISO standard. It remains to be seen, however, whether this migration will affect the necessarily proprietary NT Service installation classes. These differences require developers and IT staff who wish to import their code to Windows to be aware that the "write once, run anywhere" promise of Java is not always applicable in a Windows environment. Until all Java Virtual Machines behave identically, there will continue to be issues of migration for developers and IS departments to face.

About the Author
Jim Barnebee, a registered Sun developer and a Microsoft developer, has been an alpha and beta tester for many Java development environments. He has a BS in information systems science, and has been developing software professionally since 1986. Jim can be reached at [email protected]

	

Listing 1.

import java.lang.*;
import java.io.*;
import java.util.*;
import com.ms.service.Service;
import YOURSERVICE;
public class Linking extends com.ms.service.Service{ 

 YOUR MEMBER VARIABLES{ One of which should be an Object of the type YOURSERVICE}

 YOURSERVICE ys;




 static
    {
 // Uncomment to disable the assassin.  The service will fail to respond
 // in the time specified in the last waithint for the third pause
// event received.  If the assassin is enabled (i.e. this line is 
// commented
 // out, the default), then the service will be forcibly killed.

 Service.disableassassin = true;
    }



 public Linking(){
   System.out.println("Entered Linking");
   YOURSERVICE ys = new YOURSERVICE();
   String argsin[] = new String[1];
   argsin[0] = "-someargument";
   System.out.println("made YS argument string of "+argsin[0]);
   ys.main(argsin);
   System.out.println("called YSmain");
   System.out.println("Sending updated pending status");
        CheckPoint(1000);
   System.out.println("setting Running");
   setRunning(ACCEPT_STOP|ACCEPT_PAUSE_CONTINUE);
   System.out.println("post setRunning in Linking");
 }

protected boolean handleStop (){
 Thread currentT;
 Thread[]  iterT;
 ThreadGroup tg;
 int howmany;


 currentT = Thread.currentThread();
 tg = currentT.getThreadGroup();
 howmany = tg.activeCount();
 iterT = new Thread[howmany];
 tg.enumerate(iterT);
 System.out.println("there are "+howmany+" threads");


 for (int iter=1;iter<4;iter++){
  String name = iterT[iter].getName();
  String is = iterT[iter].toString();
  System.out.println("Attempting to stop thread number "+iter+"called : "+name+" which is
   "+is);
  try{
   iterT[iter].stop();
  }catch (Error e){
   System.out.println("Thread threw error :"+e);
  }
 }
 setStopping(5000);
 System.out.println("dying");
 rt.exit(0);
return super.handleStop();
     }

    protected boolean handlePause (){
  pausecount++;
 if (pausecount == 3)
  {
       System.out.println("pause #3, sleeping for 30 seconds, should be killed in 2+5
	    seconds");
       setPausing(2000);
       try{
    Thread.sleep(30000);
       }
   catch(InterruptedException e){
    System.out.println("interrupted");
       }
  }
  else
  {
       System.out.println("received pause #"+pausecount+", pausing for 2 seconds");
       setPausing(5000);
       try{
    Thread.sleep(2000);
       }
       catch (InterruptedException e){
    System.out.println("interrupted");
       }
       System.out.println("sending paused");
       setPaused();
       System.out.println("sent paused");
  }
  return false;
     }

    protected boolean handleContinue (){
  System.out.println("received continue, continuing after 2 seconds");
  setContinuing(5000);
  try
  {
      Thread.sleep(2000);
 }
  catch (InterruptedException ei)
  {
      System.out.println("interrupted");
  }
  System.out.println("sending running");
  setRunning();
  System.out.println("sent running after continue");
  return false;
    }

    protected boolean handleShutdown (){
  System.out.println("received shutdown, treating as stop");
  return handleStop();
    }

   

protected boolean handleInterrogate (){
  System.out.println("received interrogate");
  setServiceStatus(getServiceStatus());
 System.out.println("sent status for interrogate");

 intcount++;
 if (intcount == 3){
      System.out.println("received 3rd interrogate, stopping self in 5 seconds");
       try{
    Thread.sleep(5000);
       }
       catch (InterruptedException iex){
    System.out.println("interrupted");
       }
       System.out.println("stopping");
       StoyserviceEventHandler(1000);
  }
 return false;
     }
}//end class Linking


 
      
 

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.