// Copyright © 2001-2002 // iWay Technology Company // Boulder, Colorado USA // http://www.iwaytechnology.com // // A limited right to copy this page for individual // (non-commercial) educational use only is hereby // granted. // // IWAY PUBLISHING COMPANY MAKES NO REPRESENTATIONS OR // WARRANTIES ABOUT THE SUITABILITY OF THIS SOFTWARE, // EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED // TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS // FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. IWAY // PUBLISHING COMPANY SHALL NOT BE LIABLE FOR ANY // DAMAGES SUFFERED AS A RESULT OF USING, MODIFYING OR // OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. // package iwaypublishing.util; import java.net.*; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /********************************************************************** * IMsgPortManager extends Thread and, thus, is not created * on a Thread object externally instantiated by its creator. * This simplifies usage since a separate Thread instantiation * is not required to use the object. *

* IMsgPortManager uses the following 'protocol' convention: * Byte 0 sent to its port MUST be the 7-bit ascii code for * ':' (aka colon) and byte 1 MUST be the 7-bit ascii code for * 'X' - set terminate flag. Other combinations may be added. *

 * Usage 
 * Listening Application:
 * Initialization:
 *    IMsgPortManager man_ = 
 *       new iwaypublishing.util.IMsgPortManager( portNum );
 *    man_.start();
 *
 * Within some loop (every 1-10 seconds for responsiveness):
 *    if( man_.isTerminate() ) { 
 *       // Do Cleanup
 *       ...
 *       // Shutdown server thread
 *       man_.kill();
 *       man_.join();
 *       // exit or return (exiting)
 *    } else {
 *      // Continue working
 *    }
 *
 * Terminating App (separate JVM):
 *    if( IMsgPortManager.sendTerminate( portNum ) ) 
 *    {
 *       // Success
 *    } else {
 *       // Failure
 *    } 
 */
public class IMsgPortManager extends Thread
{
   private java.net.ServerSocket server_ = null;

   private boolean terminate_ = false; 
   
   public static final byte[] terminateSequence = { (byte)':', (byte)'X' };
   
   /*******************************************************************
    * @param port int the port on which to listen.
    */
   public IMsgPortManager( int port )
      throws IOException,
             SocketException
   {
      // Set up server on port defined by ctor call
      server_ = new java.net.ServerSocket( port );
      server_.setSoTimeout( 0 );
   }
   
   /******************************************************************/
   public void kill() 
      throws IOException
   {
      // A ServerSocket.accept() call will cause this thread
      // to be impervious to thread interrupts.  So, as per
      // Doug Lea "Concurrent Programming in Java", p172,
      // close the socket to force termination - see run() -
      // by causing an IOException to be thrown.
      server_.close();
   }
   
   /******************************************************************/
   public boolean isTerminate() 
   {
      return terminate_;   
   }
   
   /******************************************************************/
   public void run() 
   {
      boolean carryOn = true;
      while( carryOn ) 
      {
         try{
            // Blocks until client request, then creates new
            // socket on which to conduct client conversation.
            java.net.Socket client = server_.accept();
      
            client.setSoTimeout( 0 );
            java.io.InputStream in = client.getInputStream();
      
            byte[] inbuf = new byte[32];
      
            // Read designated port.  In the case of
            // Terminate, return.
            in.read( inbuf, 0, 2 );
            
            if( inbuf[0] == terminateSequence[0] && 
                inbuf[1] == terminateSequence[1]    )
            {
               terminate_ = true;
                     
               // Clean up client socket
               in.close();
               client.close();
                     
               return;
            }
         }
         catch( SocketException e1 ) {
            System.out.println( "SocketException in IMsgPortManager.run() - <" + e1.toString() + ">" );
            carryOn = false;
         }
         catch( IOException e2 ) {
            System.out.println( "IOException in IMsgPortManager.run() - " + e2.toString() );
            carryOn = false;
         }
      }
   }

   /*******************************************************************
    * Attempt to send a stop message to an instance of IMsgPortManager
    * over the provided port number.
    * 
    * @param int port number upon which to attempt communication.
    * @return boolean true if connection to running app succeeded
    * and terminate message was sent.
    */
   public static boolean sendTerminate( int port )
   {
      boolean result = true;
      try {
         
         // Connect to running server through designated port
         // (both this and first instance using same port)
         Socket socket = new Socket( "localhost", port );
         OutputStream out = socket.getOutputStream();
         
         // Send predetermined "Stop" message
         
         out.write( IMsgPortManager.terminateSequence );
         out.flush();
         out.close(); 
         socket.close();
      }
      catch( Exception e ){ result = false; }
      
      return result;
   }


}