How do I stop a Java process gracefully in Linux and Windows?
When does Runtime.getRuntime().addShutdownHook get called, and when does it not?
What about finalizers, do they help here?
Can I send some sort of signal to a Java process from a shell?
I am looking for preferably portable solutions.
Shutdown hooks execute in all cases where the VM is not forcibly killed. So, if you were to issue a "standard" kill (SIGTERM from a kill command) then they will execute. Similarly, they will execute after calling System.exit(int).
However a hard kill (kill -9 or kill -SIGKILL) then they won't execute. Similarly (and obviously) they won't execute if you pull the power from the computer, drop it into a vat of boiling lava, or beat the CPU into pieces with a sledgehammer. You probably already knew that, though.
Finalizers really should run as well, but it's best not to rely on that for shutdown cleanup, but rather rely on your shutdown hooks to stop things cleanly. And, as always, be careful with deadlocks (I've seen far too many shutdown hooks hang the entire process)!
Ok, after all the possibilities I have chosen to work with "Java Monitoring and Management"
Overview is here
That allows you to control one application from another one in relatively easy way. You can call the controlling application from a script to stop controlled application gracefully before killing it.
Here is the simplified code:
Controlled application:
run it with the folowing VM parameters:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
//ThreadMonitorMBean.java
public interface ThreadMonitorMBean
{
String getName();
void start();
void stop();
boolean isRunning();
}
// ThreadMonitor.java
public class ThreadMonitor implements ThreadMonitorMBean
{
private Thread m_thrd = null;
public ThreadMonitor(Thread thrd)
{
m_thrd = thrd;
}
#Override
public String getName()
{
return "JMX Controlled App";
}
#Override
public void start()
{
// TODO: start application here
System.out.println("remote start called");
}
#Override
public void stop()
{
// TODO: stop application here
System.out.println("remote stop called");
m_thrd.interrupt();
}
public boolean isRunning()
{
return Thread.currentThread().isAlive();
}
public static void main(String[] args)
{
try
{
System.out.println("JMX started");
ThreadMonitorMBean monitor = new ThreadMonitor(Thread.currentThread());
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
ObjectName name = new ObjectName("com.example:type=ThreadMonitor");
server.registerMBean(monitor, name);
while(!Thread.interrupted())
{
// loop until interrupted
System.out.println(".");
try
{
Thread.sleep(1000);
}
catch(InterruptedException ex)
{
Thread.currentThread().interrupt();
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
// TODO: some final clean up could be here also
System.out.println("JMX stopped");
}
}
}
Controlling application:
run it with the stop or start as the command line argument
public class ThreadMonitorConsole
{
public static void main(String[] args)
{
try
{
// connecting to JMX
System.out.println("Connect to JMX service.");
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:9999/jmxrmi");
JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
// Construct proxy for the the MBean object
ObjectName mbeanName = new ObjectName("com.example:type=ThreadMonitor");
ThreadMonitorMBean mbeanProxy = JMX.newMBeanProxy(mbsc, mbeanName, ThreadMonitorMBean.class, true);
System.out.println("Connected to: "+mbeanProxy.getName()+", the app is "+(mbeanProxy.isRunning() ? "" : "not ")+"running");
// parse command line arguments
if(args[0].equalsIgnoreCase("start"))
{
System.out.println("Invoke \"start\" method");
mbeanProxy.start();
}
else if(args[0].equalsIgnoreCase("stop"))
{
System.out.println("Invoke \"stop\" method");
mbeanProxy.stop();
}
// clean up and exit
jmxc.close();
System.out.println("Done.");
}
catch(Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
That's it. :-)
An another way: your application can open a server socet and wait for an information arrived to it. For example a string with a "magic" word :) and then react to make shutdown: System.exit(). You can send such information to the socke using an external application like telnet.
Here is a bit tricky, but portable solution:
In your application implement a shutdown hook
When you want to shut down your JVM gracefully, install a Java Agent that calls System.exit() using the Attach API.
I implemented the Java Agent. It is available on Github: https://github.com/everit-org/javaagent-shutdown
Detailed description about the solution is available here: https://everitorg.wordpress.com/2016/06/15/shutting-down-a-jvm-process/
Similar Question Here
Finalizers in Java are bad. They add a lot of overhead to garbage collection. Avoid them whenever possible.
The shutdownHook will only get called when the VM is shutting down. I think it very well may do what you want.
Thanks for you answers. Shutdown hooks seams like something that would work in my case.
But I also bumped into the thing called Monitoring and Management beans:
http://java.sun.com/j2se/1.5.0/docs/guide/management/overview.html
That gives some nice possibilities, for remote monitoring, and manipulation of the java process. (Was introduced in Java 5)
Signalling in Linux can be done with "kill" (man kill for the available signals), you'd need the process ID to do that. (ps ax | grep java) or something like that, or store the process id when the process gets created (this is used in most linux startup files, see /etc/init.d)
Portable signalling can be done by integrating a SocketServer in your java application. It's not that difficult and gives you the freedom to send any command you want.
If you meant finally clauses in stead of finalizers; they do not get extecuted when System.exit() is called.
Finalizers should work, but shouldn't really do anything more significant but print a debug statement. They're dangerous.
Related
I want to start a webservice via an executible jar I create (so that I can eventually use procrun to have it start up as a Windows Service). The webservice is currently started via the command line by calling the main method on the class.
Here is my code so far:
public class test
{
private static boolean stop = false;
private static Process process;
public static void start(String[] args)
{
String classpath = "my\\classpath\\test.jar";
ProcessBuilder processBuilder = new ProcessBuilder("C:\\java\\jdk1.6.0_43\\bin\\java",
"-cp", classpath,
"com.test.theJavaWebServiceWithAMainMethod");
try
{
process = processBuilder.start();
}
catch (IOException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
public static void stop(String[] args)
{
stop = true;
process.destroy();
}
public static void main(String[] args)
{
if (args != null && args.length > 0)
{
String command = args[0];
if ("start".equals(command))
{
start(args);
}
else if ("stop".equals(command))
{
stop(args);
}
}
else
{
throw new IllegalArgumentException("command missing");
}
}
}
Starting works fine. I see the process start in the task manager and I can now pull up the WSDL. However when I go to kill the process via process.destroy() (by calling the main method with the arguemnt stop), I get a null pointer exception I assume because this is a new instance of the jvm so it doesn't know the process from when I called start. Whats the best way to persist this process object or pid or something so when I go to call stop, I can find and kill the webservice (java process) that is running?
You are executing your 'test' program twice, which results in two completely separate program instances, and they have no reference to each other. So when you call the 'test' program with stop command, theprocess variable is null because one was not created in the current program.
Options:
Rewrite your main method to block, waiting for the "stop" command. This would mean the start/stop were not separate asynchronous program executions though. You would start the program, and it would then wait for you to enter a stop command before exiting.
You could devise a way to signal the other application 'remotely' ...even though it's all running on the same machine. This is easy to do via a simple network socket, and is a very common approach. (Checkout Tomcat's start/stop command handling for an example....though it's more complex than you need.) To summarize... once running, the main program listens on a server socket. The subsequent "stop program" connects to that server socket as client to notify the main program it should stop.
You could use underlying OS tools to find and stop the program. See this answer: How to find and kill running Win-Processes from within Java? . It's hard to do cross platform and generically though.
It's not really clear what it is you want to do though and why you are executing your web service as a separate program via ProcessBuilder. Depending on your requirements, perhaps you could instead do something like this:
public void start(String[] args)
{
try
{
com.test.theJavaWebServiceWithAMainMethod.main(args);
}
catch (IOException e1)
{
e1.printStackTrace();
}
}
That would start your service, of course, and then you could control-c to end the program.
Rather than start your runnable jar as you do, you could convert your program to start as a Windows Service. Then it would be able to react to a Windows-based shutdown request.
Most servers like Jetty or Weblogic provide some sort of mechanism to start or shutdown the server via a call (sometimes over jmx). Can always to keep stuff running from main:
public static void main(String args[]) {
try {
Setup Spring context.....
Object lock = new Object();
synchronized (lock) {
lock.wait();
}
} catch (Exception e) {
e.printStackTrace();
}
}
And do a kill on the process PID. But thought there might a nice open-source solution hanging around in cyber space that adds JMX bells and whistles.
Maybe you want to start by looking at a combination of Commons Daemon and Commons Launcher. Not sure if there are any JMX bells and whistles in there, but this provides hooks into your OS management interface for server processes.
When I first developed a Java service for Windows using Apache daemon, I used the JVM mode which I liked a lot. You specify your class and start\stop (static) methods. But with Linux, Jsvc doesn't look like it has the same option. I would really like to know why ?!
Anyway If I'm going to use Linux's init system, I'm trying to find a similar way to accomplish the same behavior which is to start the app in anyway but to stop it, I'll have to call a method in a class.
My question is, after the jar is started, how can I use the JVM libraries or anything else, to call a method in my application (which will attempt to stop my application gracefully).
Another side question, if an application is started and that application has static methods, If I use the java command line to run a main method in one if that's application class, and the main method, which is static would call another static method in the class in which I would like to signal the termination signal, would that call by in the same JVM?
Why not rather add a ShutdownHook to your application?
A shutdown hook is simply an initialized but unstarted thread. When
the virtual machine begins its shutdown sequence it will start all
registered shutdown hooks in some unspecified order and let them run
concurrently. When all the hooks have finished it will then run all
uninvoked finalizers if finalization-on-exit has been enabled.
Finally, the virtual machine will halt. Note that daemon threads will
continue to run during the shutdown sequence, as will non-daemon
threads if shutdown was initiated by invoking the exit method.
This will allow your jar to terminate gracefully before being shutdown:
public class ShutdownHookDemo {
public void start() {
System.out.println("Demo");
ShutdownHook shutdownHook = new ShutdownHook();
Runtime.getRuntime().addShutdownHook(shutdownHook);
}
public static void main(String[] args) {
ShutdownHookDemo demo = new ShutdownHookDemo();
demo.start();
try {
System.in.read();
}
catch(Exception e) {
}
}
}
class ShutdownHook extends Thread {
public void run() {
System.out.println("Shutting down");
//terminate all other stuff for the application before it exits
}
}
It is important to note
The shutdown hook runs when:
A program exists normally. For example, System.exit() is called, or the last non-daemon thread exits.
the Virtual Machine is terminated. e.g. CTRL-C. This corresponds to kill -SIGTERM pid or
kill -15 pid on Unix systems.
The shutdown hook will not run when:
The Virtual Machine aborts
A SIGKILL signal is sent to the Virtual Machine process on Unix systems. e.g. kill -SIGKILL pid or kill -9 pid
A TerminateProcess call is sent to the process on Windows systems.
Alternatively if you must you can use this to call a method in a class:
public class ReflectionDemo {
public void print(String str, int value) {
System.out.println(str);
System.out.println(value);
}
public static int getNumber() { return 42; }
public static void main(String[] args) throws Exception {
Class<?> clazz = ReflectionDemo.class;//class name goes here
// static call
Method getNumber = clazz.getMethod("getNumber");
int i = (Integer) getNumber.invoke(null /* static */);
// instance call
Constructor<?> ctor = clazz.getConstructor();
Object instance = ctor.newInstance();
Method print = clazz.getMethod("print", String.class, Integer.TYPE);
print.invoke(instance, "Hello, World!", i);
}
}
and to load a class dynamically:
ClassLoader loader = URLClassLoader.newInstance(
new URL[] { yourURL },
getClass().getClassLoader()
);
Class<?> clazz = Class.forName("mypackage.MyClass", true, loader);
Class<? extends Runnable> runClass = clazz.asSubclass(Runnable.class);
References:
http://onjava.com/onjava/2003/03/26/shutdownhook.html
http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Runtime.html#addShutdownHook(java.lang.Thread)
How to shutdown java application correctly from C# one
How does one access a method from an external jar at runtime?
How to load a jar file at runtime
Basically the only way to call between JVMs is through the use of Sockets, either directly or via implementations based on it, such as RMI
You might lie to check out http://www.javaworld.com/javaqa/2000-03/03-qa-0324-ipc.html for further info
Runtime.getRuntime.exex("abc.exe -parameters");
using .waitFor() does not help to determine the completion of process.
Looks like JDK8 introduces Process.isAlive(). Surprised it took so long...
In the meantime, the best option seems to be to poll Process.exitValue(), wrapped in a try-catch:
// somewhere previous...
String[] cmd = { "abc.exe", "-p1", "-p2" };
Process process = Runtime.getRuntime.exec(cmd);
// call this method repeatedly until it returns true
private boolean processIsTerminated () {
try {
process.exitValue();
} catch (IllegalThreadStateException itse) {
return false;
}
return true;
}
Alternately, a similar method could return the exit value if the process had terminated, or some other specified value if not.
Process.waitFor() (javadoc) should work. If it doesn't work then either:
there's a bug in the JVM or the OS (highly unlikely for something like this), or
there is something about the process and/or your Java code that means that the process won't exit.
In current releases of Java you can also use Process.isAlive (javadoc) to test the process status without blocking until it finishes. For Java 7 and older there is a hacky solution that entails polling the process return code and catching an exception, but this is inefficient. You should upgrade to Java 8 or later as soon as possible!
Once the task is finished its goes for an indefinite wait. (I don't know why).
If this happening, then neither waitFor() or isAlive() will help.
The most likely reasons that a process launched from Java won't / can't exit are:
the process is blocked waiting for your Java application to give it some input (via its stdin),
the process is blocked waiting for your Java application to read its output (i.e. its stdout or stderr),
it is blocked waiting on some external event; e.g. if it is trying to talk remote server that is not responding,
something has sent it a STOP signal of some kind, or
it is just taking a looong time to run.
The first two of these reasons / causes can be addressed by (respectively) closing the Java output stream connected to its standard input, and reading (and possibly discarding) the Java input streams connected to its standard output and standard error. The other causes are intractable, and your only options are to "wait it out" or attempt to kill off the process.
Bottom line - you need to find out why your process isn't completing. The blocked Process.waitFor() call is a symptom, not the disease.
I have a similar issue and neither of the methods written here works for me. This is my code:
public void startCCleaner() {
System.out.println("Starting ccleaner...");
try {
Process process = new ProcessBuilder("C:\\Program Files\\CCleaner\\CCleaner64.exe").start();
if(process.waitFor() == 0 ){
System.out.println("Process terminated ");
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
If you don't want to use waitFor(), which apparently you don't you can always test the exit value directly.
import java.util.*;
import java.io.*;
public class ProcExitTest
{
public static void main(String args[])
{
try
{
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("<....>");
int exitVal = proc.exitValue();
System.out.println("Process exitValue: " + exitVal);
}
catch (InterruptedException ie)
{
ie.printStackTrace();
}
}
}
exit code 0 means normal termination.
I just read the Trail on RMI from sun at http://java.sun.com/docs/books/tutorial/rmi/implementing.html
When I run the example, the JVM does not terminate although main has finished. Is RMI spawning a Thread somewhere internally?
What is the behaviour of multiple Threads spawned in main, after main exits?
Is it a clean way to let the Threads exit whenever they want or should you do a join on each Thread you spawn? I did not find any documentation on this question.
Thank you very much for your help!!
public class ComputeEngine implements Compute {
public ComputeEngine() {
super();
}
public <T> T executeTask(Task<T> t) {
return t.execute();
}
public static void main(String[] args) {
if (System.getSecurityManager() == null) {
System.setSecurityManager(new SecurityManager());
}
try {
String name = "Compute";
Compute engine = new ComputeEngine();
Compute stub = (Compute) UnicastRemoteObject.exportObject(engine, 0);
Registry registry = LocateRegistry.getRegistry();
registry.rebind(name, stub);
System.out.println("ComputeEngine bound");
} catch (Exception e) {
System.err.println("ComputeEngine exception:");
e.printStackTrace();
}
}
}
A thread is created for listening the socket and reply to requests to your object. One way to stop the JVM is to unbind the server:
Registry.unbind()
and unexport the objects:
UnicastRemoteObject.unexportObject()).
You can use the jstack utility program, included in the JDK to see which threads are running in a java program, and even what line those threads are on.
So if you're program is still running you just run jstack on the pid and it will tell you which threads are still running and what they're doing.
Concerning the "behaviour of multiple threads" part of your question: yes, the JVM will keep on running until all threads finished. The only exception are threads marked as daemon (see Thread.setDaemon()), the JVM will not wait for them.
Yes, when you are exposing objects through RMI it needs a thread to accept incoming requests for these objects. This thread could be a daemon thread which wouldn't stop the JVM from exiting but it isn't for several reasons and as long as there are still active exported objects it hinders the JVM from exiting normally. So you can use unexportObject for all objects or just use System.exit() to end the JVM although this would leave the clients uninformed about the shutdown.