//***************************************************************************************
//
//  Module Name: JPing.java
//
//  Copyrights:
//
//     Licensed Materials - Property of IBM
//
//     (C) Copyright IBM Corp. 1996, 1997 All Rights Reserved.
//
//     US Government Users Restricted Rights - Use, duplication or
//     disclosure restricted by GSA ADP Schedule Contract with
//     IBM Corp.
//
//
//  Author:    Charles S Head
//
//  Function:   JPing is a simplified version of APing. Like APing this program
//     provides a timed echo test. It will act as the client to either the
//     APingD or JPingD server program. This program requests only very basic exchanges
//     with the server program. APing and APingD demonstrate much more of the CPI-C
//     functionality than this program does. In this version of the program the
//     symbolic name, number of iterations, and length of data may be specified by
//     changing the appropriate static variables in the "Runtime input" section of
//     this module. This program is  intended as a sample for learning and
//     expermenting with basic Java CPI-C functions.
//
//  Version:
//
//    Multi-platform version. This version checks to see if it is running on
//    a Windows/95 or Windows NT operating system. If so, it issues WinCPI-C calls.
//    This version was made by combining two earlier versions:
//
//	   Windows/95 and NT version. Tested with Win/95, Win/NT version 4.0, and
//             - IBM Personal Communications Version 4.1 for Win/95 (full stack)
//             - Microsfot SNA Server version 2.11 (split stack).
//
//    OS/2 Version. Tested on Merlin (OS/2 4.0).
//
//  Conceptual Flows:
//
//     Windows CPI-C Startup			  (Windows CPI-C Startup)
//     Create Conversation     ----->     Accept Conversation
//     Exchange Versions                  Exchange Versions
//         Send Data           ----->       Receive Data
//         Receive Data        <-----       Send Data
//     Send Data               ----->     Receive Data
//     ...                     ----->     ...
//     Shut down Conversation  ----->     Shut down Conversation
//     Windows CPI-C Shutdown             (Windows CPI-C Shutdown)
//
//
//  Background: An excellent practical guide to CPI-C programming is provided in the
//     following book:
//
//     CPI-C Programming in C, John Q. Walker II and Peter J. Schwaller, 1995,
//        McGraw-Hill, ISBN 0-07-911733-3
//
//***************************************************************************************

// Java import libraries
import COM.ibm.eNetwork.cpic.*;
import java.io.*;

// ---- JPing Class----
public class JPing extends Object
{
  /** Copyright IBM Corporation 1977. All rights reserved.
    U.S. Government Users Restricted Rights - Use, duplication
    or disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */
  public static final String copyright =
   "Copyright IBM Corporation 1997. All rights reserved.\n"+
   "U.S. Government Users Restricted Rights - Use, duplication\n"+
   "or disclosure restricted by GSA ADP Schedule Contract with\n"+
   "IBM Corp.\n\n";

  // Runtime input defaults
  private static final int DEF_NUMBER_ITERATIONS = 20;
  private static final int DEF_LENGTH_OF_DATA    = 2000;
  private static final String DEF_SYMBOLIC_DEST  = "JPING   ";

  private static final byte ERROR_VERSION = 1;
  private static final byte MAJOR_VERSION = 1;
  private static final byte MINOR_VERSION = 0;

  // Global values
  private static String osName    = System.getProperty("os.name");
  private static int numIterations= DEF_NUMBER_ITERATIONS;
  private static int lenData      = DEF_LENGTH_OF_DATA;
  private static String sSymbolicDestination = DEF_SYMBOLIC_DEST;

  private static BufferedReader d = new BufferedReader(new InputStreamReader(System.in));

  // Create the Java CPI-C object. This class defines the method prototypes for the
  // non-native (to a C dll that will issue the CPI-C calls in C) calls as well as
  // some constants. It is made global since CPI-C calls are made from multiple methods.
  public static CPIC cpicObject = new CPIC();

  // ---- Main Program ----
  public static void main(String[] args)
                     throws CPICReturnCode  // for try-catch example below
  {
   // Main variable definitions
   long lLength        = 0;
   long timeStart      = 0;
   long timeStartFirst = 0;
   long timeStop       = 0;
   double timeElapsed  = 0;
   double timeTotal    = 0;

   // Classes (variables) required for Windows specific CPI-C calls
   WCPICVersionRequired winCPICVersion = new WCPICVersionRequired(0x0001);
   WCPICData wCPICData                 = new WCPICData();

   // Classes (variables) required for CPI-C calls
   byte[] bConversationId    = new byte[cpicObject.CM_CID_SIZE];
   CPICReturnCode cpicReturn = new CPICReturnCode(0);
   CPICLength cpicSendLengthVersion = new CPICLength(3);
   CPICDataReceivedType cpicWhatReceived = new CPICDataReceivedType(0);
   CPICLength cpicReceivedLength         = new CPICLength(0);
   CPICStatusReceived cpicStatusReceived = new CPICStatusReceived(0);
   CPICControlInformationReceived cpicReturnReceived =
                                     new CPICControlInformationReceived(0);
   CPICSendType cpicSendTypeBuffer = new CPICSendType(CPICSendType.CM_BUFFER_DATA);
   CPICSendType cpicSendTypeFlush  = new CPICSendType(CPICSendType.CM_SEND_AND_FLUSH);
   CPICDeallocateType cpicDeallocType =
   						new CPICDeallocateType(CPICDeallocateType.CM_DEALLOCATE_FLUSH);

   // Parse any commandline arguments.
   // User can change number_of_iterations, len_of_data, symbolic_destination

   parseArgs(args);

   // Initialize and Allocate Conversation
   System.out.println ("-----------------JPing Opening------------------");
   System.out.println ("          OS Name      = " + osName);
   System.out.println ("  CPI-C Side Info (-s) = " + sSymbolicDestination);
   System.out.println ("Number Iterations (-n) = " + numIterations);

   if (osName.startsWith("Windows"))
   {
      // WinCPICStartup must be called before any CPI-C calls are made. A return
      // of 0 indicates a successful registration. The version number is the lowest
      // version of CPI-C that the client can use (version 1 in this case).

      if (cpicObject.WinCPICStartup(winCPICVersion,wCPICData) != 0)
      {
          System.out.println("CPI-C Version incorrect\nPress Enter");
          try{d.readLine();} catch(IOException e){}
          System.exit (1);
      }
   }

   // Initialize the conversation. sSymbolicDestination is name of the CPI-C side
   // information. The partner LU, Partner TP, Security (None) and mode (#INTER) are (in
   // this sample) specified in this table. If successful an 8 byte (not null terminated)
   // handle is returned (cConversationId). If successful a return code of 0 (CM_OK)
   // is returned.

   cpicObject.cminit(bConversationId,sSymbolicDestination, cpicReturn);
   if (cpicReturn.intValue() != CPICReturnCode.CM_OK)
      error_processing(cpicReturn.intValue(), "Initialize Conversation");

   // Get the maximum buffer size. Keep data sends below this amount.
   // Use cmembs to allocate send/recv buffer of proper size.
   // You can use try-catch blocks to wrap your CPI-C calls. The CPICReturnCode is
   // a Throwable object, and CPICReturnCode.isOK() will throw an exception if the
   // CPICReturnCode.intValue() is not CPICReturnCode.CM_OK.

   try
   {
      CPICLength mbs = new CPICLength();
      cpicObject.cmembs(mbs, cpicReturn);
      cpicReturn.isOK();

      if (mbs.intValue() < lenData)
         lenData = mbs.intValue();
      System.out.println ("      Data Length (-d) = " + lenData);
      System.out.println ("   Max Buffer (cmembs) = " + mbs.intValue());
   }
   catch( CPICReturnCode c )
   {
      System.out.println("Extract_Maximum_Buffer_Size error.");
      throw c;
   }

   byte[]     bBuffer               = new byte[lenData];
   CPICLength cpicMaxReceiveLength  = new CPICLength(lenData);
   CPICLength cpicSendLengthBuffer  = new CPICLength(lenData);

   // Allocate a session with the server. If successful an active session with the
   // server(partner) has been established and the client is in the send state.

   timeStart = System.currentTimeMillis();
   cpicObject.cmallc(bConversationId,cpicReturn);
   if (cpicReturn.intValue() != CPICReturnCode.CM_OK)
      error_processing(cpicReturn.intValue(), "Allocate");
   timeStop    = System.currentTimeMillis();
   timeElapsed = (timeStop - timeStart)/1000.0;
   System.out.println("\nAllocate Time = " + timeElapsed + " seconds");

   // Exchange Major and Minor version numbers. The server side of ping expects to
   // receive 3 characters of numeric data that indicate the error version, major
   // program version and minor version.

   bBuffer[0] = ERROR_VERSION;
   bBuffer[1] = MAJOR_VERSION;
   bBuffer[2] = MINOR_VERSION;
   timeStart  = System.currentTimeMillis();

   // Send the version numbers to the server. The address of the buffer (bBuffer)
   // and the length (cpicSendLengthVersion) of the data (actually pointer to length)
   // are the new parameters of the send. Notice the the length does not
   // include the null termination character. This sample does not use the
   // cpicReturnReceived parameter. The receive call below
   // insures the data is sent and the conversation state is changed.

   cpicObject.cmsend(bConversationId,bBuffer,cpicSendLengthVersion,
                     cpicReturnReceived,cpicReturn);
   if (cpicReturn.intValue() != CPICReturnCode.CM_OK)
   		error_processing(cpicReturn.intValue(), "Send Version");

   // Receive the version numbers from the server. The new parmeters that are used in the
   // sample are the maximum length to be received (cpicMaxReceiveLength) which is set to
   // to the buffer length, whether a complete record was received (cpicWhatReceived),
   // the size of the record received (cpicReceivedLength), and whether a change in the
   // conversation state has been received (cpicStatusReceived).

   cpicObject.cmrcv(bConversationId,bBuffer,cpicMaxReceiveLength,
                    cpicWhatReceived,cpicReceivedLength,cpicStatusReceived,
                    cpicReturnReceived,cpicReturn);
   if (cpicReturn.intValue() != CPICReturnCode.CM_OK)
   		error_processing(cpicReturn.intValue(),"Receive Version");

   // The server is expected to enter the receive state after sending client
   // its version numbers.

   if (cpicStatusReceived.intValue() != CPICStatusReceived.CM_SEND_RECEIVED)
   	error_processing(cpicStatusReceived.intValue(),"CM_SEND_RECEIVED not received");
   timeStop = System.currentTimeMillis();
   timeElapsed = (timeStop - timeStart)/1000.0;
   System.out.println("\nVersion Exchange Time = " + timeElapsed + " seconds");

   // Check to see if any data was received and that the data received is at least equal
   // to the expected 3 bytes. CPI-C programs will normally be checking for incomplete
   // data i.e. data record physically sent in multiple sends -- since the client knows
   // the expected data length and can check for it, it is unnecessary in this program.

   if (cpicWhatReceived.intValue() != CPICDataReceivedType.CM_NO_DATA_RECEIVED)
   {
      if (cpicReceivedLength.intValue() > 2)
      {
         System.out.println("\nServer Error Version: " + bBuffer[0]);
         System.out.println("ServerVersion: " + bBuffer[1] + "." + bBuffer[2] + "\n");
      }
      else error_processing(0,"Unexpected data length received");
   }
   else error_processing(cpicWhatReceived.intValue(),"CM_NO_DATA_RECEIVED received");

   // Start Pinging.

   // Use the send type that will flush a call automatically after the
   // send to force an immediate sending of the data to the partner.

   cpicObject.cmsst(bConversationId,cpicSendTypeFlush,cpicReturn);
   if (cpicReturn.intValue() != CPICReturnCode.CM_OK)
      error_processing(cpicReturn.intValue(), "Send Type");

   for(int i=0;i< numIterations ;i++)
   {
      timeStart = System.currentTimeMillis();
      if (i == 0) timeStartFirst = timeStart;
      // Send record to server. No new parameters.
      cpicObject.cmsend(bConversationId,
                        bBuffer,cpicSendLengthBuffer,
                        cpicReturnReceived,
                        cpicReturn);
      if (cpicReturn.intValue() != CPICReturnCode.CM_OK)
         error_processing(cpicReturn.intValue(), "Send Ping");
      timeStop    = System.currentTimeMillis();
      timeElapsed = (timeStop - timeStart)/1000.0;
      System.out.println("Ping Time = " + timeElapsed + " seconds");
   }

   // Print totals and deallocate conversation

   timeStop  = System.currentTimeMillis();
   timeTotal = (timeStop - timeStartFirst)/1000.0;
   System.out.println ("\nTotal Ping Time = " + timeTotal);
   System.out.println ("Average Ping Time = " + (timeTotal/numIterations));

   // Set the deallocate type to deallocate and flush the buffer.

   cpicObject.cmsdt(bConversationId,cpicDeallocType,cpicReturn);
   if (cpicReturn.intValue() != CPICReturnCode.CM_OK)
      error_processing(cpicReturn.intValue(), "Deallocate Type");

   // Issue the deallocate and by implication because of the deallocate issue
   // a flush. CM_OK indicates a successful deallocation.

   cpicObject.cmdeal(bConversationId,cpicReturn);
   if (cpicReturn.intValue() != CPICReturnCode.CM_OK)
      error_processing(cpicReturn.intValue(), "Deallocate");

   System.out.println ("-----------------JPing Closing------------------\n");
   if (osName.startsWith("Windows"))
   {
      // WinCPICCleanup should be called to terminate and deregister the program
      // from the Windows CPI-C implementation.
      if (cpicObject.WinCPICCleanup()== 0)
      {
          System.out.println ("CPIC Cleanup unsuccessful\nPress Enter");
          try{d.readLine();} catch(IOException e){}
          System.exit (1);
      }
   }
   return;
  }


//  Print out error. Appendix A of Walker and Schwaller's book is an excellent reference
//  for understanding the meaning of the return codes and the probable cause.

public static void error_processing(int intReturn, String sErrorString)
  {
    System.out.println (sErrorString + " error = " + intReturn);
    if (osName.startsWith("Windows"))
    {
       if (cpicObject.WinCPICCleanup()== 0)
        {
           System.out.println ("CPI-C Cleanup unsuccessful\nPress Enter");
           try{d.readLine();} catch(IOException e){}
           System.exit (1);
        }
     }
     System.out.println ("Press Enter");
     try{d.readLine();} catch(IOException e){}
     System.exit (1);
  }

//  Parse any arguments given to the program.
//  Use reasonable defaults for any parms not set.
//  Use defaults if user tries to set unreasonable values

public static void parseArgs(String[] args)
{
   if (args.length == 0) return;
   try
   {
      for (int i = 0; i < args.length; i++ )
      {
         if (args[i].equalsIgnoreCase("-s"))
            sSymbolicDestination = args[++i];
         else if (args[i].equalsIgnoreCase("-n"))
            numIterations = Integer.parseInt(args[++i]);
         else if (args[i].equalsIgnoreCase("-d"))
            lenData = Integer.parseInt(args[++i]);
         else
         {
            System.out.print ( copyright );
            System.out.println ("JPing [-s symb_dest] [-n num_iter] [-d data_len]\nPress Enter");
            try{d.readLine();} catch(IOException e){}
            System.exit (1);
         }
      }
      if (numIterations < 1) numIterations = DEF_NUMBER_ITERATIONS;
   }
   catch(NumberFormatException n)
   {
      System.out.println ("JPing [-s symb_dest] [-n num_iter] [-d data_len]");
      System.out.println ("eg. JPing -s PINGSAA -n 20 -d 2000\nPress Enter");
      try{d.readLine();} catch(IOException e){}
      System.exit (1);
   }
}


}
