tomat(16)关闭钩子

来源:互联网 发布:java就业指导 编辑:程序博客网 时间:2024/06/07 02:04
【0】REAMDE
0)本文部分文字描述转自:“how tomcat works”,旨在学习“tomat(16)关闭钩子”的相关知识;
1)problem+solution:
1.1)problem:在很多实际环境中,当用户关闭应用程序时,并不会按照推荐的方法关闭应用程序,很有可能不做清理工作;
1.2)solution:java 为程序员提供了一种优雅的方法可以在在关闭过程中执行一些代码,以确保那些负责善后处理的代码可能能够执行;
2)在java中,虚拟机会对两类事件(events)进行响应,然后执行关闭操作:
event1)当调用System.exit()方法或程序的最后一个非守护进程线程退出时,应用程序正常退出;
event2)用户突然强制虚拟机中断运行,例如用户按 CTRL+C 快捷键或在未关闭java程序的case下,从系统中退出;
3)虚拟机在执行关闭操作时,会经过以下两个阶段(stages):
stage1)虚拟机启动所有已经注册的关闭钩子,如果有的话。关闭钩子是先前已经通过 Runtime 类注册的线程,所有的关闭钩子会并发执行,直到完成任务;(干货——关闭钩子是线程)
stage2)虚拟机根据case 调用所有没有被调用过的 终结期(finalizer);
Attention)本文重点说明第一个stage,因为该阶段允许程序员告诉虚拟机在应用程序中执行一些清理代码。
4)创建关闭钩子很简单(steps):
step1)创建Thread类的一个子类 ;
step2)实现你自己的run()方法,当应用程序(正常或突然)关闭时,会调用此方法;
step3)在应用程序中,实例化关闭钩子类;
step4)使用当前 Runtime.addShutdownHook() 方法注册关闭钩子;
5)看个荔枝(关于 创建钩子的测试用例):
package com.tomcat.chapter16.shutdownhook;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) {  }  System.out.println("Normal exit"); }}class ShutdownHook extends Thread { public void run() {  System.out.println("Shutting down"); }}
对以上代码的分析(Analysis):
A1)在实例化ShutdownHookDemo 类后,main()方法会调用start()方法;start()方法会创建一个关闭钩子,并通过当前运行时注册它:
ShutdownHook shutdownHook = new ShutdownHook();  Runtime.getRuntime().addShutdownHook(shutdownHook); // 添加一个关闭钩子.
A2)然后,应用程序会等待用户输入: System.in.read();
A3)当用户按 enter 键后,应用程序会退出。虚拟机会执行关闭钩子,输入字符串“Shutting down”;


【1】关闭钩子的荔枝
1)源代码如下(该应用程序在启动时会创建一个临时文件,并在关闭时删除该临时文件)
public class MySwingAppWithoutShutdownHook extends JFrame { //不带有关闭钩子的swing JButton exitButton = new JButton(); JTextArea jTextArea1 = new JTextArea(); String dir = System.getProperty("user.dir"); String filename = "temp.txt"; public MySwingApp() {  exitButton.setText("Exit");  exitButton.setBounds(new Rectangle(304, 248, 76, 37));  exitButton.addActionListener(new java.awt.event.ActionListener() {   public void actionPerformed(ActionEvent e) {    exitButton_actionPerformed(e);   }  });   this.getContentPane().setLayout(null);  jTextArea1.setText("Click the Exit button to quit");  jTextArea1.setBounds(new Rectangle(9, 7, 371, 235));  this.getContentPane().add(exitButton, null);  this.getContentPane().add(jTextArea1, null);  this.setDefaultCloseOperation(EXIT_ON_CLOSE);  this.setBounds(0, 0, 400, 330);  this.setVisible(true);  initialize(); } private void initialize() {  // create a temp file  File file = new File(dir, filename);  try {   System.out.println("Creating temporary file");   file.createNewFile();  } catch (IOException e) {   System.out.println("Failed creating temporary file.");  } } private void shutdown() {  // delete the temp file  File file = new File(dir, filename);  if (file.exists()) {   System.out.println("Deleting temporary file.");   file.delete();  } } void exitButton_actionPerformed(ActionEvent e) {  shutdown();  System.exit(0); } public static void main(String[] args) {  MySwingApp mySwingApp = new MySwingApp(); }}
对以上代码的分析(Analysis):
step1)应用程序会调用其 initialize()方法;
step2)initialize()方法会在用户目录中创建一个临时文件,名为 “temp.txt”;
step3)当用户关闭应用程序时,该程序会删除该临时文件;
2)problem+solution
2.1)problem:我们希望用户总是能够单击exit 来退出,这动作监听器就会调用shutdown()方法,可以删除临时文件了;但如果用户通过单击右上角的关闭按钮或是通过其他方式强制退出,则临时文件就无法删除了;
2.2)solution:使用关闭钩子来删除临时文件;关闭钩子是一个内部类,这样它就能够访问主类的所有方法;(干货——关闭钩子是一个内部类,这样它就能够访问主类的所有方法)
3)带有关闭钩子的源代码如下:
public class MySwingAppWithShutdownHook extends JFrame { //  带有关闭钩子的swing.  JButton exitButton = new JButton();  JTextArea jTextArea1 = new JTextArea();  String dir = System.getProperty("user.dir");  String filename = "temp.txt";  public MySwingAppWithShutdownHook() {    exitButton.setText("Exit");    exitButton.setBounds(new Rectangle(304, 248, 76, 37));    exitButton.addActionListener(new java.awt.event.ActionListener() {      public void actionPerformed(ActionEvent e) {        exitButton_actionPerformed(e);      }    });    this.getContentPane().setLayout(null);    jTextArea1.setText("Click the Exit button to quit");    jTextArea1.setBounds(new Rectangle(9, 7, 371, 235));    this.getContentPane().add(exitButton, null);    this.getContentPane().add(jTextArea1, null);    this.setDefaultCloseOperation(EXIT_ON_CLOSE);    this.setBounds(0,0, 400, 330);    this.setVisible(true);    initialize();  }  private void initialize() {    // add shutdown hook    MyShutdownHook shutdownHook = new MyShutdownHook();    Runtime.getRuntime().addShutdownHook(shutdownHook);    // create a temp file    File file = new File(dir, filename);    try {      System.out.println("Creating temporary file");      file.createNewFile();    }    catch (IOException e) {      System.out.println("Failed creating temporary file.");    }  }  private void shutdown() { // highlight line.    // delete the temp file    File file = new File(dir, filename);    if (file.exists()) {      System.out.println("Deleting temporary file.");      file.delete();    }  }  void exitButton_actionPerformed(ActionEvent e) {    shutdown();    System.exit(0);  }  public static void main(String[] args) {    MySwingAppWithShutdownHook mySwingApp = new MySwingAppWithShutdownHook();  }  private class MyShutdownHook extends Thread { // highlight line.    public void run() {      shutdown(); // highlight line.    }  }}
对以上代码的分析(Analysis):
step1)程序会首先创建一个内部类MyShutdownHook的一个实例,即钩子类实例,并将注册该钩子实例;
step2)其他的代码与MySwingAppWithoutShutdownHook的源代码类似;
Attention)唯一不同的是,当突然关闭应用程序时,该临时文件总是会被删除;

【2】tomcat中的关闭钩子
1)intro:tomcat也是通过关闭钩子来完成退出过程的;
2)org.apache.catalina.startup.Catalina类:负责启动管理其他组件的Server对象。一个名为 CatalinaShutdownHook的内部类继承自 java.lang.Thread类,提供了run()方法的实现,它会调用 Server.stop()方法,执行关闭操作;

Supplement)本文习惯性地给出 Catalina.main()方法的调用过程:

protected void start() { //org.apache.catalina.startup.Catalina.start().//.....               // Replace System.out and System.err with a custom PrintStream        SystemLogHandler log = new SystemLogHandler(System.out);        System.setOut(log);        System.setErr(log);        Thread shutdownHook = new CatalinaShutdownHook(); // 创建关闭钩子        // Start the new server        if (server instanceof Lifecycle) {            try {                server.initialize();                ((Lifecycle) server).start();                try {                    // Register shutdown hook                    Runtime.getRuntime().addShutdownHook(shutdownHook); // 添加关闭钩子.                } catch (Throwable t) {                    // This will fail on JDK 1.2. Ignoring, as Tomcat can run                    // fine without the shutdown hook.                }                // Wait for the server to be told to shut down                server.await();            } //......        }        // Shut down the server        if (server instanceof Lifecycle) {            try {                try {                    // Remove the ShutdownHook first so that server.stop()                     // doesn't get invoked twice                    Runtime.getRuntime().removeShutdownHook(shutdownHook);                } catch (Throwable t) {                    // This will fail on JDK 1.2. Ignoring, as Tomcat can run                    // fine without the shutdown hook.                }                ((Lifecycle) server).stop();            } //......        }    }

 protected class CatalinaShutdownHook extends Thread { //org.apache.catalina.startup.Catalina.CatalinaShutdownHook// an inner class defined in Catalina        public void run() {            if (server != null) {                try {                    ((Lifecycle) server).stop(); // highlight line.                } catch (LifecycleException e) {                    System.out.println("Catalina.stop: " + e);                    e.printStackTrace(System.out);                    if (e.getThrowable() != null) {                        System.out.println("----- Root Cause -----");                        e.getThrowable().printStackTrace(System.out);                    }                }            }                      }    }
Supplement)
S1)在Catalina实例启动时,会实例化关闭钩子,并在一个阶段将其添加到 Runtime类中;
S2)org.apache.catalina.startup.Catalina 类的源代码如下所示:(其中Catalina.createStartDigester() 创建了很多规则,规则集参见 tomcat(15)Digester库)
 public class Catalina {     protected String configFile = "conf/server.xml";       protected boolean debug = false;     protected ClassLoader parentClassLoader =        ClassLoader.getSystemClassLoader();       protected Server server = null;     protected boolean starting = false;     protected boolean stopping = false;     protected boolean useNaming = true;      public static void main(String args[]) {        (new Catalina()).process(args);    }     public void process(String args[]) {        setCatalinaHome();        setCatalinaBase();        try {            if (arguments(args))                execute();        } catch (Exception e) {            e.printStackTrace(System.out);        }    }     public void setParentClassLoader(ClassLoader parentClassLoader) {        this.parentClassLoader = parentClassLoader;    }      public void setServer(Server server) {        this.server = server;    }     protected boolean arguments(String args[]) {        boolean isConfig = false;        if (args.length < 1) {            usage();            return (false);        }        for (int i = 0; i < args.length; i++) {            if (isConfig) {                configFile = args[i];                isConfig = false;            } else if (args[i].equals("-config")) {                isConfig = true;            } else if (args[i].equals("-debug")) {                debug = true;            } else if (args[i].equals("-nonaming")) {                useNaming = false;            } else if (args[i].equals("-help")) {                usage();                return (false);            } else if (args[i].equals("start")) {                starting = true;            } else if (args[i].equals("stop")) {                stopping = true;            } else {                usage();                return (false);            }        }        return (true);    }       protected File configFile() {        File file = new File(configFile);        if (!file.isAbsolute())            file = new File(System.getProperty("catalina.base"), configFile);        return (file);    }    protected Digester createStartDigester() {        // Initialize the digester        Digester digester = new Digester();        digester.setClassLoader(StandardServer.class.getClassLoader());        if (debug)            digester.setDebug(999);        digester.setValidating(false);        // Configure the actions we will be using        digester.addObjectCreate("Server",                                 "org.apache.catalina.core.StandardServer",                                 "className");        digester.addSetProperties("Server");        digester.addSetNext("Server",                            "setServer",                            "org.apache.catalina.Server");        digester.addObjectCreate("Server/GlobalNamingResources",                                 "org.apache.catalina.deploy.NamingResources");        digester.addSetProperties("Server/GlobalNamingResources");        digester.addSetNext("Server/GlobalNamingResources",                            "setGlobalNamingResources",                            "org.apache.catalina.deploy.NamingResources");        digester.addObjectCreate("Server/Listener",                                 null, // MUST be specified in the element                                 "className");        digester.addSetProperties("Server/Listener");        digester.addSetNext("Server/Listener",                            "addLifecycleListener",                            "org.apache.catalina.LifecycleListener");        digester.addObjectCreate("Server/Service",                                 "org.apache.catalina.core.StandardService",                                 "className");        digester.addSetProperties("Server/Service");        digester.addSetNext("Server/Service",                            "addService",                            "org.apache.catalina.Service");        digester.addObjectCreate("Server/Service/Listener",                                 null, // MUST be specified in the element                                 "className");        digester.addSetProperties("Server/Service/Listener");        digester.addSetNext("Server/Service/Listener",                            "addLifecycleListener",                            "org.apache.catalina.LifecycleListener");        digester.addObjectCreate("Server/Service/Connector",                                 "org.apache.catalina.connector.http.HttpConnector",                                 "className");        digester.addSetProperties("Server/Service/Connector");        digester.addSetNext("Server/Service/Connector",                            "addConnector",                            "org.apache.catalina.Connector");        digester.addObjectCreate("Server/Service/Connector/Factory",                                 "org.apache.catalina.net.DefaultServerSocketFactory",                                 "className");        digester.addSetProperties("Server/Service/Connector/Factory");        digester.addSetNext("Server/Service/Connector/Factory",                            "setFactory",                            "org.apache.catalina.net.ServerSocketFactory");        digester.addObjectCreate("Server/Service/Connector/Listener",                                 null, // MUST be specified in the element                                 "className");        digester.addSetProperties("Server/Service/Connector/Listener");        digester.addSetNext("Server/Service/Connector/Listener",                            "addLifecycleListener",                            "org.apache.catalina.LifecycleListener");        // Add RuleSets for nested elements        digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));        digester.addRuleSet(new EngineRuleSet("Server/Service/"));        digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Default"));        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/DefaultContext/"));        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/Default"));        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/DefaultContext/"));        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));        digester.addRule("Server/Service/Engine",                         new SetParentClassLoaderRule(digester,                                                      parentClassLoader));        return (digester);    }       protected Digester createStopDigester() {        // Initialize the digester        Digester digester = new Digester();        if (debug)            digester.setDebug(999);        // Configure the rules we need for shutting down        digester.addObjectCreate("Server",                                 "org.apache.catalina.core.StandardServer",                                 "className");        digester.addSetProperties("Server");        digester.addSetNext("Server",                            "setServer",                            "org.apache.catalina.Server");        return (digester);    }      protected void execute() throws Exception {        if (starting)            start();        else if (stopping)            stop();    }    protected void setCatalinaBase() {        if (System.getProperty("catalina.base") != null)            return;        System.setProperty("catalina.base",                           System.getProperty("catalina.home"));    }        protected void setCatalinaHome() {        if (System.getProperty("catalina.home") != null)            return;        System.setProperty("catalina.home",                           System.getProperty("user.dir"));    }     protected void start() {        // Setting additional variables        if (!useNaming) {            System.setProperty("catalina.useNaming", "false");        } else {            System.setProperty("catalina.useNaming", "true");            String value = "org.apache.naming";            String oldValue =                System.getProperty(javax.naming.Context.URL_PKG_PREFIXES);            if (oldValue != null) {                value = value + ":" + oldValue;            }            System.setProperty(javax.naming.Context.URL_PKG_PREFIXES, value);            value = System.getProperty                (javax.naming.Context.INITIAL_CONTEXT_FACTORY);            if (value == null) {                System.setProperty                    (javax.naming.Context.INITIAL_CONTEXT_FACTORY,                     "org.apache.naming.java.javaURLContextFactory");            }        }        // Create and execute our Digester        Digester digester = createStartDigester();        File file = configFile();        try {            InputSource is =                new InputSource("file://" + file.getAbsolutePath());            FileInputStream fis = new FileInputStream(file);            is.setByteStream(fis);            digester.push(this);            digester.parse(is);            fis.close();        } catch (Exception e) {            System.out.println("Catalina.start using "                               + configFile() + ": " + e);            e.printStackTrace(System.out);            System.exit(1);        }        // If a SecurityManager is being used, set properties for        // checkPackageAccess() and checkPackageDefinition        if( System.getSecurityManager() != null ) {            String access = Security.getProperty("package.access");            if( access != null && access.length() > 0 )                access += ",";            else                access = "sun.,";            Security.setProperty("package.access",                access +                 "org.apache.catalina.,org.apache.jasper.");            String definition = Security.getProperty("package.definition");            if( definition != null && definition.length() > 0 )                definition += ",";            else                definition = "sun.,";            Security.setProperty("package.definition",                // FIX ME package "javax." was removed to prevent HotSpot                // fatal internal errors                definition +                 "java.,org.apache.catalina.,org.apache.jasper.,org.apache.coyote.");        }        // Replace System.out and System.err with a custom PrintStream        SystemLogHandler log = new SystemLogHandler(System.out);        System.setOut(log);        System.setErr(log);        Thread shutdownHook = new CatalinaShutdownHook();        // Start the new server        if (server instanceof Lifecycle) {            try {                server.initialize();                ((Lifecycle) server).start();                try {                    // Register shutdown hook                    Runtime.getRuntime().addShutdownHook(shutdownHook);                } catch (Throwable t) {                    // This will fail on JDK 1.2. Ignoring, as Tomcat can run                    // fine without the shutdown hook.                }                // Wait for the server to be told to shut down                server.await();            } catch (LifecycleException e) {                System.out.println("Catalina.start: " + e);                e.printStackTrace(System.out);                if (e.getThrowable() != null) {                    System.out.println("----- Root Cause -----");                    e.getThrowable().printStackTrace(System.out);                }            }        }        // Shut down the server        if (server instanceof Lifecycle) {            try {                try {                    // Remove the ShutdownHook first so that server.stop()                     // doesn't get invoked twice                    Runtime.getRuntime().removeShutdownHook(shutdownHook);                } catch (Throwable t) {                    // This will fail on JDK 1.2. Ignoring, as Tomcat can run                    // fine without the shutdown hook.                }                ((Lifecycle) server).stop();            } catch (LifecycleException e) {                System.out.println("Catalina.stop: " + e);                e.printStackTrace(System.out);                if (e.getThrowable() != null) {                    System.out.println("----- Root Cause -----");                    e.getThrowable().printStackTrace(System.out);                }            }        }    }        protected void stop() {        // Create and execute our Digester        Digester digester = createStopDigester();        File file = configFile();        try {            InputSource is =                new InputSource("file://" + file.getAbsolutePath());            FileInputStream fis = new FileInputStream(file);            is.setByteStream(fis);            digester.push(this);            digester.parse(is);            fis.close();        } catch (Exception e) {            System.out.println("Catalina.stop: " + e);            e.printStackTrace(System.out);            System.exit(1);        }      // Stop the existing server      try {          Socket socket = new Socket("127.0.0.1", server.getPort());          OutputStream stream = socket.getOutputStream();          String shutdown = server.getShutdown();          for (int i = 0; i < shutdown.length(); i++)              stream.write(shutdown.charAt(i));          stream.flush();          stream.close();          socket.close();      } catch (IOException e) {          System.out.println("Catalina.stop: " + e);          e.printStackTrace(System.out);          System.exit(1);      }    }        protected void usage() {        System.out.println            ("usage: java org.apache.catalina.startup.Catalina"             + " [ -config {pathname} ] [ -debug ]"             + " [ -nonaming ] { start | stop }");    }       protected class CatalinaShutdownHook extends Thread {        public void run() {            if (server != null) {                try {                    ((Lifecycle) server).stop();                } catch (LifecycleException e) {                    System.out.println("Catalina.stop: " + e);                    e.printStackTrace(System.out);                    if (e.getThrowable() != null) {                        System.out.println("----- Root Cause -----");                        e.getThrowable().printStackTrace(System.out);                    }                }            }                    }    }} final class SetParentClassLoaderRule extends Rule {    public SetParentClassLoaderRule(Digester digester,                                    ClassLoader parentClassLoader) {        super(digester);        this.parentClassLoader = parentClassLoader;    }    ClassLoader parentClassLoader = null;    public void begin(Attributes attributes) throws Exception {        if (digester.getDebug() >= 1)            digester.log("Setting parent class loader");        Container top = (Container) digester.peek();        top.setParentClassLoader(parentClassLoader);    }}

0 0