/////////////////////////////////////////////////////////////////////////
//
// © University of Southampton IT Innovation Centre, 2004
//
// Copyright in this library belongs to the IT Innovation Centre of
// 2 Venture Road, Chilworth Science Park, Southampton, SO16 7NP, UK.
//
// This software may not be used, sold, licensed, transferred, copied
// or reproduced in whole or in part in any manner or form or in or
// on any media by any person other than in accordance with the terms
// of the Licence Agreement supplied with the software, or otherwise
// without the prior written consent of the copyright owners.
//
// This software is distributed WITHOUT ANY WARRANTY, without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE, except where stated in the Licence Agreement supplied with
// the software.
//
//      Created by:             Stuart E. Middleton
//      Created date:           2004/04/30
//      Created for project:    GEMSS
//
/////////////////////////////////////////////////////////////////////////
//
//      Dependencies: None
//
/////////////////////////////////////////////////////////////////////////
//
//      Last commit info:       $Author: $
//                              $Date: $
//                              $Revision: $
//
/////////////////////////////////////////////////////////////////////////

package uk.ac.soton.itinnovation.gemss.infrastructure;

import java.util.*;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import java.io.*;
import java.awt.*;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JFileChooser;
import javax.swing.UIManager;
import javax.swing.filechooser.FileFilter;

import eu.gemss.GEMSS;
import eu.gemss.components.Configuration;
import eu.gemss.GridException;
import eu.gemss.components.ComponentManager;
import eu.gemss.components.transport.NetworkException;
import eu.gemss.signals.*;
import eu.gemss.signals.negotiation.*;
import eu.gemss.signals.security.*;
import eu.gemss.components.proxies.*;

import uk.ac.soton.itinnovation.gemss.*;
import uk.ac.soton.itinnovation.gemss.utils.signalhandlers.*;
import uk.ac.soton.itinnovation.gemss.utils.configuration.ConfigurationException;
import uk.ac.soton.itinnovation.gemss.infrastructure.SampleGUIPanel;

// adv security signal and handler
import de.nece.ccrl.advsec.signals.*;
import eu.gemss.signals.advsecurity.*;

/*
* Sample client to demonstrate the phase two infrastructure capabilities. A small GUI will pop up
* to allow users to start a job and save the client's state. The users can then re-start the job
* at a later date and the system will resume where it left off, picking up the results if they are
* ready.
*/
public class SampleGUI {

	// constants
      private static final String mstaticServiceName = "serviceName";
	private static final String mstaticRequestDescFilename = "requestDescFilename";
	private static final String mstaticInputFilename = "inputFilename";
	private static final String mstaticOutputFilename = "outputFilename";
	private static final String mstaticProxyConfigFile = "ProxyConfigFile";
	private static final String mstaticQoSConfigFile = "QoSConfigFile";
	private static final String mstaticFastRetry = "FastRetry";
	private static final String mstaticSlowRetry = "SlowRetry";
	private static final String mstaticOperationRetryDelay = "OperationRetryDelay";
	private static final String mstaticOperationTimeout = "OperationTimeout";
	private static final String mstaticLogo = "logoFilename";
	private static final String mstaticLoadSession = "loadSession";
	private static final String mstaticCommandLineOnly = "commandLineOnly";

	private static final String mstaticNegotiatorIDTag = "SampleClientNegotiatorID";
	private static final String mstaticTitle = "Sample client phase three";
	private static final String mstaticSessionSuffix = ".session";

	private static final String mstaticNegotiatorHeader = "Negotiator status:\n\n";
	private static final String mstaticJobStatusHeader = "Job status:\n\n";

	// member variables
      private String mstrServiceName;
	private String mstrConfigFilename;
	private GEMSSNegotiator mnegotiator;
	private String mstrInputFilename;
	private String mstrRequestDescFilename;
	private String mstrOutputFilename;
	private String mstrProxyConfigFile;
	private String mstrQoSConfigFile;
	private int mnSlowRetry;
	private int mnFastRetry;
	private int mnOperationRetryDelay;
	private int mnOperationTimeout;
	private String mstrLogoFilename;
	private String mstrLoadSessionFilename;
	private String mstrCommandLineOnly;

	// interface
	private SampleGUIPanel minterfaceObject;
	private JFrame mframe;

	/*
	* default constructor
	*/
	public SampleGUI() {
		mstrConfigFilename = null;
		mnegotiator = null;
            mstrServiceName = null;
		mstrInputFilename = null;
		mstrOutputFilename = null;
		mstrProxyConfigFile = null;
		mstrQoSConfigFile = null;
		mnFastRetry = 0;
		mnSlowRetry = 0;
		mnOperationRetryDelay = 0;
		mnOperationTimeout = -1;
		mstrLogoFilename = null;
		mstrLoadSessionFilename = null;
		mstrCommandLineOnly = null;
	}

	/*
	* Check if a flag has been set in the proxy flag set
	* @param strFlag The flag to check
	* @return true if the flag is now set
	*/
	public boolean checkFlag( String strFlag )
	{
		boolean bOk;
		Vector vectorFlags;
		long ntimeStart, ntimeNow;
		int i;
		String strText;

		try {
			// check proxy is ok, if not flag cannot be set
			if( mnegotiator == null ) {
				if( minterfaceObject != null ) minterfaceObject.setNegotiatorStatusText( mstaticNegotiatorHeader + "unavailable at present" );
				return false;
			}

			// get the negotiator flags
			vectorFlags = mnegotiator.getNegotiatorFlags();

			// update the interface flag display with new copy
			if( minterfaceObject != null ) {
				strText = mstaticNegotiatorHeader;
				if( vectorFlags.size() == 0 ) {
					strText += "No state flags yet set";
				}
				else {
					for( i=0;i<vectorFlags.size();i++ ) {
						strText += "Flag ";
						strText += (String) vectorFlags.elementAt(i);
						strText += " set\n";
					}
				}
				minterfaceObject.setNegotiatorStatusText( strText );
			}

			// check for the flag
			if( vectorFlags.contains( strFlag ) ) return true;

			// nope
			return false;
		}
		catch( Exception ex ) {
			// report failure details
			System.out.println("Exception checking for flag "+strFlag+" = "+ex.getMessage());
			ex.printStackTrace( System.out );
			return false;
		}
	}

	/*
	* Create the interface frame and other bits
	*/
	public void PopupInterface()
	{
		try {
			// make the interface object
			minterfaceObject = new SampleGUIPanel( mstrLogoFilename,this );
			minterfaceObject.setOpaque(true); //content panes must be opaque

			// Make sure we have nice window decorations
			UIManager.setLookAndFeel( UIManager.getCrossPlatformLookAndFeelClassName() );
			JFrame.setDefaultLookAndFeelDecorated( true );

			// Create and set up the frame
			mframe = new JFrame( mstaticTitle + " : <new job>" );
			mframe.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );

			// Create and set up the content pane
			mframe.getContentPane().add( minterfaceObject );

			// Display the window
			mframe.pack();
			mframe.setLocationRelativeTo( null ); // centre it
			mframe.setVisible( true );
		}
		catch( Exception ex ) {
			System.out.println("Exception creating interface "+ex.getMessage());
			ex.printStackTrace( System.out );
		}
	}

      /*
      * Wait until the go button has been hit
      */
      public void waitForGo()
      {
            try {
                  if( minterfaceObject == null ) return;

                  while( minterfaceObject.isPaused() ) {
                        try{ Thread.sleep( mnFastRetry ); } catch( Exception ex ) { ; }
                  } // keep looping until go is clicked
            }
            catch( Exception ex ) {
                  // dump error and continue anyway
                  ex.printStackTrace( System.out );
                  return;
            }
      }

	/*
	* Save the proxy state callback function then exit (if save went ok)
	*/
	public void SaveCallbackHandler()
	{
		ComponentManager compMan;
		String strSessionFile = null;
            String [] arraySessions;
            Vector vectorOptions;
		int i;
            SampleGUIChooser chooser;

		try {
			// get the comp man
			compMan = GEMSS.getInstance();
			if( compMan == null ) throw new Exception("null component manager");

                  // get list of existing sessions
                  arraySessions = compMan.sessions();
                  vectorOptions = new Vector();
                  if( arraySessions != null ) {
                        for( i=0;i<arraySessions.length;i++ ) vectorOptions.addElement( arraySessions[i] );
                  }

                  // choose one
                  chooser = new SampleGUIChooser( mframe,"Enter session name for save",vectorOptions,"Save" );
                  chooser.pack();
                  chooser.setLocationRelativeTo( mframe ); // centre it
                  chooser.setVisible( true ); // blocking model dialogue
                  if( !chooser.queryAborted() ) strSessionFile = chooser.querySelection();

			// check filename
			if( strSessionFile == null || strSessionFile.equals("") ) {
				System.out.println("Session save aborted by user");
				return;
			}

			// ask the component manager to save the session
			if( !compMan.manageInstance( mstaticNegotiatorIDTag,mnegotiator ) ) {
				System.out.println("Session ID already managed, ignoring");
			}

			try {
				System.out.println("Saving session to " +strSessionFile);
				compMan.saveSession( strSessionFile );
			}
			catch( IOException ie ) {
				System.out.println("Session failed to save to "+strSessionFile+" : "+ie.getMessage());
				ie.printStackTrace( System.out );
				return;
			}

			// update the frame title
			if( mframe != null ) mframe.setTitle( mstaticTitle+" : "+strSessionFile );

			// session saved ok, report to user via command line then exit before too many more steps are taken (!)
                  // else saved state != actual state
			System.out.println("Session saved ok to "+strSessionFile);
                  System.exit(0);
		}
		catch( Exception ex ) {
			System.out.println("Exception saving "+ex.getMessage());
			ex.printStackTrace( System.out );
		}
	}

	/*
	* Load a session from the session file
	* @param strFilename filename of the session file to be loaded. If null a popup dialogue will ask
	* for the filename from the user interface
	* @return false if returned on error
	*/
	public boolean LoadSession( String strFilenameArg )
	{
		String strFilename = strFilenameArg;
            ComponentManager compMan;
            String [] arraySessions;
            Vector vectorOptions;
            int i;
            SampleGUIChooser chooser;

		try {
                  // get the comp man
                  compMan = GEMSS.getInstance();
                  if( compMan == null ) throw new Exception("null component manager");

                  // get list of existing sessions
                  if( strFilename == null ) {
                        arraySessions = compMan.sessions();
                        vectorOptions = new Vector();
                        if( arraySessions != null ) {
                              for( i=0;i<arraySessions.length;i++ ) vectorOptions.addElement( arraySessions[i] );
                        }

                        // choose one (allow no options to show user no sessions available)
                        chooser = new SampleGUIChooser( mframe,"Enter session name to load",vectorOptions,"Load" );
                        chooser.pack();
                        chooser.setLocationRelativeTo( mframe ); // centre it
                        chooser.setVisible( true ); // blocking model dialogue
                        if( !chooser.queryAborted() ) strFilename = chooser.querySelection();
                  }

			// check filename is valid
			if( strFilename == null || strFilename.equals("") ) {
                        System.out.println( "No valid session name - load aborted" );
                        return false;
			}

			// load the session
			compMan.loadSession( strFilename );

			// get the previosuly saved instance of the negotiator (if any - it could be null)
			mnegotiator = (GEMSSNegotiator) compMan.getManagedInstance( mstaticNegotiatorIDTag );

			// update the frame title
			if( mframe != null ) mframe.setTitle( mstaticTitle + " : "+strFilename );

			// disable load button
			if( minterfaceObject != null ) minterfaceObject.setEnableLoad( false );

			// session loaded ok, report to user
			System.out.println("Session loaded ok from "+strFilename);

			// set flags in interface
			checkFlag(""); // this call will set the interface flags, we ignore the results here

			// all ok
			return true;
		}
		catch( Exception ex ) {
			System.out.println("Exception loading session "+ex.getMessage());
			ex.printStackTrace( System.out );
			return false;
		}
	}

	/*
	* Run method to start the sample client. The config file holds most of the
	* parameters such as the service endpoint information and input / output
	* filenames.
	* @param strConfigFilename configuration filename
	*/
	public void run( String strConfigFilename )
	{
		uk.ac.soton.itinnovation.gemss.utils.configuration.Configuration config;
		eu.gemss.components.ComponentManager componentManager;
		SignalHandler handler;
        String requestDescFilename;
		GEMSSNegotiatorRequest negotiatorRequest;
		GridException gridException;
		String strStatus;
		Vector vectorFlags;
		boolean bOk;
		int i, nParties, nRounds;

		try{
                  // get the framework's instance of the GEMSS component manager.
                  System.out.println("Starting the component manager");
                  componentManager = GEMSS.getInstance();
                  if( componentManager == null ) {
                        System.out.println("Component manager object null");
                        if( minterfaceObject != null ) {
                              minterfaceObject.setNegotiatorStatusText( mstaticNegotiatorHeader+"Negotiator stopped due to an infrastructure error [component manager].\nCheck logs for more information." );
                              minterfaceObject.haltInterface( true );
                        }
                        return; // end
                  }

                  // At this point it is a good idea to register any signal handlers you have implemented for dealing with
                  // signals.
                  // 'eu.gemss.signals.security.CredentialRequest'
                  // This signal is used to obtain credential information. A sample signal handler will be registered here.
                  // 'eu.gemss.signals.negotiation.SelectSingleServiceRequest'
                  // This signal is used to obtain a selection from a list of services. A sample signal handler will be registered here.
                  // 'eu.gemss.signals.negotiation.GetQoSParametersRequest'
                  // This signal is used to obtain min/max and priority values for each QoS property. A sample signal handler will be registered here.
                  // 'eu.gemss.signals.negotiation.AcceptContractRequest'
                  // This signal is used to prompt user to accept the WSLA contract negotiation has ended up with. A sample signal handler will be registered here (no signal handler will auto accept contract)
                  System.out.println("Registering signal handlers (5)");
                  handler = new uk.ac.soton.itinnovation.gemss.utils.signalhandlers.CredentialRequestGUIHandler();
                  componentManager.registerSignalHandler( handler,eu.gemss.signals.security.CredentialRequest.SIGNAL_TYPE );
                  if( mframe != null ) {
                        handler = new uk.ac.soton.itinnovation.gemss.utils.signalhandlers.SelectSingleServiceRequestHandler( mframe );
                        componentManager.registerSignalHandler( handler,SelectSingleServiceRequest.SIGNAL_TYPE );
                        handler = new uk.ac.soton.itinnovation.gemss.utils.signalhandlers.GetQoSParametersRequestHandler( mframe );
                        componentManager.registerSignalHandler( handler,GetQoSParametersRequest.SIGNAL_TYPE );
						handler = new uk.ac.soton.itinnovation.gemss.utils.signalhandlers.AcceptContractRequestHandler( mframe );
						componentManager.registerSignalHandler( handler,AcceptContractRequest.SIGNAL_TYPE );
                  }
                  else {
                        handler = new uk.ac.soton.itinnovation.gemss.utils.signalhandlers.SelectSingleServiceRequestHandler();
                        componentManager.registerSignalHandler( handler,SelectSingleServiceRequest.SIGNAL_TYPE );
                        handler = new uk.ac.soton.itinnovation.gemss.utils.signalhandlers.GetQoSParametersRequestHandler();
                        componentManager.registerSignalHandler( handler,GetQoSParametersRequest.SIGNAL_TYPE );
						handler = new uk.ac.soton.itinnovation.gemss.utils.signalhandlers.AcceptContractRequestHandler();
						componentManager.registerSignalHandler( handler,AcceptContractRequest.SIGNAL_TYPE );
                  }

	  			  // Adv security handler
				  handler = new de.nece.ccrl.advsec.signals.AdvSecSignonSigHandler();
				  componentManager.registerSignalHandler( handler,AdvSecSignonSig.SIGNAL_TYPE );


			// This sample client uses a configuration file for obtaining certain values needed to run
			mstrConfigFilename = strConfigFilename;
			config = new uk.ac.soton.itinnovation.gemss.utils.configuration.Configuration( strConfigFilename );

			// get the config parameters
                  mstrServiceName = config.getConfigurationValue( mstaticServiceName );
			mstrInputFilename = config.getConfigurationValue( mstaticInputFilename );
			mstrRequestDescFilename = config.getConfigurationValue( mstaticRequestDescFilename );
			mstrOutputFilename = config.getConfigurationValue( mstaticOutputFilename );
			mstrLogoFilename = config.getConfigurationValue( mstaticLogo );
			mnFastRetry = Integer.valueOf( config.getConfigurationValue( mstaticFastRetry ) ).intValue();
			mnSlowRetry = Integer.valueOf( config.getConfigurationValue( mstaticSlowRetry ) ).intValue();
			mnOperationRetryDelay = Integer.valueOf( config.getConfigurationValue( mstaticOperationRetryDelay ) ).intValue();
			mnOperationTimeout = Integer.valueOf( config.getConfigurationValue( mstaticOperationTimeout ) ).intValue();

			// optional QoS negoitation config file
			try { mstrQoSConfigFile = config.getConfigurationValue( mstaticQoSConfigFile ); }
                  catch( ConfigurationException ce ) { mstrQoSConfigFile = null; } // use discovery if missing
                  // optional proxy binding config file
                  try { mstrProxyConfigFile = config.getConfigurationValue( mstaticProxyConfigFile ); }
                  catch( ConfigurationException ce ) { mstrProxyConfigFile = null; } // use discovery if missing
			// optional session filename to be loaded
			try { mstrLoadSessionFilename = config.getConfigurationValue( mstaticLoadSession ); }
			catch( ConfigurationException ce ) { mstrLoadSessionFilename = null; } // ignore if missing
			// optional command line only clag
			try { mstrCommandLineOnly = config.getConfigurationValue( mstaticCommandLineOnly ); }
			catch( ConfigurationException ce ) { mstrCommandLineOnly = null; } // ignore if missing

			// print parameters so user knows what is going on
                  System.out.println("Service name = "+mstrServiceName);
			System.out.println("Input archive file = "+mstrInputFilename);
			System.out.println("Request desc file = "+mstrRequestDescFilename);
			System.out.println("Output archive file  = "+mstrOutputFilename);
			System.out.println("Fast retry delay = "+mnFastRetry+" ms");
			System.out.println("Slow retry delay = "+mnSlowRetry+" ms");
			System.out.println("Operation retry delay = "+mnOperationRetryDelay+" ms");
			System.out.println("Operation timeout delay = "+mnOperationTimeout+" ms");
			System.out.println("Logo filename = "+mstrLogoFilename);
                  if( mstrProxyConfigFile != null ) System.out.println("GEMSSProxy config filename = "+mstrProxyConfigFile);
			System.out.println("QoS config filename = "+mstrQoSConfigFile);
			if( mstrLoadSessionFilename != null ) System.out.println("Load session = "+mstrLoadSessionFilename);
			if( mstrCommandLineOnly != null ) System.out.println("Command line only mode set");

			// popup the interface (if needed)
			if( mstrCommandLineOnly == null ) PopupInterface();

			// set the config text and default text box messages (ignore logo name)
			if( minterfaceObject != null ) {
				minterfaceObject.setConfigurationText( "Configuration parameters:\n\n"+
					"Input archive file = "+mstrInputFilename+"\n"+
		                  "Output archive file  = "+mstrOutputFilename+ "\n" );
				minterfaceObject.setJobStatusText( mstaticJobStatusHeader+"Unavailable at present" );
				minterfaceObject.setNegotiatorStatusText( mstaticNegotiatorHeader+"Unavailable at present" );
			}

			// load session if needed
			// after ths call the negotiator may or may not be set, and could have a number
			// of flags set already. As such before each workflow action the flags will
			// be checked to see if this action has been performed already.
			if( mstrLoadSessionFilename != null ) {
				if( !LoadSession( mstrLoadSessionFilename ) ) throw new Exception("Failed to load session "+mstrLoadSessionFilename);
			}

			// action is initially paused, so wait until ser hits go. This gives time for the user
			// to load a session via the interface if required
			waitForGo();

			// ok - go has been hit so it's time to stop all future loads
			if( minterfaceObject != null ) minterfaceObject.setEnableLoad( false );

			// make a negotiator if we need a new one (will not be null if re-loaded from state file)
			if( mnegotiator == null ) {
                        // the GEMSS negotiator object needs a description of the job - metadata about the job. This metadata will
                        // be used to construct a server side performance model of the job we require to run. Scheduler reservations
                        // will be based on this performance model. It is in the interest of the client to make the input data metadata
                        // as accurate as possibloe since if the job take too much resources the scheduler will kill it, and the client
                        // will still have to pay.

				// ask the component manager for a GEMSS negotiator instance. Each GEMSSNegotiator instance
				// will be bound to a single application service, and be used to run a single job.
				// You can create many negotiators to run many jobs simultaniously.
				// The GEMSSNegotiatorRequest class holds the init data about the target service. Once service discovery has
				// been included into the framework, the negotiator will not need the specific WSDL URI etc and will be
				// able to find this out for itself.
				System.out.println("Asking for a GEMSSNegotiator to run the job");
				negotiatorRequest = new GEMSSNegotiatorRequest( mstrRequestDescFilename, mstrServiceName, mstrQoSConfigFile, mstrProxyConfigFile, "eu.gemss.components.proxies.GEMSSNegotiator", null, null );
				mnegotiator = (eu.gemss.components.proxies.GEMSSNegotiator) componentManager.getInstance( negotiatorRequest );
				if( mnegotiator == null ) {
					System.out.println("GEMSS Negotiator object null");
					if( minterfaceObject != null ) {
						minterfaceObject.setNegotiatorStatusText( mstaticNegotiatorHeader+"Negotiator stopped due to an infrastructure error [Proxy].\nCheck logs for more information." );
						minterfaceObject.haltInterface( true );
					}
					return; // end
				}
			} // end of negotiator null check

			// wait for the negotiator to finish setting up the job. It will perform any discovery and
			// negotiation steps, and call getCId to get a job conversation ID. Once it has finished the
			// negotiator will be bound to a specific service and ready for use.
			// If the WSDL URI is provided explicitly then this negotiation step is more trivial, and the
			// discovery step bypassed so as to use the URI provided.
			System.out.println("Waiting for negotiation to finish");
			bOk = false;
			nParties = 0;
			nRounds = 0;
			while( !bOk ) {
				// check the negotiator flags
				if( checkFlag( GEMSSNegotiator.mstaticNegotiatorHaltedFlag ) ) {
					// an error has occured during negotiation
					gridException = mnegotiator.getNegotiatorException();
					if( gridException != null ) throw gridException;
					throw new Exception( "Negotiator halted with a null exception" );
				}
				else if( checkFlag( GEMSSNegotiationProxy.mstaticNegCompleteFlag ) &&
					checkFlag( GEMSSNegotiationProxy.mstaticJobPrepFlag ) ) {
					// both the negotiation AND job preperation are complete, so now is the time to move on
					// and do the job handling thing.
					bOk = true;
				}
				else {
					// wait a short time
					try { Thread.sleep( mnOperationRetryDelay ); } catch( InterruptedException ie ) { ; }

					// update interface
					if( minterfaceObject != null ) {
						if( checkFlag( GEMSSNegotiator.mstaticAuctionEndedFlag ) ) {
							minterfaceObject.setWorkflowNodeValue( 0,3,0 ); // discover finished
							minterfaceObject.setWorkflowNodeValue( 1,3,nParties ); // inform finished
							minterfaceObject.setWorkflowNodeValue( 2,3,nRounds ); // call finished
							minterfaceObject.setWorkflowNodeValue( 3,3,nRounds ); // propose finished
							minterfaceObject.setWorkflowNodeValue( 4,3,0 ); // accept finished
							minterfaceObject.setWorkflowNodeValue( 5,1,0 ); // job prop started
						}
						else if( checkFlag( GEMSSNegotiator.mstaticBiddingFinishedFlag ) ) {
							minterfaceObject.setWorkflowNodeValue( 0,3,0 ); // discover finished
							minterfaceObject.setWorkflowNodeValue( 1,3,nParties ); // inform finished
							minterfaceObject.setWorkflowNodeValue( 2,3,nRounds ); // call and propose finished
							minterfaceObject.setWorkflowNodeValue( 3,3,nRounds ); // call and propose finished
							minterfaceObject.setWorkflowNodeValue( 4,1,0 ); // accept started
						}
						else if( checkFlag( GEMSSNegotiator.mstaticQoSPropertiesObtainedFlag ) ) {
							nRounds = (int) mnegotiator.queryAuctionRound(); // update number of rounds
							minterfaceObject.setWorkflowNodeValue( 0,3,0 ); // discover finished
							minterfaceObject.setWorkflowNodeValue( 1,3,nParties ); // inform finished
							minterfaceObject.setWorkflowNodeValue( 2,1,nRounds ); // call and propose started
							minterfaceObject.setWorkflowNodeValue( 3,1,nRounds ); // call and propose started
						}
						else if( checkFlag( GEMSSNegotiator.mstaticAuctionStartedFlag ) ) {
							// get no of parties after neg has done discovery
							if( nParties == 0 ) nParties = mnegotiator.queryNumberOfParties();
							minterfaceObject.setWorkflowNodeValue( 0,3,0 ); // discovery finished
							minterfaceObject.setWorkflowNodeValue( 1,1,nParties ); // inform started
						}
					}
				}
			} // loop on negotiation

			// update interface
			if( minterfaceObject != null ) {
				minterfaceObject.setWorkflowNodeValue( 0,3,nParties ); // discover finished
				minterfaceObject.setWorkflowNodeValue( 1,3,nParties ); // inform finished
				minterfaceObject.setWorkflowNodeValue( 2,3,nRounds ); // call finished
				minterfaceObject.setWorkflowNodeValue( 3,3,nRounds ); // propose finished
				minterfaceObject.setWorkflowNodeValue( 4,3,0 ); // accept finished
				minterfaceObject.setWorkflowNodeValue( 5,3,0 ); // job prep finished
				minterfaceObject.setWorkflowNodeValue( 6,1,0 ); // upload started
			}

			// upload the input archive file to the service
			if( !checkFlag( GEMSSJobHandlingProxy.mstaticUploadCompleteFlag ) ) {
				System.out.println("Input data uploading");
				bOk = false;
				while( !bOk ) {
					// try to upload data
					try {
						mnegotiator.uploadData( mstrInputFilename );
						bOk = true;
					}
					catch( GridException gridex ) {
						// abort if anything other than a network problem
						// otherwise keep trying again and again until timeout
						if( !(gridex instanceof NetworkException) ) throw gridex;
					}
				} // loop until successful
			} // end upload flag check

			// update interface
			if( minterfaceObject != null ) {
				minterfaceObject.setWorkflowNodeValue( 6,3,0 ); // upload finished
				minterfaceObject.setWorkflowNodeValue( 7,1,0 ); // start started
			}

			// start the job
			if( !checkFlag( GEMSSJobHandlingProxy.mstaticJobStartedFlag ) ) {
				System.out.println("Starting job");
				bOk = false;
				while( !bOk ) {
					// try to start data
					try {
						mnegotiator.start();
						bOk = true;
					}
					catch( GridException gridex ) {
						// abort if anything other than a network problem
						// otherwise keep trying again and again until timeout
						if( !(gridex instanceof NetworkException) ) throw gridex;
					}
				} // loop until successful
			} // end started flag check

			// update interface
			if( minterfaceObject != null ) {
				minterfaceObject.setWorkflowNodeValue( 7,3,0 ); // start finished
				minterfaceObject.setWorkflowNodeValue( 8,1,0 ); // running started
			}

			// set the negotiator handle into the interface (for kill + download intermediate files)
			// do now since the job has just started and so both kill and download intermediate
			// files become possible
			if( minterfaceObject != null ) minterfaceObject.setNegotiator( mnegotiator );

			// wait for the job to finish, calling getStatus regularly to get an idea on the
			// progress so far
			bOk = false;
			do {
				System.out.println("Checking status");

				// get status
				try {
					strStatus = mnegotiator.getStatus();
				}
				catch( GridException gridex ) {
					// abort if anything other than a network problem
					// otherwise keep trying again and again until timeout
					if( !(gridex instanceof NetworkException) ) throw gridex;
					strStatus = "unavailable due to network connection problems";
				}

				// get the proxy flags
				vectorFlags = mnegotiator.getNegotiatorFlags();

				// has the job finished, failed or been killed?
				if( vectorFlags.contains( GEMSSJobHandlingProxy.mstaticJobFinishedFlag ) ) {
				   bOk = true;
				   // update status text
				   if( minterfaceObject != null ) {
					   minterfaceObject.setJobStatusText( mstaticJobStatusHeader+"Job has finished" );
					   minterfaceObject.jobFinished();
				   }
				}
				else if( vectorFlags.contains( GEMSSJobHandlingProxy.mstaticJobFailedFlag ) ) {
					// update status text
					if( minterfaceObject != null ) {
						minterfaceObject.setJobStatusText( mstaticJobStatusHeader+"Job has failed" );
						minterfaceObject.haltInterface( true );
					}
					// terminate
					return;
				}
				else if( vectorFlags.contains( GEMSSJobHandlingProxy.mstaticJobKilledFlag ) ) {
					// update status text
					if( minterfaceObject != null ) {
						minterfaceObject.setJobStatusText( mstaticJobStatusHeader+"Job has been killed" );
						minterfaceObject.haltInterface( true );
					}
					// terminate
					return;
				}
				else {
					// update interface with the job status report
					if( minterfaceObject != null ) {
						minterfaceObject.setJobStatusText( mstaticJobStatusHeader + strStatus );
					}

					// The job is on-going, so report the status
					System.out.println( mstaticNegotiatorHeader + strStatus );

					// wait a short time
					if( mnSlowRetry != -1 ) {
						try { Thread.sleep( mnSlowRetry ); } catch( InterruptedException ie ) { ; }
					}

				}
			} while( !bOk );

			// update interface
			if( minterfaceObject != null ) {
				minterfaceObject.setWorkflowNodeValue( 8,3,0 ); // running finished
				minterfaceObject.setWorkflowNodeValue( 9,1,0 ); // download started
			}

			if( !checkFlag( GEMSSJobHandlingProxy.mstaticFinalDownloadCompleteFlag ) ) {
				// download the result data. The empty string argument means that all the result
				// data will be downloaded and the final download flag set. You can specify filenames
				// to download as intermediate downloads if you like, and this can be useful to see how
				// a job is going or to look at partial / low-res results quickly.
				System.out.println("Downloading data");
				bOk = false;
				while( !bOk ) {
					// try to download data
					try {
						mnegotiator.downloadData( "",mstrOutputFilename );
						bOk = true;
					}
					catch( GridException gridex ) {
						// abort if anything other than a network problem
						// otherwise keep trying again and again until timeout
						if( !(gridex instanceof NetworkException) ) throw gridex;
					}
				} // loop until successful
			} // end download flag check

			// update interface
			if( minterfaceObject != null ) {
				minterfaceObject.setWorkflowNodeValue( 9,3,0 ); // download finished
				minterfaceObject.setWorkflowNodeValue( 10,1,0 ); // acknowledge started
			}

			// acknowledge the results
			if( !checkFlag( GEMSSJobHandlingProxy.mstaticAcknowledgementSentFlag ) ) {
				System.out.println("Acknowledging results");
				bOk = false;
				while( !bOk ) {
					// try to acknowledge results
					try {
						mnegotiator.acknowledgeResults();
						bOk = true;
					}
					catch( GridException gridex ) {
						// abort if anything other than a network problem
						// otherwise keep trying again and again until timeout
						if( !(gridex instanceof NetworkException) ) throw gridex;
					}
				} // loop until successful
			} // end acknowledgeResults check

			// update interface
			if( minterfaceObject != null ) {
				minterfaceObject.setWorkflowNodeValue( 10,3,0 ); // acknowledege finished
				minterfaceObject.setWorkflowNodeValue( 11,3,0 ); // finished finished
			}

			// thats it - job run ok and redy to end.
			if( minterfaceObject != null ) {
				minterfaceObject.haltInterface( false );
				minterfaceObject.setNegotiatorStatusText( mstaticNegotiatorHeader+"Negotiator has finished after a successful run.\nThe result data is now ready at "+mstrOutputFilename );
			}
			System.out.println("Job processing complete, data saved to "+mstrOutputFilename);
			return; // success :)
		}
		catch( GridException gridex ) {
			// update interface
			if( minterfaceObject != null ) {
				minterfaceObject.setNegotiatorStatusText( mstaticNegotiatorHeader+"Negotiator stopped due to an Grid error.\n"+gridex.getUserMessage()+"\nCheck logs for more information." );
				minterfaceObject.haltInterface( true );
			}
			System.out.println("GridException: "+gridex.getUserMessage());
			return; // end
		}
		catch( Exception ex ) {
			// update interface
			if( minterfaceObject != null ) {
				minterfaceObject.setNegotiatorStatusText( mstaticNegotiatorHeader+"Negotiator stopped due to an infrastructure error [unrecognized exception].\nCheck logs for more information." );
				minterfaceObject.haltInterface( true );
			}
			System.out.println("Unrecognised failure reason: ");
			ex.printStackTrace();
			return; // end
		}
	} // end of run

	/*
	* set the shutdown hook
	*/
	public void setShutdownHook() {
		//add shutdown hook
		ShutdownThread shutdownHook = new ShutdownThread();
		Runtime.getRuntime().addShutdownHook(shutdownHook);
	} // end of setShutdownHook

	/*
	* shutdown class to respond to a kill request
	*/
	class ShutdownThread extends Thread {
		/*
		* constructor
		*/
		public ShutdownThread() {
			super();
		}

		/*
		* shutdown the component manager
		*/
		public void run() {
			try{
				System.out.println("[Shutdown thread] Shutting down");
				if( mnegotiator != null ) GEMSS.getInstance().terminate( mnegotiator );
				System.out.println("[Shutdown thread] Shutdown complete");
			}
			catch(Exception ex) {
				System.out.println("[Shutdown thread] Shutdown incomplete : "+ex.getMessage());
				ex.printStackTrace();
			}
		}
	} // end of ShutdownThread

	/*
	* Main method to start the sample client application. Call this app with no
	* args to get the argument list help text.
	* @param args argument string
	*/
	public static void main(String[] args) {
		SampleGUI gemssClient;

		try{
			//read in the configuration file name
			if( args.length!=2 || !args[0].equals("-configFile") ) {
				System.out.println("Command line incorrect, format should be: 'java uk.ac.soton.itinnovation.gemss.infrastructure.SampleClientPhaseTwo -configFile <config-file-path>'");
				System.exit(1);
			}

			// create a client instance
			gemssClient = new SampleGUI();
			if( gemssClient == null ) {
				System.out.println("Unrecognised failure, could not create client");
				System.exit(1);
			}
			else {
				//set shutdown hook
				gemssClient.setShutdownHook();
			}

			//run the sample client
			gemssClient.run( args[1] );
		}
		catch( Throwable ex ) {
			System.out.println("Unrecognised failure, reason:");
			ex.printStackTrace();
			System.exit(1);
		}
	} // end of main

	/* file filter class for session files */
	class SessionFileFilter extends FileFilter {
		/* constructor */
		public SessionFileFilter() { super(); }
		/* accept all .sobj files and directories
		* @param file file to decied upon
		* @return true if the file should be displayed (i.e. not filtered)
		*/
		public boolean accept( File file )
		{
			if( file.isDirectory() ) return true;
			if( file.getName().endsWith(".sobj") ) return true;
			return false;
		}
		/* return the description of this filter
		* @return "session file"
		*/
		public String getDescription() { return "GEMSS Session File"; }
	} // end of sessionFileFilter class
} // end of SampleClientPhaseTwo