//#
//# Corba Time Server
//# $Revision: 1.1 $
//# Copyright 2004 by Eric Y. Theriault
//# All Rights Reserved.
//# http://www.eyt.ca/CORBA
//#
package ca.eyt.corba;

/**
 * Implements a CORBA server to provide the local time.
 *
 * @author Eric Y. Theriault <eric@eyt.ca>
 */
public class TimeServer3 {
   /**
    * Port to multicast on.  This number is random.
    */
   public static final int MulticastPort = 7007;

   /**
    * Address to multicast to.  The default is link-local, which is
    * generally OK, but in the presence of a segmented network with
    * routers, this address and the TTL will have to be changed.
    */
   public static final String MulticastAddress = "224.0.0.1";

   /**
    * This is the message that is multicasted to discover the new 
    * server. 
    */
   public static final String MulticastDiscoverServer = "DiscoverIOR";

   /**
    * Multicast Service.
    *
    * For more information on Multicasting, please see Chapter 21 [549]
    * of W. Richard Stevens, Bill Fenner, and Andrew M. Rudoff's UNIX
    * Network Programming: Volume 1: The Sockets Networking API--Third
    * Edition, Addison-Wesley, 2004.
    */
   public static class MulticastIORServer extends java.lang.Thread {
      /**
       * IOR to multicast.
       */
      private String ior;

      /**
       * Multicast Socket reference.
       */
      private java.net.MulticastSocket mcast;

      /**
       * Creates a service to answer to inbound Multicast 
       */
      public MulticastIORServer( String ior ) throws java.io.IOException {
         this.ior = ior;

         // Please note that a real implementation would provide options
         // to set the bind address, Time To Live, etc., but this is
         // simply to demonstrate the functionality.
         mcast = new java.net.MulticastSocket( MulticastPort );
         mcast.joinGroup( java.net.InetAddress.getByName( MulticastAddress ) );
      }

      /**
       * This awaits for a multicast packet, and if anything is received,
       * it will validate the message and respond with an IOR.
       */
      public void run() {
         while ( true ) {
            try {
               // Acquire a packet.
               byte[] buffer = new byte[1000];
               java.net.DatagramPacket receive =
                              new java.net.DatagramPacket(
                                                          buffer,
                                                          buffer.length
                                                         );
               mcast.receive( receive );

               // Create a received String
               String receivedString = new String( buffer );

               // If the packet is good, reply
               //
               // In a real application, a checksum or something could
               // be used to validate the message, and the reply should
               // contain a more verbose message.
               if ( receivedString.startsWith( MulticastDiscoverServer ) ) {
                  // Unicast back to the sender the ior string.
                  java.net.DatagramPacket pkt =
                        new java.net.DatagramPacket(
                                                    ior.getBytes(),
                                                    ior.length(),
                                                    receive.getSocketAddress()
                                                   );
                  mcast.send( pkt );
               }
            } catch ( java.lang.Throwable t ) {
               // Log the message.
               System.err.println( "Exception raised: " );
               t.printStackTrace();
            }
         }
      }
   };

   /**
    * main
    *
    * @param args Currently forwarded directly to ORB.init.
    */
   public static void main( String args[] ) {
      try {
         String iorFile = "time.ior";

         // Create and Initialize the ORB
         org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init( args, null );

         // Get a reference to the root POA and activate the POAManager
         org.omg.PortableServer.POA rootPOA =
                         org.omg.PortableServer.POAHelper.narrow(
                              orb.resolve_initial_references( "RootPOA" )
                                                                );
         rootPOA.the_POAManager().activate();

         // Create the time Servant and register it.
         ca.eyt.corba.TimeImpl timeImpl = new ca.eyt.corba.TimeImpl();
         timeImpl.setORB( orb );

         // Create the object reference.
         org.omg.CORBA.Object ref = rootPOA.servant_to_reference( timeImpl );
         String ior = orb.object_to_string( ref );

         // Create the file.
         java.io.FileWriter fileWriter = null;
         java.io.BufferedWriter writer = null;
         try {
            fileWriter = new java.io.FileWriter( iorFile );
            writer = new java.io.BufferedWriter( fileWriter );
         } catch ( java.io.IOException ioe ) {
            System.err.println( iorFile + ": Failure to create:" );
            ioe.printStackTrace();
            System.exit( 1 );
         }

         // Write in the IOR from the file...
         try {
            writer.write( ior );
            writer.newLine();
         } catch ( java.io.IOException ioe ) {
            System.err.println( iorFile + ": exception raised: " );
            ioe.printStackTrace();
            System.exit( 1 );
         } finally {
            writer.flush();
            try {
               fileWriter.close();
            } catch ( java.io.IOException ioe ) {
               System.err.println( iorFile + ": exception raised " +
                                   "closing the file: " );
               ioe.printStackTrace();
            }
            writer = null;
            fileWriter = null;
         }

         // Let's get the root naming context.
         org.omg.CORBA.Object objectReference =
                         orb.resolve_initial_references( "NameService" );
         org.omg.CosNaming.NamingContextExt ncRef =
                       org.omg.CosNaming.NamingContextExtHelper.narrow(
                                                         objectReference
                                                                      );

         // Start the Multicast Server
         MulticastIORServer mcast = new MulticastIORServer(
                                 orb.object_to_string( objectReference )
                                                          );
         mcast.start();

         // Bind the Time object to the reference.
         String name = "Time";
         org.omg.CosNaming.NameComponent path[] = ncRef.to_name( name );
         ncRef.rebind( path, ref );

         // Server is now up and running
         System.out.println( "TimeServer is ready and waiting..." );
         orb.run();
      } catch ( Throwable t ) {
         System.err.println( "Exception raised: " );
         t.printStackTrace();
      }
   }
};

