July 9, 2012

Introducing JSS: Java Simple Services



UPDATE: I've moved my whole blog to a new domain. That's why the comments section is closed here. The new URL for this post is http://www.leonardofischer.com/introducing-jss-java-simple-services/. If you have any question, post it there.

A few weeks ago, I needed to implement a service. Or a daemon. Or whatever you call a process that you start, and it runs in background until you call it to stop. I found the Java Service Wrapper, but I didn't like it: it has several OS dependent distributions, and a documentation that you take some time to learn. Other options include some forums recommending some scripts that depend on the operating system that allows you to run a java application in background. No one seems as easy as I thought that it could be.

So I decided to take the joy of writing my own service wrapper. I called it Java Simple Services. I focused on making it simple, small and only dependent on the JDK. The result is a small set of classes that manages the service for the user, making it relatively easy to start a background process. First, I'll present how to use it. If you're interested on how it works, just keep reading.


The Clock Application Example


I like the 'learn by example' approach, so I will present a very simple clock application. When started, it will run "forever", or until you give it a signal to stop. At each loop, it will update the last time read. You can think of it as a much more complex process, such as a web server or a database system that should run in background mode. It will serve as an example for the service that I will build using Java Simple Services.

package com.leonardofischer.jss.test;
import java.util.Date;
public class ClockApp {
    public boolean keepRunning = true;
    public String date = "Not executed yet";
    public void run() {
        try {
            while (keepRunning) {
                date = (new Date()).toString();
                System.out.println(date);
                Thread.sleep(1000);
            }
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        ClockApp clock = new ClockApp();
        clock.run();
    }
}


The Service Class: Creating the Clock Service


I tried to build an API as simple as possible to create services. After some work, I got an API design that requires you to implement only one abstract class and that is it.

In the Java Simple Services, you need to extend the Service class. Although it has several methods you can override, you only need to know three methods: start(String args[]), stop(String args[]) and parseArgs(String args[]). You can think this way:

  • The start(String args[]) method is the main(String args[]) method of your service. You will put your code here, and you should return from it only after you processing is done or your service must stop;
  • The stop(String args[]) method is where you put the code to stop your application. Although it can take as long as you need, you should use this method to only send a signal to your code in the start(String args[]) method to make it stop running, and then return. Note that this method will be executed in a different thread from the one where start(String args[]) is running, so you may need some synchronization here;
  • Finally, the parseArgs(String args[]) will take your command line arguments and will start, stop and do other things with your service.
So, here is the code to wrap our clock application in a service:

package com.leonardofischer.jss.test;
import com.leonardofischer.jss.Service;
public class ClockService extends Service {
    ClockApp clock;
    public void start(String[] args) {
        clock = new ClockApp();
        clock.run();
    }

    public void stop(String[] args) {
        clock.keepRunning = false;
    }

    public static void main(String[] args) {
        ClockService clockService = new ClockService();
        clockService.parseArgs(args);
    }
}

Using Java Simple Services and the code above, you have an application that runs in background, can be started, and stopped and so on... Let's run it:

$> java com.leonardofischer.jss.test.ClockService
Usage: java com.leonardofischer.jss.test.ClockService {start|stop|restart|sta
tus}
$> java com.leonardofischer.jss.test.ClockService start
The service started
$> java com.leonardofischer.jss.test.ClockService start
The service is already running, or another process is using the port 6400.
$> java com.leonardofischer.jss.test.ClockService status
STATUS: the service is running
$> java com.leonardofischer.jss.test.ClockService restart
The service stoped
The service started
$> java com.leonardofischer.jss.test.ClockService status
STATUS: the service is running
$> java com.leonardofischer.jss.test.ClockService stop
The service stoped
$> java com.leonardofischer.jss.test.ClockService stop
The service is not running
$> 

As you can see, it is very easy to execute, get status and stop the ClockApp in a background process. The commands above are already built into JSS, so all the code you needed was the new ClockService class. But how you can change those generic default messages? How to return the current time using the status command? All that can be changed using service configuration.


Service Customization


The code above may be too simple for what you need, so I developed several methods in the service class that, if you override, should allow you to customize your service. I'll present some methods, but please give a look at the class Documentation, so you can know exactly what you can do here.

public class ClockService extends Service {

    // same code as above, plus these methods

    public String status(String[] args) {
        return clock.date;
    }

    public void onServiceNotRunning() {
        printErrorMessage("The ClockService is not running, "+
                "please start it with the 'iniciar' command");
    }

    // new version of the main, with some customization
    public static void main(String[] args) {
        ClockService clockService = new ClockService();
        // 'iniciar' is the portuguese word for 'start'
        clockService.setStartCommand("iniciar");
        // Go to 'How Java Simple Services Work' if you want to know
        // why setting a port here
        clockService.getServiceController().setPort(9876);
        clockService.parseArgs(args);
    }
}

And if we run again those commands...

$> java com.leonardofischer.jss.test.ClockService
Usage: java com.leonardofischer.jss.test.ClockService {iniciar|stop|restart|s
tatus}
$> java com.leonardofischer.jss.test.ClockService start
Usage: java com.leonardofischer.jss.test.ClockService {iniciar|stop|restart|s
tatus}
$> java com.leonardofischer.jss.test.ClockService iniciar
The service started
$> java com.leonardofischer.jss.test.ClockService iniciar
The service is already running, or another process is using the port 9876.
$> java com.leonardofischer.jss.test.ClockService status
Wed Jul 11 21:53:51 BRT 2012
$> java com.leonardofischer.jss.test.ClockService restart
The service stoped
The service started
$> java com.leonardofischer.jss.test.ClockService status
Wed Jul 11 21:54:19 BRT 2012
$> java com.leonardofischer.jss.test.ClockService stop
The service stoped
$> java com.leonardofischer.jss.test.ClockService stop
The ClockService is not running, please start it with the 'iniciar' command
$> 

I did not use here all the possible customization. I only wanted to show you some examples, such as returning the last read time with the status command, changing the command string used to start the service and change the error message if the service is not running.

Also, there is a ServiceController class that helps on the service execution. There are some customization points here too, but they are more related to the process that will be executed in background. Read carefully the documentation before use these methods, ok?


How Java Simple Services Work


This is the advanced topic on Java Simple Services, so not everyone needs to read from now on to use it. But I invite you to continue reading.

First of all, your service will run in a different process than the one that you executed it with the start command line parameter. If you use the 'run' parameter, then it will run in this process, but with start, another process will be started with the 'run' parameter, and the process that you started will finish normally. The configuration of the ServiceController class serves to customize how the JVM will be executed here.

When you execute the run parameter, the ServiceController also creates a thread that listens for future commands. It uses the Java Socket API, and that is why you can set a port to listen to. This socket will be opened until you return from your start(String args[]) method. Note that this thread will be blocked by the socket waiting for a connection, so it will not take any processor time until you try to connect to the service.

If you run a command such as stop, status or restart, the process that executes this command will connect to the service through the socket. Thus, you need to use the same port that you used to run your service, or you will not be able to connect to it. When the running server receives a command through the socket, it understands it (as well as the command line parameters from the command line) and calls the corresponding service method.


That's it!

That's all folks. I hope that Java Simple Services can be as helpful to you as it was for me. I am releasing the full source code, and I invite you to submit any bug fix or feature that you developed for it. Or just leave a comment with your thoughts about it.

If you are interested in download Java Simple Services go to Git Hub, I'm pushing updates there. You can also download the latest build, so you can try it.


Update 1: I did several code updates days after publishing this post, so I did updated the output shown as well as some complement text to it.

UPDATE: I've moved my whole blog to a new domain. That's why the comments section is closed here. The new URL for this post is http://www.leonardofischer.com/introducing-jss-java-simple-services/. If you have any question, post it there.