Creating a Java Daemon (System Service) for Debian using Apache Commons Jsvc

Creating a Java Daemon for Debiandaemon is a computer program that runs as a background process, executing tasks on a predefined schedule or in response to particular events, or in response to requests for information or services from other programs.

To start up a Java program as a daemon using Jsvc, we’ll need to do a few things:

a) Implement the Apache Commons Daemon interface.

b) Install Jsvc by downloading and compiling the source (C) or via the command line with the sudo apt-get install jsvc command.

c) Create a bash shell script to launch the daemon and manage the basic set of daemon controls, namely: startstop restart. Others include reload status.

Implementing the Java Daemon Interface

Implementing the Daemon interface is a fairly simple undertaking. Once you have downloaded the library from the Apache Commons project site and referenced it in your Java project, you can go about creating your implementation as follows:

package za.co.neilson;

import org.apache.commons.daemon.Daemon;
import org.apache.commons.daemon.DaemonContext;
import org.apache.commons.daemon.DaemonInitException;

public class MyDaemon implements Daemon {

    private Thread myThread; 
    private boolean stopped = false;
    private boolean lastOneWasATick = false;
   
    @Override
    public void init(DaemonContext daemonContext) throws DaemonInitException, Exception {
        /*
         * Construct objects and initialize variables here.
         * You can access the command line arguments that would normally be passed to your main() 
         * method as follows:
         */

        String[] args = daemonContext.getArguments(); 
       
        myThread = new Thread(){
            private long lastTick = 0;
           
            @Override
            public synchronized void start() {
                MyDaemon.this.stopped = false;
                super.start();
            }

            @Override
            public void run() {            
                while(!stopped){
                    long now = System.currentTimeMillis();
                    if(now - lastTick >= 1000){
                        System.out.println(!lastOneWasATick ? "tick" : "tock");
                        lastOneWasATick = !lastOneWasATick;
                        lastTick = now; 
                    }
                }
            }
        };
    }

    @Override
    public void start() throws Exception {
        myThread.start();
    }

    @Override
    public void stop() throws Exception {
        stopped = true;
        try{
            myThread.join(1000);
        }catch(InterruptedException e){
            System.err.println(e.getMessage());
            throw e;
        }
    }
   
    @Override
    public void destroy() {
        myThread = null;
    }
}

Jsvc seems to require that our program is exported to a jar, which in this instance I have stored at /usr/local/mydaemon/MyDaemon.jar and the Apache Commons Daemon library has been stored at /usr/local/mydaemon/lib/commons-daemon-1.0.15.jar.

Creating a bash shell script to launch the daemon and manage the basic set of daemon controls

Debian daemon init scripts are stored in /etc/init.d/. A skeleton init script, (/etc/init.d/skeleton) is available to copy and modify according to your needs and generally would provide a good starting point for your services’ init script, however, writing an init script for Jsvc is a little different.

So assuming that that your Daemon implementation’s jar file and the Apache Commons Daemon library are stored at the above locations A basic example of a bash init script for our MyDaemon service could look like this:

#! /bin/sh
#  /etc/init.d/mydaemon

### BEGIN INIT INFO
# Provides:          mydaemon
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Short-Description: Starts the MyDaemon service
# Description:       This file is used to start the daemon
#                    and should be placed in /etc/init.d
### END INIT INFO

# Author:   Sheldon Neilson <sheldon[AT]neilson.co.za>
# Url:      www.neilson.co.za
# Date:     25/04/2013

NAME="mydaemon"
DESC="MyDaemon service"

# The path to Jsvc
EXEC="/usr/bin/jsvc"

# The path to the folder containing MyDaemon.jar
FILE_PATH="/usr/local/$NAME"

# The path to the folder containing the java runtime
JAVA_HOME="/usr/lib/jvm/default-java"

# Our classpath including our jar file and the Apache Commons Daemon library
CLASS_PATH="$FILE_PATH/MyDaemon.jar:$FILE_PATH/lib/commons-daemon-1.0.15.jar"

# The fully qualified name of the class to execute
CLASS="za.co.neilson.MyDaemon"

# Any command line arguments to be passed to the our Java Daemon implementations init() method
ARGS="myArg1 myArg2 myArg3"

#The user to run the daemon as
USER="root"

# The file that will contain our process identification number (pid) for other scripts/programs that need to access it.
PID="/var/run/$NAME.pid"

# System.out writes to this file...
LOG_OUT="$FILE_PATH/log/$NAME.out"

# System.err writes to this file...
LOG_ERR="$FILE_PATH/err/$NAME.err"

jsvc_exec()
{  
    cd $FILE_PATH
    $EXEC -home $JAVA_HOME -cp $CLASS_PATH -user $USER -outfile $LOG_OUT -errfile $LOG_ERR -pidfile $PID $1 $CLASS $ARGS
}

case "$1" in
    start) 
        echo "Starting the $DESC..."       
       
        # Start the service
        jsvc_exec
       
        echo "The $DESC has started."
    ;;
    stop)
        echo "Stopping the $DESC..."
       
        # Stop the service
        jsvc_exec "-stop"      
       
        echo "The $DESC has stopped."
    ;;
    restart)
        if [ -f "$PID" ]; then
           
            echo "Restarting the $DESC..."
           
            # Stop the service
            jsvc_exec "-stop"
           
            # Start the service
            jsvc_exec
           
            echo "The $DESC has restarted."
        else
            echo "Daemon not running, no action taken"
            exit 1
        fi
            ;;
    *)
    echo "Usage: /etc/init.d/$NAME {start|stop|restart}" >&2
    exit 3
    ;;
esac

The init script above should be stored in /etc/init.d/ along with the system’s other start-up scripts.

Automatically Starting the Daemon at System Boot

With our init script done, we need to change it’s permissions to make it executable using chmod. chmod 755 /etc/init.d/mydaemon. And register it for execution as the system’s run level changes.

“Debian uses a Sys-V like system for executing commands when the system runlevel changes, for example at boot and shutdown time.” – Source: Making scripts run at boot time with Debian

As the system’s runlevel changes, any scripts within corresponding /etc/rc*.d/ directory will be executed, where * is the number of the runlevel.

The easiest way to have our script executed appropriately is to use the Debian command update-rc.d to add the appropriate symlinks for us.

To enable our daemon at boot use update-rc.d mydaemon defaults:

root@sheldon-pc:/etc/init.d# update-rc.d mydaemon defaults
 Adding system startup for /etc/init.d/mydaemon...
   /etc/rc0.d/K20mydaemon -> ../init.d/mydaemon
   /etc/rc1.d/K20mydaemon -> ../init.d/mydaemon
   /etc/rc6.d/K20mydaemon -> ../init.d/mydaemon
   /etc/rc2.d/S20mydaemon -> ../init.d/mydaemon
   /etc/rc3.d/S20mydaemon-> ../init.d/mydaemon
   /etc/rc4.d/S20mydaemon -> ../init.d/mydaemon
   /etc/rc5.d/S20mydaemon-> ../init.d/mydaemon

If you should ever wish to disable it, run update-rc.d -f mydaemon remove:

root@sheldon-pc:/etc/init.d# update-rc.d -f mydaemon remove
update-rc.d: /etc/init.d/mydaemon exists during rc.d purge (continuing)
 Removing any system startup links for /etc/init.d/blah ...
   /etc/rc0.d/K20mydaemon
   /etc/rc1.d/K20mydaemon
   /etc/rc2.d/S20mydaemon
   /etc/rc3.d/S20mydaemon
   /etc/rc4.d/S20mydaemon
   /etc/rc5.d/S20mydaemon
   /etc/rc6.d/S20mydaemon

Our daemon should now start when the system boots. To view it’s output, first we should stop it to prevent any concurrent access issues when we open the log file: /etc/init.d/mydaemon stop.

As we know that our log file is being created at /usr/local/mydaemon/log/mydaemon.out, we can view it using the command vi /usr/local/mydaemon/log/mydaemon.out

root@sheldon-pc:/etc/init.d# vi /usr/local/mydaemon/log/mydaemon.out
tick
tock
tick
tock
tick
tock...
  • jjkale

    So DaemonContext.getArguments() should be able to read the ARGS field in the bash script, so to speak?

    • SheldonNeilson

      That’s right:)

      • jjkale

        Ok, I’m trying this, I can store the command line arguments in ARGS and echo $ARGS shows me that that much works. Using daemonContext.getArguments() tells me that the String array is of length 0 however. Would you have any idea as to what’s going on?

        Many thanks.

        • SheldonNeilson

          Would you be able to post your init.d script where your ARGS are declared and the jsvc_exec() section if it has been modified? You may need to enclose your ARGS declaration in quotes and remove any unnecessary spaces? ARGS=”myArg1 myArg2 myArg3″

  • jjkale

    Sorry for the delay Sheldon and thanks again for looking at this. Here is the init file….

    #!/bin/sh

    EXEC=/usr/bin/jsvc

    JAVA_HOME=/usr/lib/jvm/java-7-oracle

    CLASS_PATH=”/usr/share/java/commons-daemon.jar”:”/home/howard/.m2/repository/com/sony/daemon/1.0/daemon-1.0-jar-with-dependencies.jar”

    CLASS=daemon.LogDaemon

    ARGS=$2

    echo $ARGS

    USER=howard

    mkdir -p erroutpid

    PID=erroutpid/daemon.pid

    LOG_OUT=erroutpid/daemon.out

    LOG_ERR=erroutpid/daemon.err

    jsvc_exec()

    {

    $EXEC -home “$JAVA_HOME” -cp $CLASS_PATH -user $USER -outfile $LOG_OUT -errfile $LOG_ERR -pidfile $PID $1 $CLASS

    }

    case “$1” in

    start)

    jsvc_exec

    echo “Daemon started”

    ;;

    stop)

    jsvc_exec “-stop”

    ;;

    restart)

    if [ -f “$PID” ]; then

    jsvc_exec “-stop”

    jsvc_exec

    else

    echo “Daemon not running, will do nothing”

    exit 1

    fi

    ;;

    *)

    echo “usage: daemon {start|stop|restart}” >&2

    exit 3

    ;;

    esac

    • SheldonNeilson

      This line should end with ” $ARGS”:

      $EXEC -home “$JAVA_HOME” -cp $CLASS_PATH -user $USER -outfile $LOG_OUT -errfile $LOG_ERR -pidfile $PID $1 $CLASS

      And since you’re using ARGS=$2 remember to put all your command line args in quotes when you run it too, so /etc/init.d/yourdaemonname start “myArg1 myArg2 myArg3”. If you want this to start at boot, you’ll have to specify your args in the init.d or in a separate default configuration file.

      • jjkale

        Thanks Sheldon, that did the trick.

  • eizt

    Thank you very much.

    I finally understood about using jsvc .. !! 🙂 🙂

    • SheldonNeilson

      Glad it helped 🙂

  • Jon

    so why does this cause high cpu usage by jsvc. Where should the extra logic be added to reduce usage? I have reduced polling time from 1000ms to 10000ms but not much of a difference. Would appreciate some thoughts. Thanks.

    • SheldonNeilson

      Hi Jon. I imagine it would.. This is a ludicrous example of a service. The thread would be constantly checking whether or not a second has passed. Perhaps you could have a look at using a ScheduledExecutorService instance in your implementation unless you really do need to use the cpu 100% of the time or the device is dedicated to running your application. http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledExecutorService.html

      • Jon

        Hi Sheldon – thanks for the quick reply. I understand it’s just a basic example, and in attempt to ease the execution I added thread.setDaemon(true); … I will try this.

        Also, (1) is it possible to somehow specify the .class file rather than the .jar? (2) Does it improve execution time since the class is packaged within the .jar, and doing so would require less processing?

        And (3) how does jsvc compare to other java wrapper utilities like http://wrapper.tanukisoftware.com/ ?

        Cheers – Jon

        • Jon

          Hi again Sheldon, I actually applied another fix to it by adding a Thread.sleep method within the core while loop in the run() method (on suggestion of ‘nos’ at stackoverflow) – as follows:

          @Override

          public void run() {

          while(!stopped){

          long now = System.currentTimeMillis();

          if(now – lastTick >= 10000){
          System.out.println(!lastOneWasATick ? “tick” : “tock”);
          lastOneWasATick = !lastOneWasATick;
          lastTick = now;

          }

          //ADDED THIS BLOCK
          try {
          Thread.sleep(15000);
          } catch (InterruptedException ex) {
          //Logger.getLogger(JDaemon.class.getName()).log(Level.SEVERE, null, ex);
          }
          //END BLOCK

          }

          }

          • Jon
          • SheldonNeilson

            Hi Jon.

            Thread.setDaemon(true); shouldn’t affect the performance of your program.

            I had trouble starting up a service with JSVC with a class file, and I remember reading somewhere that packaging your service into a jar is a requirement of JSVC. I can’t think of a reason not to anyway?

            Depending on what you’re doing, The Thread.sleep method is probably fine, but I would still recommend using the ScheduledExecutorService.scheduleAtFixedRate() method. It has been thoroughly tested, documented and other developers that might need to maintain your code in the future should be familiar with it. It also provides methods to cancel execution for example and to manage more than one task. I’d give it a closer look.

  • FollowTheMedia

    I wonder if this piece of code is Open Source because I’d like to use it in a very simple application.

    • SheldonNeilson

      Hi. You’re welcome to use my code. You may want to check out Apache’s requirements for using JSVC though: http://www.apache.org/licenses/

  • Omar Shekfeh

    Hello Sheldon,

    Thank you for this useful article, I am new on java, so I am wandering if the java project should contain main() class or not? Let’s say that it should contain what is the main() method content?
    Thanks

    • SheldonNeilson

      Hi Omar

      A main method is not required here. Only overrides for the init, start, stop & destroy methods. Sometimes I create a main that calls these methods if I want to run the program manually or for use while debugging.

      • Omar Shekfeh

        Thank you Sheldon for your reply, I expected that this project should not need any main() method specially that the bash script contain the name of the class of the daemon, I tried it on debian server. but I had the following error:

        java.lang.ClassNotFoundException: [the package and the class of the daemon name which is in ur example za.co.neilson.MyDaemon]
        at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:323)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:268)
        at org.apache.commons.daemon.support.DaemonLoader.load(DaemonLoader.java:151)
        Cannot load daemon

        Do you have any helpful solution?

        • SheldonNeilson

          Hi again Omar.

          Has this line in your init file updated to match your fully qualified java class name?

          # The fully qualified name of the class to execute
          CLASS=”za.co.neilson.MyDaemon”

          • Omar Shekfeh

            I solved this problem, but I am facing another one. Would you please check it here : http://goo.gl/up3zQf

          • Rast1821

            You have compiled and run your code with two different jvm

  • Rast1821

    Hi sheldon neilson.
    I’m running a similar version of your code. The only issue i find happens when i call stop method ( /etc/init.d/mydaemon stop ). In /usr/local/mydaemon/log/mydaemon.err i find this error line: “Service exit with a return value of 143” and any System.out.print message is shown in /usr/local/mydaemon/log/mydaemon.out.
    Seems like java stop and destroy method are never called.
    Any suggestions?

    • SheldonNeilson

      Hi. Your service might be hogging the processor if it is anything like this code (which was only meant as a silly example).

      Look at the accepted answer here: http://stackoverflow.com/questions/12097104/apache-jsvc-fails-to-stop-daemon

      I’m not sold on the Thread.sleep method though.. Somewhere in this thread I discussed ScheduledExecutorService.scheduleAtFixedRate() with someone else. Look into that instead 🙂

  • aithusa123

    Hi, thank you very much for your tutorial. I am new to using daemons and it helped a lot for setting it up. I am trying to use the daemon to fetch data from the Twitter api and write it to a file using bufferedwriter. From the stdout log file, I can see that it is running correctly and fetcing data successfully, but it is not writing to the file. I also checked that there is no exceptions thrown from the bufferedwriter and the progrram works well through calling main. Would you have any idea on what may be wrong in my setup?

  • Mani Kannan

    Hi, i’m facing some peculiar problem. When i’m running the java application as a daemon process, i’m unable to create a file. Below is the excetion stack:

    java.io.FileNotFoundException: /data/sample.txt (Permission denied)
    at java.io.FileOutputStream.open(Native Method)
    at java.io.FileOutputStream.(Unknown Source)
    at java.io.FileOutputStream.(Unknown Source)
    at org.apache.log4j.FileAppender.setFile(FileAppender.java:294)
    at org.apache.log4j.FileAppender.activateOptions(FileAppender.java:165)
    at org.apache.log4j.rolling.RollingFileAppender.activateOptions(RollingFileAppender.java:180)
    at com.Log4jLoggerManager.getTimeBasedRollingPolicyLogger(Log4jLoggerManager.java:70)
    at com.LogRequestHandler.storeLogEntry(LogRequestHandler.java:188)
    at com.LogRequestHandler.processLogData(LogRequestHandler.java:115)
    at com.LogRequestHandler.run(LogRequestHandler.java:259)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

    But if i run same java jar from terminal as a java application (java -cp “MyJar.jar:lib/*” com.LogRequestHandler), i’m able to this file and write data in to it.

    Is there any special user under which daemon will be running other than the specified user under -user flag?
    Had anyone came across this?

    Thanks in advance.