Let's talk about it first
Recently, I have been studying a domestic open source MySQL database middleware. After pulling down its latest version of the code to eclipse, starting it up, and doing various tests and code tracking. When I want to close it after using it, I pull out its STOP class and want to run it. I found that only the following lines of code were written in this class, so I felt that I was hurt a lot in an instant.
public static void main(String[] args) { System.out.println(new Date() + ",server shutdown!"); }When this middleware is started and run, listening is enabled, many threads are started running, and there are many socket connections. But there is no elegant way to turn it off. So in desperation, I could only go to the little red dot of eclipse and forcefully stopped the VM.
If it is a software with a good architecture, modularity and clear structure, especially a Server-like software, it is very important to have a set of life cycle management mechanisms. Not only can it manage the life cycle of each module, it can also be more elegant when starting and stopping the entire software without missing any resources.
Simple implementation of life cycle mechanism
Lifecycle state
The life cycle state of a module generally has the following:
Newborn-> Initialization-> Initialization Completed-> Initialization-> Initialization Completed-> Initialization-> Initialization-> Pausing-> Recovering-> Recovering-> Destroying-> Destroying-> Destroying. If the conversion between any states fails, it will enter another state: failure.
To do this, you can use an enumeration class to enumerate these states, as shown below:
public enum LifecycleState { NEW, //Newsen INITIALIZING, INITIALIZED, //Initialize STARTING, STARTED, //Start SUSPENDING, SUSPENDED, //Pause RESUMING, RESUMED,//Restore DESTROYING, DESTROYED,//Destroy FAILED;//Failed}interface
Various behavioral norms in the life cycle also require an interface to define, as shown below:
public interface ILifecycle { /** * Initialize* * @throws LifecycleException */ public void init() throws LifecycleException; /** * Start* * @throws LifecycleException */ public void start() throws LifecycleException; /** * Pause* * @throws LifecycleException */ public void suspend() throws LifecycleException; /** * Recover* * @throws LifecycleException */ public void resume() throws LifecycleException; /** * Destroy* * @throws LifecycleException */ public void destroy() throws LifecycleException; /** * Add lifecycle listener* * @param listener */ public void addLifecycleListener(ILifecycleListener listener); /** * Delete lifecycle listener* * @param listener */ public void removeLifecycleListener(ILifecycleListener listener);}When a lifecycle state transition occurs, listeners who are interested in a certain type of event may need to be triggered, so ILifeCycle also defines two methods to add and remove listeners. They are: public void addLifecycleListener(ILifecycleListener listener); and public void removeLifecycleListener(ILifecycleListener listener);
The listener also defines its behavioral norms by an interface, as shown below:
public interface ILifecycleListener { /** * Handle lifecycle events* * @param event Lifecycle events*/ public void lifecycleEvent(LifecycleEvent event);}Lifecycle events are represented by LifecycleEvent, as shown below:
public final class LifecycleEvent { private LifecycleState state; public LifecycleEvent(LifecycleState state) { this.state = state; } /** * @return the state */ public LifecycleState getState() { return state; }}Skeleton implementation
With the ILifeCycle interface, any class that implements this interface will be used as a lifecycle management object. This class can be a socket listening service, or it can represent a specific module, etc. So do we just need to implement ILifeCycle? It can be said that, but considering that each life cycle management object will have some common behaviors at various stages of the life cycle, such as:
Setting your own life cycle state to check whether the transition of the state conforms to logic to notify the listener that the life cycle state has changed. Therefore, it is of great significance to provide an abstract class AbstractLifeCycle as the skeleton implementation of ILifeCycle, which avoids a lot of duplicate code and makes the architecture clearer. This abstract class will implement all interface methods defined in ILifeCycle and add corresponding abstract methods for subclass implementation. AbstractLifeCycle can be implemented like this:
public abstract class AbstractLifecycle implements ILifecycle { private List<ILifecycleListener> listeners = new CopyOnWriteArrayList<ILifecycleListener>(); /** * state represents the current lifecycle state*/ private LifecycleState state = LifecycleState.NEW; /* * @see ILifecycle#init() */ @Override public final synchronized void init() throws LifecycleException { if (state != LifecycleState.NEW) { return; } setStateAndFireEvent(LifecycleState.INITIALIZING); try { init0(); } catch (Throwable t) { setStateAndFireEvent(LifecycleState.FAILED); if (t instanceof LifecycleException) { throw (LifecycleException) t; } else { throw new LifecycleException(formatString( "Failed to initialize {0}, Error Msg: {1}", toString(), t.getMessage()), t); } } setStateAndFireEvent(LifecycleState.INITIALIZED); } protected abstract void init0() throws LifecycleException; /* * @see ILifecycle#start() */ @Override public final synchronized void start() throws LifecycleException { if (state == LifecycleState.NEW) { init(); } if (state != LifecycleState.INITIALIZED) { return; } setStateAndFireEvent(LifecycleState.STARTING); try { start0(); } catch (Throwable t) { setStateAndFireEvent(LifecycleState.FAILED); if (t instanceof LifecycleException) { throw (LifecycleException) t; } else { throw new LifecycleException(formatString("Failed to start {0}, Error Msg: {1}", toString(), t.getMessage()), t); } } setStateAndFireEvent(LifecycleState.STARTED); } protected abstract void start0() throws LifecycleException; /* * @see ILifecycle#suspend() */ @Override public final synchronized void suspend() throws LifecycleException { if (state == LifecycleState.SUSPENDING || state == LifecycleState.SUSPENDED) { return; } if (state != LifecycleState.STARTED) { return; } setStateAndFireEvent(LifecycleState.SUSPENDING); try { suspend0(); } catch (Throwable t) { setStateAndFireEvent(LifecycleState.FAILED); if (t instanceof LifecycleException) { throw (LifecycleException) t; } else { throw new LifecycleException(formatString("Failed to suspend {0}, Error Msg: {1}", toString(), t.getMessage()), t); } } setStateAndFireEvent(LifecycleState.SUSPENDED); } protected abstract void suspend0() throws LifecycleException; /* * @see ILifecycle#resume() */ @Override public final synchronized void resume() throws LifecycleException { if (state != LifecycleState.SUSPENDED) { return; } setStateAndFireEvent(LifecycleState.RESUMING); try { resume0(); } catch (Throwable t) { setStateAndFireEvent(LifecycleState.FAILED); if (t instanceof LifecycleException) { throw (LifecycleException) t; } else { throw new LifecycleException(formatString("Failed to resume {0}, Error Msg: {1}", toString(), t.getMessage()), t); } } setStateAndFireEvent(LifecycleState.RESUMED); } protected abstract void resume0() throws LifecycleException; /* * @see ILifecycle#destroy() */ @Override public final synchronized void destroy() throws LifecycleException { if (state == LifecycleState.DESTROYING || state == LifecycleState.DESTROYED) { return; } setStateAndFireEvent(LifecycleState.DESTROYING); try { destroy0(); } catch (Throwable t) { setStateAndFireEvent(LifecycleState.FAILED); if (t instanceof LifecycleException) { throw (LifecycleException) t; } else { throw new LifecycleException(formatString("Failed to destroy {0}, Error Msg: {1}", toString(), t.getMessage()), t); } } setStateAndFireEvent(LifecycleState.DESTROYED); } protected abstract void destroy0() throws LifecycleException; /* * @see * ILifecycle#addLifecycleListener(ILifecycleListener) */ @Override public void addLifecycleListener(ILifecycleListener listener) { listeners.add(listener); } /* * @see * ILifecycle#removeListener(ILifecycleListener) */ @Override public void removeLifecycleListener(ILifecycleListener listener) { listeners.remove(listener); } private void fireLifecycleEvent(LifecycleEvent event) { for (Iterator<ILifecycleListener> it = listeners.iterator(); it.hasNext();) { ILifecycleListener listener = it.next(); listener.lifecycleEvent(event); } } protected synchronized LifecycleState getState() { return state; } private synchronized void setStateAndFireEvent(LifecycleState newState) throws LifecycleException { state = newState; fireLifecycleEvent(new LifecycleEvent(state)); } private String formatString(String pattern, Object... arguments) { return MessageFormat.format(pattern, arguments); } /* * @see java.lang.Object#toString() */ @Override public String toString() { return getClass().getSimpleName(); }}It can be seen that the abstract class skeleton implementation does several common things in life cycle management, checking whether the transition between states is legal (for example, it must be init before start), setting the internal state, and triggering the corresponding listener.
After the abstract class implements the method defined by ILifeCycle, it leaves corresponding abstract methods for its subclass to implement. As shown in the above code, the abstract methods left out include the following:
protected abstract void init0() throws LifecycleException; protected abstract void start0() throws LifecycleException; protected abstract void suspend0() throws LifecycleException; protected abstract void resume0() throws LifecycleException; protected abstract void destroy0() throws LifecycleException;
Elegant implementation
So far, we have defined the interface ILifeCycle, and its skeleton implements AbstractLifeCycle, and added a listener mechanism. It seems that we can start writing a class to inherit AbstractLifecycle and rewrite the abstract method it defines, so far so good.
But before we start, we need to consider several other issues.
Are our implementation classes interested in all abstract methods?
Does each implementation need to implement init0, start0, suspend0, resume0, destroy0, destroy0?
Are sometimes our living classes or modules do not support suspension or recovery?
Directly inheriting AbstractLifeCycle means that all its abstract methods must be implemented.
So we also need a default implementation, DefaultLifeCycle, let it inherit AbstractLifeCycle, and implement all abstract methods, but it does not do anything practical, do nothing. Just let us real implementation class inherit this default implementation class and override the method of interest.
So, our DefaultLifeCycle was born:
public class DefaultLifecycle extends AbstractLifecycle { /* * @see AbstractLifecycle#init0() */ @Override protected void init0() throws LifecycleException { // do nothing } /* * @see AbstractLifecycle#start0() */ @Override protected void start0() throws LifecycleException { // do nothing } /* * @see AbstractLifecycle#suspend0() */ @Override protected void suspendInternal() throws LifecycleException { // do nothing } /* * @see AbstractLifecycle#resume0() */ @Override protected void resume0() throws LifecycleException { // do nothing } /* * @see AbstractLifecycle#destroy0() */ @Override protected void destroy0() throws LifecycleException { // do nothing }} For DefaultLifeCycle, do nothing is its responsibility.
So next we can write our own implementation class, inherit DefaultLifeCycle, and rewrite those lifecycle methods of interest.
For example, I have a class that only needs to do some tasks during initialization, startup, and destruction, so I can write it like this:
import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;public class SocketServer extends DefaultLifecycle { private ServerSocket acceptor = null; private int port = 9527; /* * @see DefaultLifecycle#init0() */ @Override protected void init0() throws LifecycleException { try { acceptor = new ServerSocket(port); } catch (IOException e) { throw new LifecycleException(e); } } /* * @see DefaultLifecycle#start0() */ @Override protected void start0() throws LifecycleException { Socket socket = null; try { socket = acceptor.accept(); //do something with socket } catch (IOException e) { throw new LifecycleException(e); } finally { if (socket != null) { try { socket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } } /* * @see DefaultLifecycle#destroy0() */ @Override protected void destroy0() throws LifecycleException { if (acceptor != null) { try { acceptor.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }} In ServerSocket here, init0 initializes socket listening, start0 starts to obtain socket connection, destroy0 destroys socket listening.
Under this life cycle management mechanism, we will easily manage resources, and there will be no resource shutdown, and the architecture and modularity will be clearer.
end
So far, this article has implemented a simple life cycle management mechanism and given all implementation codes. After that, all the source code will be placed on github. Please pay attention to the update of this article.