项目中有时需要在Java
类中启动一个长时间运行的服务进程(监听进程、监控进程等),且该进程需要在tomcat
启动时启动,在tomcat
停止时退出。在项目开发的过程中,我需要在Java类中启动一个监听进程,而在该进程成功启动后,一调用该监听服务进程时,该进程就挂掉(退出),现将解决方法记录如下。
ServletContextListener接口能够接收到web
context初始化和销毁的消息事件,因此我们可以在该接口中事件长时间运行进程的启动和停止。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 public class MyContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { ClassLoader classLoader = Thread.currentThread() .getContextClassLoader(); String scriptPath = classLoader.getResource("daemon.sh") .getPath(); List<String> commandsList = new ArrayList<String>(); commandsList.add("sh"); commandsList.add(scriptPath); commandsList.add("start"); ProcessBuilder processBuilder = new ProcessBuilder(commandsList); try { Process process = processBuilder.start(); //以下两行代码是关键 ServletContext servletContext = sce.getServletContext(); servletContext.setAttribute("daemon", process); int exitValue = process.waitFor(); if (0 == exitValue) { System.out.println("start daemon success."); } else { System.out.println("start daemon failed, exitValue: " + exitValue); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void contextDestroyed(ServletContextEvent sce) { ClassLoader classLoader = Thread.currentThread() .getContextClassLoader(); String scriptPath = classLoader.getResource("daemon.sh") .getPath(); List<String> commandsList = new ArrayList<String>(); commandsList.add("sh"); commandsList.add(scriptPath); commandsList.add("stop"); ProcessBuilder processBuilder = new ProcessBuilder(commandsList); try { Process process = processBuilder.start(); int exitValue = process.waitFor(); if (0 == exitValue) { System.out.println("stop daemon success."); } else { System.out.println("stop daemon failed, exitValue: " + exitValue); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
然后需要在web.xml
的所有filters
和servlets
定义之前定义:
1 2 3 <listener> <listener-class>org.readus.executescript.MyContextListener</listener-class> </listener>
如果使用Spring,则可使用如下设置代替上面的listener-class
:
1 2 3 <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
在上述的实现中,最重要的概括为两点:
实现了ServletContextListener
的接口
将生成的长时间运行的进程写入ServletContext
的属性中
Java
中使用ServletContextListener
接口来监听web应用的生命周期变化。其中包含的两个接口:
1 public void contextInitialized(ServletContextEvent sce);
该接口文档说明如下:
Receives notification that the web application initialization process
is starting.All ServletContextListeners are notified of context
initialization before any filters or servlets in the web application are
initialized. @param sce
the ServletContextEvent containing the ServletContext that is being
initialized.
1 public void contextDestroyed(ServletContextEvent sce);
该接口的文档说明如下:
Receives notification that the ServletContext is about to be shut
down. All servlets and filters will have been destroyed before any
ServletContextListeners are notified of context destruction. @param sce the
ServletContextEvent containing the ServletContext that is being
destroyed.
本文所涉及的代码托管在了GitHub上ExecuteScriptDemo .