Wednesday, May 3, 2023

Don't Call System.exit() on Java Web Application in Tomcat - Best Practice

I have recently come across a code snippet, where the programmer was using System.exit() if the application failed to acquire necessary resources after a couple of retries. His reasoning was that the application cannot function if essential resources like the database are not available or there is no disk space to write records in the File system. Ok, I hear you; but System.exit() in Java Web application, which runs inside either web server or application server, which itself is Java program is not a good idea at all. Why? because invoking System.exit() kills your JVM, invoking this from Tomcat or Jetty, will not only kill your application but the most likely server itself. This can be potentially dangerous if that server also hosts other critical applications, which is not uncommon at all.

As per my experience, System.exit() calls are quite common in overly broad try-catch blocks in web application start-up code that loads environment variables, properties files, connect to MQ Series, establishes database connection, opens socket connections, etc.

This is still ok if you are writing a core Java based server, where each application has its own JVM, but with web application deployed on Tomcat, JBoss, WebSphere, Weblogic, or any other application server, using System.exit() is a big mistake. In worst case can result in an outage for lots of other critical application.

On the other hand, there are ways to prevent your web application from someone else’s mistake, by enabling Security Manager. System.exit() and Runtime.exit() both go through the security manager. Enabling the Security manager will catch these calls and reduce them into an exception rather than shutting down the whole VM.

It's not difficult to enable the security manager in most application servers, Tomcat, JBoss both have documented steps to enable security Manager.




Why you should not use System.exit() in Java Web application

I think it's well known for senior Java developer that System.exit() shouldn't be used except in e.g. command-line tools; Many beginner Java programmers, though familiar with System.exit(), may not know that using them in a Servlet/JSP code can result in shutdown of server itself, so if you have of any of them in your team them take some time to educate them about coding in Java web application.

In fact coding a Java based web application is totally different then coding a core Java application like core concerns like threading, object pooling, parsing are all done by Web server and it's prohibited for application code to create threads.

In fact use of ThreadLocal variable can create memory leak in Java web-app, so coding in web-app require more caution than in core Java application.

By the way there are other reasons, why using System.exit is absolutely terrible. Especially when you dealing with un-managed resources, if you don't release resources properly and hope that OS will do the clean-up for you, then it could lead to a temporary resource leak, until OS really clean stuff created by your Java application.

Using System.exit() in Java Web Application


What does this all mean? Do System.exit() has any legitimate use? Of course there are many cases where use of System.exit is imperative. Sometime you really want to close your JVM once done, especially if it spawned from scheduling software like Control-M or Autosys. For example command line Java applications and scheduled tasks, can use System.exit(). Ideally, if you are sure that calling System.exit() will not have any side effect, you can use it. Here is a sample use of system.exit() in core Java application :

public static void main( String[] args ){

    try
    {
        processFxRatesFileAndPutIntoDatabase();

    }  catch ( Exception e )
    {
        logError(e);
        // if you need to exit with a failed status then System.exit(statusCode)
        // is fine here.
        // otherwise program will complete successfully, once it return from main() method
    }

 }

Remember, if you are using Java program as command-line tool then you can return System.exit(0) or System.exit(1) as whether it succeeded or failed. A non-zero exit status code, usually indicates abnormal termination.

You can return different error codes to indicate different errors e.g. bad arguments, can't find file, could not connect to database etc. I hope you notice the difference between using System.exit(1) and letting the Java program complete successfully. When java platform will terminate with System.exit() it will do so with a non-zero status (as long as the main thread ends and there are no running daemon threads).


That’s all about Why you should not use System.exit() inside Java application. It can be dangerous and potentially shut down the whole server, which is hosting other critical Java application. For example, if you have more than one application in tomcat and one of them call System.exit() then the whole tomcat will be shut down and all other web applications will also be shutdown.

As a safety net or Insurance, you must enable the Security Manager of your Server. It catches a lot of security problems! Including an intentional and unintentional call to System.exit() or Runtime.exit(), turning it into an exception rather than shutting down the whole JVM.

You are relatively safe to use System.exit() in command-line Java application, as they run on their own JVM and can be used to signal success and failure by returning different status code in case of failure.


8 comments :

SARAL SAXENA said...

@Javin ..well still I can see and people ask lots of questions in case of web application with respect to servlet specially..that..

Is calling System.exit() inside a Servlet's destroy() method to forcefully kill any non-daemon threads a good idea?

Calling System.exit() inside ANY servlet-related method is always 100% incorrect. Your code is not the only code running in the JVM - even if you are the only servlet running (the servlet container has resources it will need to cleanup when the JVM really exits.)

The correct way to handle this case is to clean up your threads in the destroy() method. This means starting them in a way that lets you gently stop them in a correct way. Here is an example (where MyThread is one of your threads, and extends ServletManagedThread):

public class MyServlet extends HttpServlet {
private List threads = new ArrayList();

// lots of irrelevant stuff left out for brevity

public void init() {
ServletManagedThread t = new MyThread();
threads.add(t);
t.start();
}

public void destroy() {
for(ServletManagedThread thread : threads) {
thread.stopExecuting();
}
}
}

public abstract class ServletManagedThread extends Thread {

private boolean keepGoing = true;

protected abstract void doSomeStuff();
protected abstract void probablySleepForABit();
protected abstract void cleanup();

public void stopExecuting() {
keepRunning = false;
}

public void run() {
while(keepGoing) {
doSomeStuff();
probablySleepForABit();
}
this.cleanup();
}
}




Anonymous said...

Indeed, destroy() method is there for a reason and that is cleanup. This is also very important to prevent memory leak in Java application e.g. closing any database connection or releasing file handlers etc. In any case, you should not call System.exit() if you are running in a java server e.g. tomcat, glassfish, weblogic, websphere or jboss.

Verhás Péter said...

If the servlet container runs servlets under the default security context on a mission critical server it deserves to be crashed. And sysadmin should be fired.

rockey said...

i have a web application running in production on linux server so how can i stop that application and deploy my new jars ?what is the correct way

javin paul said...

Hello @rockey, if you just have your web application deployed in the server then you can stop the server, deploy new WAR file and restart the server. If it contains other application which cannot be stopped, then you need a webapp similar to manager webapp of tomcat. The stop and start the server is what many company use.

Anonymous said...

Fantastic explanation, thanks so much!

Anonymous said...

So, instead of System.exit(1) or System.exit(0), we can use something like below

throw new RuntimeException("error starting application"

javin paul said...

Hello @Unknown, yes you can throw RuntimeException whenever expected conditions are not met but in web application, you don't really start anything. When you deploy your WAR file , servlet container like Tomcat configure URL path and initialize Servlet to process HTTP request. When Request comes, a Servlet or Filter is assigned based upon web.xml, the process is still the tomcat which can run multiple web application.

Post a Comment