Platinum Solutions Corporate Website


Java as Windows Service with Apache Commons Daemon

The answer you entered to the math problem is incorrect.

Recently, I wanted to take a Java command line program and turn it into a Windows Service. There are a number of ways to do this, but I decided to try out the Apache Commons Daemon project (http://commons.apache.org/daemon/).

It is actually very simple to turn your Java Program into a service with Commons Daemon but, if you’re like me, you will very quickly become confused by browsing the project page, so here are some tips.

Procrun
On the Commons Daemon homepage, under the "Platforms" section you’ll see that it says "For win32 platforms use procrun". Procrun is actually an umbrella term for a set of libraries and applications. The applications we are concerned with are called "prunsrv" and "prunmgr".

The "prunsrv" application is the Windows Service binary. It is native windows binary that runs as a windows service and starts up an embedded JVM to run your Java code in (it can run in other modes, but "embedded JVM" was all I cared about). The "prunmgr" is a GUI application to start/stop and configure the service. It can also be run in the system tray where its icon will show the current state of the service (running or stopped). The Commons Daemon homepage has a link in its left navigation called "Procrun" which has documentation for these two applications.

So where do you download the "prunsrv" and "prunmgr" binaries? That is the confusing part! Your first instinct will be to click the "Download" link on the Commons Daemon home page, but that only has a download for UNIX. Next, you’ll click the "Procrun" link, but that only has documentation. Last, you’ll click on the "Native binaries" link, but that page has nothing but a link back to the documentation.

After a bit of googling, and sifting through a lot of "where do I download procrun?" postings, I found that the Commons Daemon project does not provide binaries for these applications. The only binaries available are from the Tomcat project. Tomcat comes with 2 windows binaries: tomcat5.exe and tomcat5w.exe. It turns out that these are "prunsrv" and "prunmgr" respectively!

You can download the binaries without downloading Tomcat (or to get 64 bit versions) here:
http://svn.apache.org/repos/asf/tomcat/connectors/trunk/procrun/bin/

The Daemon Java Interface is not used!
The next thing that confused me was that the Commons Daemon project has a Java API, that revolves around an interface called "Daemon". This interface has methods one would expect in a service such as start/stop and initialize/destroy methods.

My assumption was that you needed to implement the Daemon interface to have Procrun interact with your java application. I spent a lot of time trying to figure out how this worked (the Procrun documentation makes no mention of this interface). Well, it turns out that this interface is not used at all by the Procrun application. (It might be used by the UNIX daemon binary, I’m not sure.) If your only goal is to create a Windows Service, don’t bother with the Daemon Java interface.

Writing a Service
Procrun will call a static method that takes a String array as its only argument to start your service. By default, Procrun will look for and call static void main(String[] args) as the startup method, but you can configure any method name. Procrun will also call a static method that takes a String array as its only argument to stop your service. Again, it will default to static void main(String[] args), but this is also configurable.

The idea is that your "main" method will determine whether the service is starting or stopping based on the arguments. You can specify different static methods for start and stop, but they still must have a single String array for its argument.

(Note: Another thing that had confused me was that you will get the message "Static method 'void main(String[])' not found" if Procrun can’t find your start/stop method, even if you specify a method name other than “main”)

The service will be considered in "running" state for as long as your "start" method blocks. Procrun will run your start method in a worker thread, and monitor that thread for your method to return.

Sample Java Service: "MyService"
I created a sample Java service called "MyService", which is a simple class that just logs a message to standard out ever minute.

Below is a simplified sequence diagram of how Procrun starts and stops the MyService class.

Below is the code for the MyService class:


package com.platinumsolutions;

/**
 * Simple service class that writes a
 * message to standard out every minute.
 */
public class MyService {

   /**
    * Single static instance of the service class
    */
   private static MyService 
       serviceInstance = new MyService();
	
   /**
    * Static method called by prunsrv to start/stop
    * the service.  Pass the argument "start"
    * to start the service, and pass "stop" to
    * stop the service.
    */
   public static void windowsService(String args[]) {
      String cmd = "start";
      if(args.length > 0) {
         cmd = args[0];
      }
	
      if("start".equals(cmd)) {
         serviceInstance.start();
      }
      else {
         serviceInstance.stop();
      }
   }

   /**
    * Flag to know if this service
    * instance has been stopped.
    */
   private boolean stopped = false;
	
	
   /**
    * Start this service instance
    */
   public void start() {
	
      stopped = false;
		
      System.out.println("My Service Started "
                         + new java.util.Date());
		
      while(!stopped) {
         System.out.println("My Service Executing "
                             + new java.util.Date());
         synchronized(this) {
            try {
               this.wait(60000);  // wait 1 minute
            }
            catch(InterruptedException ie){}
         }
      }
		
      System.out.println("My Service Finished "
                          + new java.util.Date());
   }
	
   /**
    * Stop this service instance
    */
   public void stop() {
      stopped = true;
      synchronized(this) {
         this.notify();
      }
   }
}

Deploying the Sample Java Service
I will be using the following folder hierarchy for my sample service:


C:\MyService
    \bin
        \myService.exe
        \myServicew.exe
    \classes
        \com\platinumsolutions\MyService.class
    \logs

You’ll notice that I renamed the prunsrv and prunmgr executables (originally called tomcat5.exe and tomcat5w.exe) to "myService.exe" and "myServicew.exe" respectively. This is simply so that when I look in the running process list for my service, it will be listed as "myService.exe".

To deploy my service, I open a Command Prompt to the "C:\MyService\bin" folder and run the following command:


C:\MyService\bin> myService.exe //IS//MyService --Install=C:\MyService\bin\myService.exe --Description="My Java Service" --Jvm=auto --Classpath=C:\MyService\classes --StartMode=jvm --StartClass=com.platinumsolutions.MyService --StartMethod=windowsService --StartParams=start --StopMode=jvm --StopClass=com.platinumsolutions.MyService --StopMethod=windowsService --StopParams=stop --LogPath=C:\MyService\logs --StdOutput=auto --StdError=auto

There is an exhaustive list of command line parameters documented at http://commons.apache.org/daemon/procrun.html

Here is a description of command line arguments I am using for the sample service

Parameter Value Description
//IS// MyService "IS" is "Install Service", and MyService is the service name. (The name that will be listed in the Window Control Panel Service Administration)
--Install C:\MyService\bin\myService.exe The full path to the prunsrv executable to install as the service binary
--Description "My Java Service" Descriptive name displayed in the Window Control Panel Service Administration.
--Jvm auto This specifies the jvm.dll to use for the embedded JVM. By specifying "auto", it will automatically locate the Java instance installed on the machine.
--Classpath C:\MyService\classes The classpath for the JVM environment. Include any Jar files here that your service depends on.
--StartMode jvm This specifies that we want to use an embedded JVM to run our service.
--StartClass com.platinumsolutions.MyService The sample Java service class.
--StartMethod windowsService The static method used to start the service (this static method must take a String array as an argument)
--StartParams start String values to pass to the StartMethod. The sample Java service expects a single parameter of "start" to start the service.
--StopMode jvm This specifies that we want to use an embedded JVM to run our service.
--StopClass com.platinumsolutions.MyService The sample Java service class.
--StopMethod windowsService The static method used to stop the service (this static method must take a String array as an argument)
--StopParams stop String values to pass to the StopMethod. The sample Java service expects a single parameter of "stop" to stop the service.
--LogPath C:\MyService\logs Specify the folder where prunsvr will write *it’s* log files to, including the StdOutput and StdError redirect logs.
--StdOutput auto The name of the log file to create where the Standard Out will be redirected to. By specifying "auto", the file will be called "stdout" with a date/time stamp appended to it.
--StdError auto The name of the log file to create where the Standard Error will be redirected to. By specifying "auto", the file will be called "stderr" with a date/time stamp appended to it.

Monitor the sample service with prunmgr
By default the service will be installed with a startup mode of "Manual", so it is not running after it is deployed. It can be started via the Window's Control Panel's Administer Services screen, or we can use Procrun's prunmgr.

As previously mentioned, I renamed prunmgr (which was originally named tomcat5w.exe) to "myServicew.exe". Running myServicew.exe will bring up a GUI screen with a start/stop button as well as a number of tabs to modify the configuration. Just about every value that you can specify on the command line when deploying can be changed after deployment via this GUI.

One nice feature of prunmgr is the ability to have it run in the system tray. To run prunmgr in the system tray, use the command line parameter "//MS//" like so:
C:\MyService\bin> myServicew.exe //MS//

The system tray icon will have a red box on it if the service is stopped, and a green arrow if it is running. You can start/stop the service by right clicking the system tray icon and picking start/stop from the context menu.

In Conclusion
It's actually very simple to turn a Java application into a windows service with Apache Commons Daemon. In fact you can do it with no change at all to your command line program since, by default, all Procrun does is call your programs "main" method, and waits for it to return. Hopefully this write up will help you get your service up and running in no time at all.

Comments

Post new comment

Please solve the math problem above and type in the result. e.g. for 1+1, type 2.
The content of this field is kept private and will not be shown publicly.
  • Lines and paragraphs break automatically.

More information about formatting options