java-service-wrapper or how to daemonize your java services for all major OSes

Java services (java programs in general) come in different flavors, either in a jar file either with a shell script that will execute the java program after parsing your options or any other form. Most often than not, java services do not come with a handy init.d script leaving you the responsibility of doing/maintaining it by your own. Writing init.d scripts isn’t the most attractive task one can be asked to do, it can get really cumbersome. Also it raises the question of ‘What if I want to run it on another OS ?’, this script will need to be adapted each and every time. In this blog post, Java-Service-Wrapper (JSW) will be introduced, JSW allows you to make your java services run as native Unix/Linux/Windows services in a painless way.

In this blog post in order to show how java-service-wrapper works, Logstash (A log management tool, see this post if you want more details) will be installed as a Linux service.

Getting Logstash and running it manually

First thing first, let’s download the logstash jar here. It contains all the dependencies Logstash needs to run.

Then create a the configuration file (ie. /etc/logstash/test.conf) Logstash will be run against :

input {
    file {
        path => '/var/log/secure'
        type => 'secure'
    }
}
output {
    file {
        path => '/tmp/test.json'
    }
}

Finally simply run : java -jar logstash-1.1.9-monolithic.jar agent -f /etc/logstash/test.conf
After few seconds (maybe a minute or two), if you ssh to the box where Logstash is running, you should see the content of the /var/log/secure log line in your /tmp/test.json file

We have a working version of Logstash, but you can already see, that starting it and stoping it without a service fashion way will be a pain.

In order to make things the clean way we will move and rename logstash-1.1.9-monolithic.jar to /usr/local/bin/logstash.jar

Getting java-service-wrapper

For this step simply download the appropriate tar ball from here

Once untared you’ll be presented with some files and directories, out of all of them only 5 will be useful here

  • lib/libwrapper.so: copy it to /usr/local/lib
  • lib/wrapper.jar: copy it to /usr/local/lib
  • bin/wrapper: copy it to /usr/local/bin
  • src/bin/sh.script.in: copy it and rename it to /usr/local/bin/myservice_wrapper and add the executable bit permission if this is not already the case (ie. /usr/local/bin/logstash_wrapper)
  • conf/wrapper.conf: copy it and rename it to /etc/myservice.conf (ie. /etc/logstash.conf)

And that is it for the installation of java-service-wrapper, from now on if you need to add other java services all you’ll need to do is copy again the sh.script.in and wrapper.conf with the accurate names.

Configuring sh.script.in and wrapper.conf accordingly

sh.script.in aka myservice_wrapper

The change in this file are pretty straight forward, it concerns the details about the way your service will be run.

Those are the minimum changed one needs to edit :

  • APP_NAME: your service name (ie. logstash)
  • APP_LONG_NAME: if you have a longer description
  • WRAPPER_CONF: /etc/myservice.conf (ie. /etc/logstash.conf)

Later more in depth change can be brought :

  • PRIORITY: specify nice value
  • PIDDIR: the path of where the pid file should be stored
  • RUN_AS_USER: specify the user the service should be run as
  • USE_UPSTART: flag for using upstart

Also you can define the run levels when your service should be started/stopped in this specific file

wrapper.conf aka myservice.conf

Here, the configuration is a bit more complex. This file defines the way your java program will be called (classpath, libraries, parameters, etc…). We will see some aspect of the configuration here, but for a full details of what is possible please refer to the official doc

wrapper.java.command

Simply specify the java executable path

wrapper.java.command=/usr/bin/java

wrapper.java.mainclass

Specify the class to execute when the Wrapper starts the application. There are 4 possibilities, refer to the doc for more informations.

Basically, if your application comes within a jar use WrapperJarApp if it comes in a different way use WrapperSimpleApp. Since Logstash comes in a jar we will be using WrapperJarApp class

wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperJarApp

wrapper.logfile

Log file to which all output to the console will be logged

wrapper.properties=/var/log/logstash_wrapper.log

wrapper.java.library.path.x

Java library path to use

wrapper.java.library.path.1=/usr/local/lib

wrapper.java.classpath.x

Java classpath to use

wrapper.java.classpath.1=/usr/local/lib/wrapper.jar
wrapper.java.classpath.2=/usr/local/bin/logstash.jar

wrapper.java.additional.x

Additional Java parameters to pass to Java when it is launched. These are not parameters for your application, but rather parameters for the JVM.

wrapper.java.additional.1=-Xms1G
wrapper.java.additional.2=-Xmx1G

wrapper.app.parameters.x

And finally the parameter we want to pass to our service.
We’ve seen previously that we ran logstash the following way :java -jar /usr/local/bin/logstash.jar agent -f /tmp/test.conf

This will be translated with the following configuration

wrapper.app.parameter.1=/usr/local/bin/logstash.jar
wrapper.app.parameter.2=agent
wrapper.app.parameter.3=-f
wrapper.app.parameter.3=/etc/logstash/test.conf

And we are done !

Testing

First step of testing will be to verify the wrapper shell script does work correctly.
Run : /usr/local/bin/logstash_wrapper console

If that works you should see something similar to this

wrapper  | --> Wrapper Started as Console
wrapper  | Java Service Wrapper Community Edition 32-bit 3.5.17
wrapper  |   Copyright (C) 1999-2012 Tanuki Software, Ltd. All Rights Reserved.
wrapper  |     http://wrapper.tanukisoftware.com
wrapper  | 
wrapper  | Launching a JVM...
jvm 1    | WrapperManager: Initializing...
jvm 1    | {:message=>"Read config", :level=>:info, :file=>"/home/vagrant/logstash-1.1.9-monolithic.jar!/logstash/agent.rb", :line=>"329", :method=>"run"}
jvm 1    | {:message=>"Start thread", :level=>:info, :file=>"/home/vagrant/logstash-1.1.9-monolithic.jar!/logstash/agent.rb", :line=>"332", :method=>"run"}

Finally create a symbolic link /etc/init.d/logstash pointing to your /usr/local/bin/logstash_wrapper, and you are done.

service logstash start
service logstash status
service logstash stop

All three command should be available.

Now if you change OSes all you have to do is edit the file path in the wrapper.conf (ie. /etc/logstash.conf) to reflect your actual paths on the new OS and nothing else. You will be up and running in no time.

Conclusion

Java-service-wrapper is one out of several options to do this specific task. I am not claiming that it is the solution that will solve all your problem, but it is a strong option, it does – in an understandable way – solve the java-service daemonization issue and the multiple OS porting. JSW saves you time and effort on the long run. Now write the configuration file and daemonzie it everywhere. QED

Advertisements


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s