// Copyright © 2002 // John M. Thompson // Boulder, Colorado USA // jt@iwaytechnology.com // http://www.iwaytechnology.com // // A limited right to copy this page for // individual (non-commercial) educational // use only is hereby granted. import iwaypublishing.util.IMsgPortManager; import java.io.*; import java.net.*; /********************************************************************** * * A server app that requires an orderly shutdown feature. *

* * Usage (Windows): * * Start first Server app (in cmd wdw 1): * * > java -classpath . myserver 1789 sample.txt * * Start second Server app w/stop (in cmd wdw 2): * * > java -classpath . myserver 1789 stop * */ public class myserver { /** * Port number for termination communication. * 'static' implies we'll have one instance of * this server per JVM. */ private static int msgPort_ = 0; /** Sleep times between 'units of work' (milliseconds) */ private static final int SLEEP_MILLIS_TR = 3000; private static final int SLEEP_MILLIS_WA = 6000; private static final int SLEEP_MILLIS_WB = 1500; /** * IMsgPortManager portManager_ will listen for a second * instance of myserver attempting to notify us to * shutdown in orderly fashion. This communication is * achieved with IMsgPortManager.sendTerminate(). */ private static IMsgPortManager portManager_ = null; /** * Worker threads do important work of server and must not * be interrupted during a critical work phase. */ private static WorkerThread worker1_ = null; private static WorkerThread worker2_ = null; /** Where 'important work' is 'documented' */ private static PrintWriter out_ = null; // Optional upper bound on processing iterations // (demonstration purposes only, not required). private static final int INTERNAL_MAX_COUNT = 100000; /******************************************************************* * @param args String array of command line args - first one is * port number to monitor or terminate with, and if followed by * 'stop' (no quotes), attempt to terminate running server. */ public static void main( String[] args ) throws IOException, SocketException, InterruptedException { // See processArgs() for reasons it may return false if( ! processArgs( args ) ) return; // Start up a message port manager startupPortManager(); // Start other worker threads startupWorkers(); // Wait for termination request; when returns, shutdown waitForTermination(); // Do shutdown steps shutdownWorkers(); shutdownPortManager(); shutdownPrintWriter(); // Hold up app for a response byte[] response = new byte[ 256 ]; System.out.println( "\nmyserver.main() all done. Press ." ); System.in.read( response ); } /******************************************************************* * processArgs - Handle comand line args * * @param args String array of cmd line args. * @return boolean true => continue, false => exit */ private static boolean processArgs( String[] args ) { if( args.length < 2 ) { System.out.println( "Usage: myserver ( stop | )" ); return false; } try { msgPort_ = Integer.parseInt( args[ 0 ] ); // Is second arg Stop msg or file name? if( args[ 1 ].equalsIgnoreCase( "stop" ) ) { // DEBUG System.out.println( "Attempting to stop running instance -" ); if( IMsgPortManager.sendTerminate( msgPort_ ) ) { // DEBUG System.out.println( " - Success." ); } else { // DEBUG System.out.println( " - Failure." ); } return false; } else { // Get file name arg out_ = new PrintWriter( new BufferedWriter( new FileWriter( args[ 1 ] ) ) ); } } catch( Exception e ) { System.out.println( "Usage: myserver ( stop | )" ); return false; } return true; } /* ************************************************************** */ private static void shutdownPrintWriter() throws IOException, SocketException, InterruptedException { out_.flush(); out_.close(); } /* ************************************************************** */ private static void startupWorkers() { worker1_ = new WorkerThread( "A", SLEEP_MILLIS_WA, out_ ); worker1_.start(); worker2_ = new WorkerThread( "B", SLEEP_MILLIS_WB, out_ ); worker2_.start(); } /* ************************************************************** */ private static void shutdownWorkers() throws InterruptedException { worker1_.terminateUponWakeup( true ); worker2_.terminateUponWakeup( true ); worker1_.join(); worker2_.join(); // Prove Java Thread objects are "sound" System.out.println( "\nAccessing WorkerThread instance data - " ); System.out.println( "WorkerThread " + worker1_.name() + " did " + worker1_.workUnits() + " units of work." ); System.out.println( "WorkerThread " + worker2_.name() + " did " + worker2_.workUnits() + " units of work." ); } /* ************************************************************** */ private static void startupPortManager() throws IOException, SocketException { portManager_ = new IMsgPortManager( msgPort_ ); portManager_.start(); } /* ************************************************************** */ private static void shutdownPortManager() throws IOException, SocketException, InterruptedException { portManager_.kill(); portManager_.join(); } /* ****************************************************************/ private static void waitForTermination() throws InterruptedException { int count = 0; while( count++ < INTERNAL_MAX_COUNT ) { // Check for termination request from second cmd-line // instance. This should be done AT LEAST once every ten // seconds, if possible, at a "good time" for shutdown // (ie, no possible loss of data) System.out.println( "In myserver.waitForTermination(), checking for request." ); if( portManager_.isTerminate() ) { System.out.println( "\nIn myserver.waitForTermination(), handling request.\n" ); return; } // May, or may not, do meaningful work here (aside // from what any worker threads are doing). // Canonical thread sleep construct Thread.sleep( SLEEP_MILLIS_TR ); } } }