📜 ⬆️ ⬇️

Creating a daemon process in Java

Good all the time of day.

Recently, on duty, it became necessary to write the 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? By this, let's get down to business.
')


1 demonization



In order to demonize an application, it is necessary to disconnect it from the terminal in which it starts up directly. To do this, follow these steps:
  1. Disconnect stdin from terminal
  2. Run the process in background'e 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 startup line should look like this:

  java daemon_app <& - & 


where "<& -" will disable stdin;
"&" as it was said, will allow the application to be switched 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 disables the application from stdin and stdout. The stderr stream remains one active, which we can use to log errors during the initialization stage.

The doProcessing method is designed to implement the main logic.

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

The next improvement is to change the launch string to intercept the data sent by our process to stderr. To do this, we modify the launch string as follows:

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

where “2> /var/log/daemon_app_error.log” redirects the output from stderr to the file /var/log/daemon_app_error.log.

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

stdin - 0
stdout - 1
stderr - 2

2 Organization of interrupt signal processing



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

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

There are several ways to process signals in Java:

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

Here is what Sun writes (http://java.sun.com/products/jdk/faq/faq-sun-packages.html) about package sun. *:
The sun. * Public packages.
A Java program that directly calls into the sun. * Java-compatible platforms. In fact, such a program is not guaranteed to work even in the future ...

The 2nd method involves passing to it as an argument a class inherited from Thread. This means that when SIGTERM is received, a new thread will be created, which will take the actions determined by the programmer to terminate the entire process.

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

This is what happens when:
  • All the threads that are not daemon threads (§2.19) terminate.
  • It is a bylaw of the security manager.

Therefore:
  1. If we have non-running daemon threads, it is necessary to ensure their correct completion and join them to main using join. Daemon thread those for which setDaemon (true) was executed are not necessary to stop.
  2. Stop the thread main work, which means exit from the main method of the class being run.

You can simply do System.exit (), if you do not need to perform specific operations on the completion of the program, closing the resources used and active connections.

Modified code using Runtime.getRuntime (). AddShutdownHook to create an interrupt signal 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 a handler for the interrupt signal.

When receiving an interrupt signal, the static method setShutdownFlag is called, which changes the value of the static boolean property shutdownFlag to true, by the value of which the cycle is organized in the doProcessing method with a precondition.

3 Controlling script



So, the demon process is written. Now you need to create a script that controls its start, stop and state monitoring.

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

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

No argument procedure. We iterate over the necessary environment variables with the for loop. If there is no variable, output a warning. 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 of this procedure is a string with a classpath in which the elements are separated by a colon.
Replace the colon with a space 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 is not readable
		 then
			 echo "WARNING: Resource '$ resource' included in CLASSPATH does not exist or not readable"
		 fi
	 done
 } 

Sample 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 process ID started in the background, can be defined using the shell variable "$!", Which is subsequently written to the pid file.

After that, there is a test for 5 seconds, or the process “did not fall” during the launch process. Every second there is a check with the help of ps -p process status.

  launch_daemon ()
 {
	 JVM_OPTS = ""

         # Set max memory to 1G
         JVM_OPTS = "$ JVM_OPTS -Xmx1G"

	 echo "Starting process \ c"

	 #Transfer 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 the procedure to stop the process

The following implementation accepts 2 input arguments:

The procedure sends the process SIGTERM and after 5 seconds of waiting, if the process is not stopped, sends it a 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
 } 


Sample process status check 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 what everyone thinks about this topic.

Source: https://habr.com/ru/post/51991/


All Articles