Creating a daemon process in Java

    Good day to all.

    Recently, as a matter of service, it became necessary to write a background process for IBM AIX 5.2 in Java with a shell-controlling script.

    Once the work is done, the documentation is up, the code is written, why not share it with the public? According to this, we get down to business.



    1 Demonization



    In order to demonize the application, it is necessary to disconnect it from the terminal where it is launched directly. To do this, follow these steps:
    1. Disconnect stdin from terminal
    2. Start the process in background by specifying the ampersand "&" at the end of the launch line
    3. Close stdin, stdout directly in the application (in our case, for Java, it will be System.in.close (); System.out.close ();)

    Thus, the minimum launch line should look like this:

    java daemon_app <& - &


    where "<& -" will disable stdin;
    "&" as said, will allow you to put the application from foreground to background mode.

    The daemon_app code should look something like this:

    public class daemon_app
    {
        public static int main (String [] args)
        {
            try
            {
                daemonize ();
            }
            catch (Throwable e)
            {
                System.err.println ("Startup failed." + E.getMessage ());
                return 1;
            }
            doProcessing ();
            return 0;
        }
        static private void daemonize () throws Exception
        {
            System.in.close ();
            System.out.close ();
        }
        static private void doProcessing ()
        {
            // Do some processing
        }
    }
    

    The daemonize method disconnects the application from stdin and stdout. The stderr stream remains the only active one, which we can use to log errors at the initialization stage.

    The doProcessing method is designed to implement basic logic.

    Next, we can use some kind of logging framework, for example log4j.

    The next improvement will be changing the launch line to intercept the data sent by our process to stderr. To do this, modify the launch line as follows:

    java daemon_app <& - 2> /var/log/daemon_app_error.log &

    where "2> /var/log/daemon_app_error.log" will redirect the output from stderr to the file /var/log/daemon_app_error.log.

    If the reader is not in the know, then the input / output streams in the unix shell have the following identifiers:

    stdin - 0
    stdout - 1
    stderr - 2

    2 Organization of processing interrupt signals



    In the doProcessing method, we can organize an infinite loop with a precondition by which the process will end. This condition may be SIGTERM sent from the operating system, for example, via kill -15.

    Code 15 (SIGTERM) is the same for both AIX, HP-UX, and a regular Linux-based system.
    A list of signals and their codes can be obtained using the kill -l command.

    There are several ways to process signals in Java:
    • Using sun.misc.Signal and sun.misc.SignalHandler and then creating your own handler class that implements sun.misc.SignalHandler. You can find more detailed information on this method here (http://www.ibm.com/developerworks/ibm/library/i-signalhandling/)
    • Using the Runtime.getRuntime (). AddShutdownHook Method. (method signature: public void addShutdownHook (Thread hook))

    The first method is not very good due to the use of classes from the package sun.misc. Why?

    Here is what Sun writes (http://java.sun.com/products/jdk/faq/faq-sun-packages.html) about package sun. *:
    The sun. * Packages are not part of the supported, public interface.
    A Java program that directly calls into sun. * Packages is not guaranteed to work on all Java-compatible platforms. In fact, such a program is not guaranteed to work even in future versions on the same platform ...

    The 2nd method involves passing a class inherited from Thread as an argument to it. This means that upon receipt of SIGTERM, a new thread will be created that will perform actions defined by the programmer to complete the process as a whole.

    Turning to the documentation (http://java.sun.com/docs/books/jvms/second_edition/html/Concepts.doc.html#19152), what prerequisites are needed to complete the Java program?

    The Java virtual machine terminates all its activity and exits when one of two things happens:
    • All the threads that are not daemon threads (§2.19) terminate.
    • Some thread invokes the exit method of class Runtime or class System, and the exit operation is permitted by the security manager.

    Therefore:
    1. If we have non-daemon threads running, it is necessary to ensure their correct completion and attach them to main using join. Daemon threads, i.e. those for which setDaemon (true) has been executed need not be stopped.
    2. Stop thread main, which means exiting the main method of the launched class.

    You can simply make System.exit () if you do not need to carry out specific operations to complete the program, closing the resources used and active connections.

    Modified code using Runtime.getRuntime (). AddShutdownHook to create an interrupt handler is shown below:

    public class daemon_app
    {
        static private boolean shutdownFlag = false;
        public static int main (String [] args)
        {
            try
            {
                daemonize ();
            }
            catch (Throwable e)
            {
                System.err.println ("Startup failed." + E.getMessage ());
                return 1;
            }
            registerShutdownHook ();
            doProcessing ();
            return 0;
        }
        static private void doProcessing ()
        {
            while (false == shutdownFlag)
            {
                // Do some processing
            }
        }
        static public void setShutdownFlag () {shutdownFlag = true;}
        private static void registerShutdownHook ()
        {
            Runtime.getRuntime (). AddShutdownHook (
                new Thread () {
                    public void run () {
                            daemon_app.setShutdownFlag ();
                    }
                }
            );
        }
        static private void daemonize () throws Exception
        {
            System.in.close ();
            System.out.close ();
        }
    }

    So, we have the registerShutdownHook method, which is called from main and registers the handler for the interrupt signal.

    When an interrupt signal is received, the static method setShutdownFlag is called, which changes the value of the static boolean property shutdownFlag to true, the value of which organizes the loop in the doProcessing method with a precondition.

    3 Controlling script



    So the daemon process is written. Now you need to create a script to control its start, stop and status monitoring.

    I will not describe the entire process of creating a shell control script; I will give only a few useful procedures.

    An example of checking environment variables necessary for starting / running a process

    The procedure without arguments. We iterate over the necessary environment variables with the for loop. If there is no variable, a warning is displayed. If at least one variable is not set, stop execution with error code 1.

    check_env ()
    {
            exit_flag = 0
    	for env_var in JAVA_HOME ORACLE_HOME TUXEDO_HOME;
    	do
    		eval "env_value = \ $$ env_var"
    		if [-z "$ env_value"]
    		then
    			echo "ERROR: environment variable '$ env_var' is not set"
    			exit_flag = 1
    		fi
    	done
    	if [$ exit_flag -eq 1]
    	then
    		echo "Exiting. No process started"
    		exit 1
    	fi
    }
    

    Checking classpath elements

    The argument to this procedure is a line with a classpath in which the elements are separated by a colon.
    We replace the colon with a whitespace character - as a result, we get the opportunity to check each element.

    check_classpath ()
    {
    	#Checking files in classpath are exists and readable
    	for resource in `echo $ 1 | sed -e "s /: / / g" `;
    	do
    		if [$ {# resource} -gt 0] && [! -r $ resource] # if file not exists or not readable
    		then
    			echo "WARNING: Resource '$ resource' included in CLASSPATH does not exist or not readable"
    		fi
    	done
    }

    Example process start procedure

    This procedure provides for the presence of set variables - PID_DIR, PROCESS_NAME, CPATH

    where

    PID_DIR - directory for storing pid files
    PROCESS_NAME - process name
    CPATH - line with classpath

    The identifier of the process launched in the background can be determined using the shell variable "$!", Which subsequently written to the pid file.

    After that, a check takes place for 5 seconds, or the process “did not fall” during the startup process. Every second, a process status is checked using ps -p.

    launch_daemon ()
    {
    	JVM_OPTS = ""
            # Set max memory to 1G
            JVM_OPTS = "$ JVM_OPTS -Xmx1G"
    	echo "Starting process \ c"
    	# Run process in background with closed input stream, to detach it from terminal
    	$ JAVA_HOME / bin / java $ JVM_OPTS -cp $ CPATH daemon_app <& - 2> /var/log/$PROCESS_NAME.pid &
    	#Write pid to pid file
    	echo $! > $ PID_DIR / $ PROCESS_NAME.pid
    	if [$ {#!} -eq 0]
    	then
    		echo "... [failed]"
    	else # Checking for 5 seconds if process is alive
    		timer = 0
    		while [$ timer -lt 6]
    		do
    			echo ". \ c"
    			sleep 1
    			timer = $ timer + 1
    		done
    		if [`ps -p $! | | | wc -l` -gt 1]
    		then
    			echo "[started]"
    			exit 0
    		else
    			echo "[failed]"
    			exit 1
    		fi
    	fi
    }

    An example of a process stop procedure

    The given implementation takes 2 input arguments:
    • process id
    • absolute path to the pid file

    The procedure sends the SIGTERM process and after 5 seconds of waiting, if the process is not stopped, sends it SIGKILL, after which it deletes the pid file.

    stop_daemon ()
    {
    	echo "Stopping process \ c"
    	timer = 0
    	if [`ps -p $ 1 | wc -l` -eq 1]
    	then
    		echo "not running"
    	else
    		kill -TERM $ 1
    		while [`ps -p $ 1 | wc -l` -gt 1]
    		do
    			if [$ timer -gt 5]
    			then
    				kill -KILL $ 1
    				timer = 0
    			else
    				echo ". \ c"
    				sleep 1
    			fi
    			timer = $ timer + 1
    		done
    		echo "stopped"
    	fi
    	rm $ 2
    }


    An example of a process status verification procedure


    Takes 1 argument - the absolute path to the pid file.

    Returns a value other than 0 if the process is running and running, 0 - if the process is inactive.

    check_running ()
    {
    	if [-e $ 1]
    	then
    		fpid = `cat $ 1`
    		if [$ {# fpid} -gt 0]
    		then
    			lines = `ps -p $ fpid | wc -l`
    			echo $ (($ lines-1))
    		else
    			echo 0
    		fi
    	else
    		echo 0
    	fi
    }


    That's basically all I wanted to tell.

    It is interesting to hear who thinks what on this topic.

    Also popular now: