oddjob之smooth关闭程序

java程序的smooth关闭策略可以采用hook跟观察者的模式实现

无限等状态,如果状态出现可以关闭的事件则进行关闭

虚拟机的关闭通过钩子调用关闭,如果关闭失败,在超时时间内强制杀掉jvm

状态类

public interface State {

    public boolean isStoppable();
    
}
package com.s.stat;



/**
 * A Stateful job implements this interface so that 
 * it's state can be discovered. State is used to 
 * control the flow of execution within Oddjob, as
 * well as being a way of informing client applications
 * of progress.
 * 
 * @author Rob Gordon
 */
public interface Stateful {

    /**
     * Add a job state listener.
     * 
     * @param listener The listener.
     * 
     */
    public void addStateListener(StateListener listener) ;

    /**
     * Remove a job state listener.
     * 
     * @param listener The listener.
     */    
    public void removeStateListener(StateListener listener);

    /**
     * Get the last state event.
     * 
     * @return The last State Event
     */
    public StateEvent lastStateEvent();
    
}
package com.s.stat;

import java.io.Serializable;
import java.util.Date;
import java.util.EventObject;

/**
 * An instance of this class is produced when a job state changes. It is
 * passed to all JobStateListeners.
 * 
 * @author Rob Gordon
 */

public class StateEvent extends EventObject 
implements Serializable {

    private static final long serialVersionUID = 20051026;

    static final String REPLACEMENT_EXCEPTION_TEXT = "Exception is not serializable, message is: ";
    
    private State state;
    private Date time;
    private Throwable exception;
    
    /**
     * Used to replace a non serializable exception.
     *
     */
    class ExceptionReplacement extends Exception {
        private static final long serialVersionUID = 20051217;
        public ExceptionReplacement(Throwable replacing) {
            super(REPLACEMENT_EXCEPTION_TEXT + replacing.getMessage());
            super.setStackTrace(exception.getStackTrace());
        }
    }
    
    /**
     * Constructor.
     * 
     * @param job The source of the event.
     * @param jobState The state.
     * @param time the Time of the event.
     * @param exception The exception if applicable, or null otherwise.
     */    
    public StateEvent(Stateful job, State jobState, Date time, Throwable exception) {
        super(job);
        if (jobState == null) {
            throw new NullPointerException("JobState can not be null!");
        }
        this.state = jobState;
        this.time = time;
        this.exception = exception;
    }

    /**
     * Constructor.
     * 
     * @param job The source of the event.
     * @param jobState The state.
     * @param exception The exception if applicable, or null otherwise.
     */
    public StateEvent(Stateful job, State jobState, Throwable exception) {
        this(job, jobState, new Date(), exception);
    }

    /**
     * Constructor.
     * 
     * @param job The source of the event.
     * @param jobState The state.
     */
    public StateEvent(Stateful job, State jobState) {
        this(job, jobState, null);
    }

    @Override
    public Stateful getSource() {
        return (Stateful) super.getSource();
    }
    
    /**
     * Get the job state.
     * 
     * @return The job state.
     */    
    public State getState() {
        return state;    
    }

    /**
     * Get the exception if applicable, null otherwise.
     * 
     * @return The exception of null.
     */    
    public Throwable getException() {
        return exception;
    }    

    /**
     * Get the time of the event..
     * 
     * @return The time.
     */
    public Date getTime() {
        return time;
    }

    /**
     * Override toString.
     */
    public String toString() {
        return "JobStateEvent, source=" + getSource() + ", " + state;
    }
    
    
}
package com.s.stat;



/**
 * Implementors of this interface are able to listen to state events.
 * 
 * @author Rob Gordon
 */

public interface StateListener {


    /**
     * Triggered when the job state changes.
     * 
     * @param event The job state event.
     */    
    public void jobStateChange(StateEvent event);
    
}
package com.s;

import com.s.stat.StateEvent;
import com.s.stat.StateListener;
import com.s.stat.Stateful;


public class Runner implements Runnable{
    public static final String KILLER_TIMEOUT_PROPERTY = "shutdown.killer.timeout";

    public static final long DEFAULT_KILLER_TIMEOUT = 15000L;

    /** Flag if program is being destroyed from the Shutdown Hook. */
    private volatile boolean destroying = false;
    
    /** The killer thread time out. */
    private final long killerTimeout;

    public Runner() {
        String timeoutProperty = System.getProperty(KILLER_TIMEOUT_PROPERTY);
        if (timeoutProperty == null) {
            killerTimeout = DEFAULT_KILLER_TIMEOUT;
        } else {
            killerTimeout = Long.parseLong(timeoutProperty);
        }
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        Runtime.getRuntime().addShutdownHook(new ShutdownHook());        
        // Possibly wait for Oddjob to be in a stopped state.
        new StopWait(new Stateful() {
            
            @Override
            public void removeStateListener(StateListener listener) {
                // TODO Auto-generated method stub
                
            }
            
            @Override
            public StateEvent lastStateEvent() {
                // TODO Auto-generated method stub
                return null;
            }
            
            @Override
            public void addStateListener(StateListener listener) {
                // TODO Auto-generated method stub
                
            }
        }, Long.MAX_VALUE).run();
        
        // 调用程序的stop
        
        
    }

    /**
     * shutdown hook.
     * <p>
     * This Class has evolved quite a lot though trial and error due to
     * a lack of understanding of JVM shutdown. Should this thread be a
     * daemon? Current thinking is no because you don't want other daemon
     * threads to terminate until  has been shutdown properly.
     *
     */
    class ShutdownHook extends Thread {
        
        /** Killer thread will forcibly halt  if it hasn't terminated
         * cleanly. */
        private Thread killer;
        
        /*
         * (non-Javadoc)
         * @see java.lang.Thread#run()
         */
        public void run() {

        //    logger.info("Shutdown Hook Executing.");
            
            // killer will just kill process if we can't stop in 15 sec
            killer = new Thread(new Runnable() {
                public void run() {
                    try {
                        Thread.sleep(killerTimeout);
                    }
                    catch (InterruptedException e) {
                    //    logger.debug("Killer thread interrupted and terminating.");
                        return;
                    }
//                    logger.error("Failed to stop Oddjob nicely, using halt(-1)");
                    Runtime.getRuntime().halt(-1);
                }
            });
            
            // start the killer. Not sure it really need to be daemon but
            // it does no harm.
//            logger.debug("Starting killer thread.");
            killer.setDaemon(true);
            killer.start();
            
            // 调用程序的关闭
            
            
            // Nothing's hanging so we don't need our killer.
            //TODO 判断程序是否调用自身的关闭程序,如果成功则单端killer
//            killer.interrupt();
            
        }
    }
}
package com.s;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import com.s.stat.IsStoppable;
import com.s.stat.State;
import com.s.stat.StateEvent;
import com.s.stat.StateListener;
import com.s.stat.Stateful;

/**
 * A utility class to provide wait until stopped functionality.
 * <p>
 * The default timeout is 5 seconds before a {@link FailedToStopException}
 * is thrown.
 * 
 * @author rob
 *
 */
public class StopWait {

    private final Stateful stateful;
    
    private final long timeout;
    
    /**
     * Constructor with default timeout.
     * 
     * @param stateful The thing to wait until stopped.
     */
    public StopWait(Stateful stateful) {
        this(stateful, 5000);
    }
    
    /**
     * Constructor where timeout can be specified.
     * 
     * @param stateful The thing to wait until stopped.
     * @param timeout The timeout. Note that a timeout of 0 or less is
     * no timeout.
     */
    public StopWait(Stateful stateful, long timeout) {
        this.stateful = stateful;
        this.timeout = timeout;
    }
    
    /**
     * Run the stop wait. This will block until the job stops or the
     * timeout occurs.
     * 
     * @throws FailedToStopException If timeout occurs.
     */
    public void run()  {        

        if (new IsStoppable().test(
                stateful.lastStateEvent().getState())) {
            doWait();
        }
    }
    
    private void doWait() {        
        
        final BlockingQueue<State> handoff = new LinkedBlockingQueue<State>();
        
        class StopListener implements StateListener {
            
            @Override
            public void jobStateChange(StateEvent event) {
                handoff.add(event.getState());
            }
        };
        
        StopListener listener = new StopListener();
                
        stateful.addStateListener(listener);
        
        try {
            while (true) {

                State state = handoff.poll(timeout, TimeUnit.MILLISECONDS);
                if (state == null) {
                }
                if (!state.isStoppable()) {
                    return;
                }
            }
        }
        catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        finally {
            stateful.removeStateListener(listener);
        }
    }    
}
原文地址:https://www.cnblogs.com/blachie/p/3652484.html