码迷,mamicode.com
首页 > 其他好文 > 详细

tomat(16)关闭钩子

时间:2016-05-27 11:20:33      阅读:312      评论:0      收藏:0      [点我收藏+]

标签:

【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);
    }
}

tomat(16)关闭钩子

标签:

原文地址:http://blog.csdn.net/pacosonswjtu/article/details/51506556

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!