/////////////////////////////////////////////////////////////////////////
//
// © 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.proxies;

import java.lang.*;
import java.util.*;
import java.io.*;
import java.net.*;
import java.util.logging.Level;
import java.util.logging.Logger;

import uk.ac.soton.itinnovation.gemss.proxies.*;
import uk.ac.soton.itinnovation.gemss.transportmessaging.*;
import uk.ac.soton.itinnovation.gemss.transportmessaging.servicedescription.*;
import uk.ac.soton.itinnovation.gemss.transportmessaging.servicedescription.interfacetypes.*;
import uk.ac.soton.itinnovation.gemss.transportmessaging.servicedescription.policytypes.*;
import uk.ac.soton.itinnovation.gemss.transportmessaging.servicedescription.endpointtypes.*;
import uk.ac.soton.itinnovation.gemss.transportmessaging.payload.typedescriptions.*;
import uk.ac.soton.itinnovation.gemss.transportmessaging.proxies.gemss.*;
import uk.ac.soton.itinnovation.gemss.state.*;
import uk.ac.soton.itinnovation.gemss.xmlobject.*;
import uk.ac.soton.itinnovation.gemss.transportmessaging.payload.*;

import eu.gemss.*;
import eu.gemss.components.*;
import eu.gemss.components.proxies.*;
import eu.gemss.components.transport.*;
import eu.gemss.components.transport.payload.*;
import eu.gemss.components.discovery.*;
import eu.gemss.components.negotiation.*;
import eu.gemss.signals.negotiation.SelectSingleServiceRequest;
import eu.gemss.signals.Signal;

import javax.wsdl.xml.*;
import javax.wsdl.factory.*;
import javax.wsdl.*;


/*
 * GEMSSNegotiator class implementation. Ths class provides the GEMSSNegotiator interface, and has a
 * GEMSSProxy attribute which will actually make all proxy calls.
*/
public class GEMSSNegotiatorImp implements Terminable, Serializable, GEMSSNegotiator {

	// member variables
	private State mstate;
	private NegotiationThread mnegotiationThread;
	private GEMSSProxy mproxyBound;

	// state object tags
	private final static String mstaticRequestDesc = "RequestDescFilename";
      private final static String mstaticQoSConfigFilename = "QoSConfigFilename";
      private final static String mstaticServiceName = "ServiceName";
      private final static String mstaticHistory = "ProposalHistory";
      private final static String mstaticQoSRequirements = "QoSReqs";
      private final static String mstaticAccountConvID = "AccountConvID"; // needed by neg to get initial neg conv ID's

	// logger variable etc
	private Logger mlogger = Logger.getLogger("uk.ac.soton.itinnovation.gemss.proxies.gemssnegotiatorimp");
	private final static String mstaticProxyName = "eu.gemss.components.proxies.GEMSSProxy";

      // private object labels for service metadata binding info. The names here so NOT have to be the
      // same as the internal proxy state names. WSDLURI is not needed since it's explicitly represented
      // within the discovery component.
      private final static String mstaticTagServiceName = "serviceCategory";
      private final static String mstaticTagEndPointPortName = "EndPointPortType";
      private final static String mstaticTagServicePortType = "ServicePortType";
      private final static String mstaticTagBinding = "Binding";

      private final static String mstaticOpUploadData = "UploadAttachmentOp";
      private final static String mstaticOpStart = "StartOp";
      private final static String mstaticOpKill = "KillOp";
      private final static String mstaticOpGetStatus = "GetStatusOp";
      private final static String mstaticOpDownload = "DownloadAttachmentOp";
      private final static String mstaticOpAcknowledgeResults = "AcknowledgeResultsOp";

      private final static String mstaticOpGetWSLA = "GetWSLAOp";
      private final static String mstaticOpUploadRequestDesc = "UploadRequestDescAttachmentOp";
      private final static String mstaticOpAuctionInform = "AuctionInformOp";
      private final static String mstaticOpAuctionCFP = "AuctionCFPOp";
      private final static String mstaticOpAuctionPropose = "AuctionProposeOp";
      private final static String mstaticOpAuctionAcceptProposal = "AuctionAcceptProposalOp";
      private final static String mstaticOpAuctionRejectProposal = "AuctionRejectProposalOp";
      private final static String mstaticOpAuctionRequest = "AuctionRequestOp";
      private final static String mstaticOpGetJobCID = "GetJobCIDOp";
      private final static String mstaticOpGetNegCID = "GetNegCIDOp";

      private final static String mstaticArgConversationID = "CIdArg";
      private final static String mstaticArgFilenameParam = "DownloadFilename";
      private final static String mstaticArgInputDataFileTag = "InputFileTag";
      private final static String mstaticArgOutputDataFileTag = "DownloadFileTag";
      private final static String mstaticArgAuctionMessage = "AuctionMessageTag";

      private final static String mstaticUsePolicy = "UseSecurityPolicyFile";

      // value used for WSDL op names and args if a definition is not provided by the service metadata
      private final static String mstaticUnknownTag = "UnknownTag_negotiation";

	/**
	 * constructor
	 */
	public GEMSSNegotiatorImp()
	{
		super();

		// initialise state. The GEMSSProxyProvider will create a new instance of the
		// proxy component each time it is asked to. This proxy init() method will be called by the
		// GEMSSProxyProvider to initialize. However, we should do the alloc of objects
		// in the constructor.

		mstate = new State();
		mnegotiationThread = null;
		mproxyBound = null;

		// all done
		return;
	}

	/**
	 * destructor
	 */
	public void finalize() throws Throwable
	{
		// all done
        return;
	}

	/**
	 * Signal to kill on-going threads but do not wait for them to finish. If state is required to be saved
	 * then the serialization functions should have been called prior to this method.
	 */
	synchronized public void terminate()
	{
		try
		{
			// stop the threads if needed
			if( mnegotiationThread != null ) {
				mnegotiationThread.flagStop();
			}

			// all done
			return;
		}
		catch ( Exception ex )
		{
			mlogger.log( Level.SEVERE,"Exception during terminate",ex);
			return;
		}
	}

	/**
	 * Initiate the negotiation for the execution of a job. Multiple service providers will be invited to
	 * bid for the job, and the winner will have a job conversation ID set-up for use by the client. The
	 * negotiation thread spawned by init will terminate once the job conversation is ready. The job handling
	 * functions will then become available for use.
	 * @param strRequestDescFilename filename of the application specific description of the input data characteristics. This will be
       * used by the service providers to estimate exec resource and thus formulate a bid
       * @param strServiceName name of the service to be run
       * @param strQoSConfigFile config filename for the QoSNegotiation component (if null a signal will fire to ask for service selection)
       * @param strProxyConfigFile config filename for a GEMSSProxy (if null discovery will occur)
	 */
	public void init( String strRequestDescFilename, String strServiceName, String strQoSConfigFile, String strProxyConfigFile ) throws Exception
	{
            String strAccountID;

		try
		{
                  // bind to proxy if provided with a config file
                  if( strProxyConfigFile != null ) {
                        // bind the proxy to these end point parameters
                        mproxyBound = getProxyWithEndPoint( strProxyConfigFile );

                        // setup account info etc
                        // TODO get account conv ID from the discovery metadata (or a local database ?!?)
                        strAccountID = "AccountConvID-NOT_SUPPORTED";

                        // register account ID (needed to allow startAuction to call getNegCID successfully)
                        mproxyBound.setAccountConversationID( strAccountID );

                        // set the negotiation complete flag since we have an end point
                        // this will make the negotiator thread skip straight to the job preperation
                        // stage, and not do the negotiation stuff.
                        mstate.addFlag( GEMSSNegotiationProxy.mstaticNegCompleteFlag );
                  }

			// add request desc and service name to state
			mstate.addObject( mstaticRequestDesc,strRequestDescFilename );
                  mstate.addObject( mstaticServiceName,strServiceName );
                  if( strQoSConfigFile != null ) {
                        mstate.addObject( mstaticQoSConfigFilename, strQoSConfigFile );
                  }
                  else {
                        mstate.addObject( mstaticQoSConfigFilename, "" );
                  }

			// init the negotiation thread
			mnegotiationThread = new NegotiationThread();
			mnegotiationThread.init( mstate,this );

			// start the negotiation thread
			mnegotiationThread.start();

			// all done
			return;
		}
		catch ( Exception ex )
		{
			mlogger.log( Level.SEVERE,"Exception during init(1)",ex);
			throw new Exception("init failed");
		}
	}

	/**
	 * Return the flags for this negotiator. The GEMSSJobHandlingProxy and GEMSSNegotiationProxy
	 * interfaces support the flags used. Null is returned on error.
	 * @return vector of flag strings
	 */
	public Vector getNegotiatorFlags()
	{
		Vector vectorProxy, vectorResult;
		int i;

		try
		{
			// return all flags of negotiator AND it's proxy (if instantiated)
			// get proxy flags
			vectorProxy = null;
			if( mproxyBound != null ) {
				vectorProxy = mproxyBound.getProxyFlags();
			}

			// get negotiator flags
			vectorResult = mstate.getFlags();

			// combine the two
			if( vectorProxy != null ) {
				for( i=0;i<vectorProxy.size();i++ ) {
					vectorResult.addElement( (String) vectorProxy.elementAt(i) );
				}
			}

			// return result vector
			return vectorResult;
		}
		catch ( Exception ex )
		{
			mlogger.log( Level.SEVERE,"Exception during getNegotiatorFlags",ex);
			return null;
		}
	}

	/**
	 * Return the negotiator's GridException, if any, that is recorded when the negotiator
	 * is halted and the GEMSSNegotiator.mstaticNegotiatorHaltedFlag flag is set. If there is
	 * no exception, or the negotiator is not halted, then a null is returned.
	 * @return GridException type that halted the negotiator (or null if there is none)
	 */
	synchronized public GridException getNegotiatorException()
	{
		if( mnegotiationThread == null ) return null;
		return mnegotiationThread.getGridException();
	}

      /**
       * Return the number of parties in the current negotiation. This will be 1 after a proxy is bound, or zero
       * if the negotiation failed.
       * @return number of parties (proxies)
       */
      public int queryNumberOfParties()
      {
            Vector vectorFlags, vectorParties;
            ProposalHistory history;

            try {
                  // if bound only one party left
                  if( mproxyBound != null ) return 1;

                  // if auction halted no parties left
                  vectorFlags = getNegotiatorFlags();
                  if( vectorFlags.contains( GEMSSNegotiator.mstaticNegotiatorHaltedFlag ) ) return 0;

                  // if auction finished, must have failed to bind to a proxy
                  if( vectorFlags.contains( GEMSSNegotiator.mstaticAuctionEndedFlag ) ) return 0;

                  // get proposal history and return the parties
                  history = (ProposalHistory) mstate.getObject( mstaticHistory );
                  if( history == null ) return 0;
                  vectorParties = (Vector) history.getParties();
                  if( vectorParties == null ) return 0;
                  return vectorParties.size();
            }
            catch( Exception ex ) {
                  mlogger.log( Level.SEVERE,"Failed to query number of parties - returning 0",ex );
                  return 0;
            }
      }

      /**
       * Return the current bidding round for the negotiation (auction). The final round is returned if the auction has ended.
       * @return current auction round number
       */
      public long queryAuctionRound()
      {
            Vector vectorFlags, vectorParties;
            ProposalHistory history;

            try {
                  // get proposal history and return the parties
                  history = (ProposalHistory) mstate.getObject( mstaticHistory );
                  if( history == null ) return 0;
                  return history.getLastTimestamp();
            }
            catch( Exception ex ) {
                  mlogger.log( Level.SEVERE,"Failed to query auction round - returning 0",ex );
                  return 0;
            }
      }

	/**
	 * Starts the job.
	 * This method will block.
	 */
	public void start() throws GridException
	{
		// let exceptions feed back to caller
		if( mproxyBound != null ) {
			mproxyBound.start();
		}
		else throw new GridException("Proxy not bound to negotiator yet, not ready for start. This is probably an application error, please check logs for more information.","mproxyBound = null");
	}

	/**
	 * Uploads data from the provided file to the Grid server.
	 * This method will block.
	 * @param inputArchiveFilename input filename to pass to the proxy for upload to the server
	 */
	public void uploadData( String inputArchiveFilename ) throws GridException
	{
		// let exceptions feed back to caller
		if( mproxyBound != null ) {
			mproxyBound.uploadData( inputArchiveFilename );
		}
		else throw new GridException("Proxy not bound to negotiator yet, not ready for upload. This is probably an application error, please check logs for more information.","mproxyBound = null");
	}

	/**
	 * Downloads the result data for a job.
	 * This method will block.
	 * @param filename of file to download. If all the
	 * files are required then an empty string should be provided. If the empty string is used then the
	 * proxy will set the "FinalDownloadComplete" flag.
	 * @param outputArchiveFilename filename of the output archive where the resulting downloaded archive can be saved. All files
	 * requested in the filename list will be archived by the service and downloaded to this filename.
	 */
	public void downloadData( String filename, String outputArchiveFilename ) throws GridException
	{
		// let exceptions feed back to caller
		if( mproxyBound != null ) {
			mproxyBound.downloadData( filename,outputArchiveFilename );
		}
		else throw new GridException("Proxy not bound to negotiator yet, not ready for download. This is probably an application error, please check logs for more information.","mproxyBound = null");
	}

	/**
	 * Retrieves the job status.
	 * This method will block.
	 * @return a string containing the application specific status return.
	 */
	public String getStatus() throws GridException
	{
		// let exceptions feed back to caller
		if( mproxyBound != null ) {
			return mproxyBound.getStatus();
		}
		else throw new GridException("Proxy not bound to negotiator yet, not ready for getStatus. This is probably an application error, please check logs for more information.","mproxyBound = null");
	}

	/**
	 * Kills the running job.
	 * This method will block.
	 */
	public void kill() throws GridException
	{
		// let exceptions feed back to caller
		if( mproxyBound != null ) {
			mproxyBound.kill();
		}
		else throw new GridException("Proxy not bound to negotiator yet, not ready for kill. This is probably an application error, please check logs for more information.","mproxyBound = null");
	}

	/**
	 * Acknowledge results. This methods can be called when the client is happy with the results
	 * downloaded, and wants the service provider to end the job. The service provider will delete
	 * all traces of data from the server side, important when dealing with medical data etc.
	 * This method will block.
	 */
     public void acknowledgeResults() throws GridException
     {
	     // let exceptions feed back to caller
	     if( mproxyBound != null ) {
		     mproxyBound.acknowledgeResults();
	     }
	     else throw new GridException("Proxy not bound to negotiator yet, not ready for acknowledgeResults. This is probably an application error, please check logs for more information.","mproxyBound = null");
     }

     /**
      * Creats a new proxy object with the provided end point.
      * @param strConfigFile filename where the proxy config file is containing the end point data etc
      */
     private GEMSSProxy getProxyWithEndPoint( String strConfigFile )
     {
           GEMSSProxyRequest proxyRequest;
           ComponentManager compMan;

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

                 // make the request object
                 proxyRequest = new GEMSSProxyRequest( strConfigFile, mstaticProxyName, null, null );

                 // get a proxy bound to this end point (service)
                 return (GEMSSProxy) compMan.getInstance( proxyRequest );
           }
           catch( Exception ex ) {
                 mlogger.log( Level.SEVERE,"Failed to get proxy with an end point",ex);
                 return null;
           }
     }

     /**
      * Creats a new proxy object with the provided end point.
      * @param strWSDLURI WSDL URI for this proxy to bind to
      * @param strServiceName The name of the service
      * @param strEndPointPortName The name of the end point port
      * @param strServicePortType The name of the service port
      * @param strBinding The binding type to be used
      * @param strOpUploadData Name of the upload operation
      * @param strOpStart Name of the start operation
      * @param strOpKill Name of the kill operation
      * @param strOpGetStatus Name of the get status op
      * @param strOpDownload Name of the download op
      * @param strOpAcknowledgeResults Name of the acknowledge results op
      * @param strOpGetWSLA Name of the get WSLA op
      * @param strOpUploadRequestDesc Name of the upload request descriptor file op
      * @param strOpAuctionInform Name of the auction inform op
      * @param strOpAuctionCFP Name of the auction call for proposals op
      * @param strOpAuctionPropose Name of the auctino propose op
      * @param strOpAuctionAcceptProposal Name of the auction accept proposal op
      * @param strOpAuctionRejectProposal Name of the auction reject proposal op
      * @param strOpAuctionRequest Name of the auction request op
      * @param strOpGetJobCID Name of the getJobCID op
      * @param strOpGetNegCID Name of the getNegCID op
      * @param strArgConversationID Name of the conv ID argument type
      * @param strArgFilenameParam Name of the filename argument type
      * @param strArgInputDataFileTag Name of the input file tag argument (for SOAP attachment version of upload/download)
      * @param strArgOutputDataFileTag Name of the output file tag argument (for SOAP attachment version of upload/download)
      * @param strArgAuctionMessage Name of the auction message tag argument
      * @param bUsePolicy use security policy (false = no policy)
      */
     private GEMSSProxy getProxyWithAnEndPoint( String strWSDLURI, String strServiceName, String strEndPointPortName, String strServicePortType,
                                String strBinding, String strOpUploadData, String strOpStart, String strOpKill,
                                String strOpGetStatus, String strOpDownload, String strOpAcknowledgeResults,
                                String strOpGetWSLA, String strOpUploadRequestDesc, String strOpAuctionInform, String strOpAuctionCFP,
                                String strOpAuctionPropose, String strOpAuctionAcceptProposal, String strOpAuctionRejectProposal,
                                String strOpAuctionRequest, String strOpGetJobCID, String strOpGetNegCID,
                                String strArgConversationID, String strArgFilenameParam, String strArgInputDataFileTag, String strArgOutputDataFileTag,
                                String strArgAuctionMessage,
                                boolean bUsePolicy  )
     {
           GEMSSProxyRequest proxyRequest;
           ComponentManager compMan;

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

                 // make the request object using the args passed
                 proxyRequest = new GEMSSProxyRequest(
                             strWSDLURI, strServiceName, strEndPointPortName, strServicePortType,
                             strBinding, strOpUploadData, strOpStart, strOpKill,
                             strOpGetStatus, strOpDownload, strOpAcknowledgeResults,
                             strOpGetWSLA, strOpUploadRequestDesc, strOpAuctionInform, strOpAuctionCFP,
                             strOpAuctionPropose, strOpAuctionAcceptProposal, strOpAuctionRejectProposal,
                             strOpAuctionRequest, strOpGetJobCID, strOpGetNegCID,
                             strArgConversationID, strArgFilenameParam, strArgInputDataFileTag, strArgOutputDataFileTag,
                             strArgAuctionMessage,
                             bUsePolicy,
                             mstaticProxyName, null, null );

                 // get a proxy bound to this end point (service)
                 return (GEMSSProxy) compMan.getInstance( proxyRequest );
           }
           catch( Exception ex ) {
                 mlogger.log( Level.SEVERE,"Failed to get proxy with an end point",ex);
                 return null;
           }
     }

     /**
      * Creats a new proxy object and binds it to the service end point passed
      * in the paremeters. An exception will be thrown if the attribute list does not
      * contain all the required atributes to make the binding (e.g. operation name,
      * arg names etc).
      * The list of required attributes is :
      * @param strWSDLURI WSDL URI for this proxy to bind to
      * @param serviceAttributeArray Array of attributes from the service.
      * These attributes must contain all the operation names and argument types from the
      * service registry.
      */
     private GEMSSProxy getProxyWithAnEndPoint( String strWSDLURI, Attribute[] serviceAttributeArray )
     {
           GEMSSProxyRequest proxyRequest;
           ComponentManager compMan;
           String strServiceName = null, strEndPointPortName = null, strServicePortType = null;
           String strBinding = null, strOpUploadData = null, strOpStart = null, strOpKill = null;
           String strOpGetStatus = null, strOpDownload = null, strOpAcknowledgeResults = null;
           String strOpGetWSLA = null, strOpUploadRequestDesc = null, strOpAuctionInform = null, strOpAuctionCFP = null;
           String strOpAuctionPropose = null, strOpAuctionAcceptProposal = null, strOpAuctionRejectProposal = null;
           String strOpAuctionRequest = null, strOpGetJobCID = null, strOpGetNegCID = null;
           String strArgConversationID = null, strArgFilenameParam = null, strArgInputDataFileTag = null, strArgOutputDataFileTag = null;
           String strArgAuctionMessage = null, strArgServiceName = null;
           boolean bUsePolicy = GEMSSProxyImp.mbstaticUsePolicyFileDefault;
           String strAttributeName, strAttributeValue;
           int i,j;

           try {
                 // check array is ok
                 if( serviceAttributeArray == null ) throw new Exception("Service attribute array is null");

                 // get each of the attributes required to bind to the proxy
                 for( i=0;i<26;i++ ) {
                       // get attribute to find within the attribute list
                       switch( i ) {
                             case 0 : strAttributeName = mstaticTagServiceName; break;
                             case 1 : strAttributeName = mstaticTagEndPointPortName; break;
                             case 2 : strAttributeName = mstaticTagServicePortType; break;
                             case 3 : strAttributeName = mstaticTagBinding; break;
                             case 4 : strAttributeName = mstaticOpUploadData; break;
                             case 5 : strAttributeName = mstaticOpStart; break;
                             case 6 : strAttributeName = mstaticOpKill; break;
                             case 7 : strAttributeName = mstaticOpGetStatus; break;
                             case 8 : strAttributeName = mstaticOpDownload; break;
                             case 9 : strAttributeName = mstaticOpAcknowledgeResults; break;
                             case 10 : strAttributeName = mstaticOpGetWSLA; break;
                             case 11 : strAttributeName = mstaticOpUploadRequestDesc; break;
                             case 12 : strAttributeName = mstaticOpAuctionInform; break;
                             case 13 : strAttributeName = mstaticOpAuctionCFP; break;
                             case 14 : strAttributeName = mstaticOpAuctionPropose; break;
                             case 15 : strAttributeName = mstaticOpAuctionAcceptProposal; break;
                             case 16 : strAttributeName = mstaticOpAuctionRejectProposal; break;
                             case 17 : strAttributeName = mstaticOpAuctionRequest; break;
                             case 18 : strAttributeName = mstaticOpGetJobCID; break;
                             case 19 : strAttributeName = mstaticOpGetNegCID; break;
                             case 20 : strAttributeName = mstaticArgConversationID; break;
                             case 21 : strAttributeName = mstaticArgFilenameParam; break;
                             case 22 : strAttributeName = mstaticArgInputDataFileTag; break;
                             case 23 : strAttributeName = mstaticArgOutputDataFileTag; break;
                             case 24 : strAttributeName = mstaticArgAuctionMessage; break;
                             case 25 : strAttributeName = mstaticUsePolicy; break;
                             default : throw new Exception("Invalid index for switch (1)");
                       } // end switch

                       // find the attribute value
                       strAttributeValue = null;
                       for( j=0;j<serviceAttributeArray.length;j++ ) {
                             if( serviceAttributeArray[j].getName().equals( strAttributeName ) ) {
                                   strAttributeValue = serviceAttributeArray[j].getValue();
                                   j = serviceAttributeArray.length;
                             }
                       }

                       // check it has a value
                        if( strAttributeValue == null ) {
                              // use empty string for values not specified in the registry metadata.
                              // This might want to throw an exception to prevent partially defined operation
                              // names being used - for now it's allowed so you can have a job handling proxy
                              // without the neg functions.
                              // cannot use nulls since State hashtable cannot have null entries.
                              mlogger.log( Level.WARNING,"Attribute "+strAttributeName+" not found in service attribute array - empty string used for value (this is probably an error)");
                              strAttributeValue = mstaticUnknownTag+strAttributeName;
                        }

                       // store the attribute value in the appropriate variable
                        switch( i ) {
                              case 0 : strServiceName = strAttributeValue; break;
                              case 1 : strEndPointPortName = strAttributeValue; break;
                              case 2 : strServicePortType = strAttributeValue; break;
                              case 3 : strBinding = strAttributeValue; break;
                              case 4 : strOpUploadData = strAttributeValue; break;
                              case 5 : strOpStart = strAttributeValue; break;
                              case 6 : strOpKill = strAttributeValue; break;
                              case 7 : strOpGetStatus = strAttributeValue; break;
                              case 8 : strOpDownload = strAttributeValue; break;
                              case 9 : strOpAcknowledgeResults = strAttributeValue; break;
                              case 10 : strOpGetWSLA = strAttributeValue; break;
                              case 11 : strOpUploadRequestDesc = strAttributeValue; break;
                              case 12 : strOpAuctionInform = strAttributeValue; break;
                              case 13 : strOpAuctionCFP = strAttributeValue; break;
                              case 14 : strOpAuctionPropose = strAttributeValue; break;
                              case 15 : strOpAuctionAcceptProposal = strAttributeValue; break;
                              case 16 : strOpAuctionRejectProposal = strAttributeValue; break;
                              case 17 : strOpAuctionRequest = strAttributeValue; break;
                              case 18 : strOpGetJobCID = strAttributeValue; break;
                              case 19 : strOpGetNegCID = strAttributeValue; break;
                              case 20 : strArgConversationID = strAttributeValue; break;
                              case 21 : strArgFilenameParam = strAttributeValue; break;
                              case 22 : strArgInputDataFileTag = strAttributeValue; break;
                              case 23 : strArgOutputDataFileTag = strAttributeValue; break;
                              case 24 : strArgAuctionMessage = strAttributeValue; break;
                              case 25 : bUsePolicy = Boolean.getBoolean( strAttributeValue );
mlogger.log( Level.INFO,"Policy file metadata == "+bUsePolicy );
break;
                              default : throw new Exception("Invalid index for switch (2)");
                        } // end switch
                 } // next attribute

                 // bind to the proxy using the new values
                 return getProxyWithAnEndPoint ( strWSDLURI, strServiceName, strEndPointPortName, strServicePortType,
                                         strBinding, strOpUploadData, strOpStart, strOpKill,
                                         strOpGetStatus, strOpDownload, strOpAcknowledgeResults,
                                         strOpGetWSLA, strOpUploadRequestDesc, strOpAuctionInform, strOpAuctionCFP, strOpAuctionPropose,
                                         strOpAuctionAcceptProposal, strOpAuctionRejectProposal, strOpAuctionRequest, strOpGetJobCID, strOpGetNegCID,
                                         strArgConversationID, strArgFilenameParam, strArgInputDataFileTag, strArgOutputDataFileTag, strArgAuctionMessage,
                                         bUsePolicy );
           }
           catch( Exception ex ) {
                 mlogger.log( Level.SEVERE,"Failed to get proxy with an end point using service attributes ",ex);
                 return null;
           }
     }

     /**
      * Generate and process a select single service signal, and return the resulting proxy. A null
      * is returned if the signal is not handled.
      * This method will block.
      * @param listServices list of services obtained from the discovery component
      * @return GEMSS proxy object selected by the app/user
      */
     private GEMSSProxy manualServiceSelection( List listServices ) throws Exception
     {
           ComponentManager compMan;
           SelectSingleServiceRequest requestSelect;
           eu.gemss.components.discovery.ServiceDescription service, selectedService;
           Signal [] arraySignals;
           Attribute[] attributeArray;
           Vector vectorMetadata;
           String strSelectedService, strServiceName;
           Iterator iterator;
           int i;

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

                 // generate a service selection request (single)
                 requestSelect = new SelectSingleServiceRequest( this );

                 // generate request service info strings
                 iterator = listServices.iterator();
                 while ( iterator.hasNext()) {
                       service = (eu.gemss.components.discovery.ServiceDescription) iterator.next();
                       vectorMetadata = new Vector();
                       vectorMetadata.addElement( "Service metadata" );
                       vectorMetadata.addElement( "----------------" );
                       attributeArray = service.getAttributes();
                       if( attributeArray != null ){
                             for( i=0 ; i < attributeArray.length ; i++ ) {
                                   // add string to service metadata
                                   vectorMetadata.addElement( attributeArray[i].getName() + " = " +attributeArray[i].getValue() );
                             }
                       }
                       requestSelect.addServiceToList( service.getWsdlEndpoint(), vectorMetadata );
                 }

                 // wait for the answer (comp man will block until all responses are in)
                 arraySignals = compMan.generateSignal( requestSelect );
                 if( arraySignals.length == 0 ) throw new Exception("No handlers responded to SelectServiceRequest signal");
                 if( !(arraySignals[0] instanceof SelectSingleServiceRequest) ) throw new Exception("Handler returned an incorrect signal response type - aborting");
                 if( arraySignals.length > 1 ) mlogger.log( Level.WARNING,"Multiple handlers responded to SelectServiceRequest - first response used only, others are ignored");

                 // get the selected service's name
                 strSelectedService = ((SelectSingleServiceRequest) arraySignals[0]).getSelectedService();
                 if( strSelectedService == null ) throw new Exception("No service selected - aborting");

                 // find the service from the original discovery service list
                 selectedService = null;
                 iterator = listServices.iterator();
                 while ( iterator.hasNext()) {
                       service = (eu.gemss.components.discovery.ServiceDescription) iterator.next();
                       if( service.getWsdlEndpoint().equals( strSelectedService ) ) {
                             selectedService = service;
                             break;
                       }
                 }
                 if( selectedService == null ) throw new Exception("Chosen service does not match any services in the original list - aborting");

                 mlogger.log( Level.INFO,"GEMSSNegotiator NegotiationThread binding service to GEMSS Proxy step using "+selectedService.getWsdlEndpoint());
                 mlogger.log( Level.INFO,"Probe: manualServiceSelection\t"+selectedService.getWsdlEndpoint() );

                 // bind proxy to the selected service, using its attributes for operation name etc.
                 // these attributes must be held within the servuce registry else the bind
                 // method will not find them and an exception will be thrown
                 return getProxyWithAnEndPoint( selectedService.getWsdlEndpoint(), selectedService.getAttributes() );
           }
           catch( Exception ex ) {
                 mlogger.log( Level.SEVERE,"Error manually selecting a service - returning a null proxy object",ex );
                 return null;
           }
     }

     /**
      * Start an auction with a list of services. This method will block. The participants are removed if they
      * cause an error, after an invocation timeout limit by the QoS Negotiation component config. This method
      * keeps looping until all participants work or there are none left.
      * @param listServices list of services obtained from the discovery component
      * @param qosnegotiation QoSNegotiation component to use to actually do the startAuction etc
      * @param runThread thread object that spanwed this processing. This is required to check for the stop flag being
      * set and thus allowing for a graceful and timely exit
      * @return proposal history object, newly created
      */
     private ProposalHistory startAuction( List listServices, QoSNegotiation qosnegotiation, NegotiationThread runThread ) throws Exception
     {
           eu.gemss.components.discovery.ServiceDescription service;
           Vector vectorProxies, vectorBadList;
           GEMSSProxy proxy;
           ParticipantsNotReadyException partex;
           ProposalHistory history;
           String strRequestDescFilename, strAccountID;
           Iterator iterator;
           int i;
           boolean bRetry;

           // check outside try due to catch using the run thread
           if( runThread == null ) throw new Exception("null run thread");

           try {
                 // check params
                 if( qosnegotiation == null ) throw new Exception("null qos negotiation component");
                 if( listServices == null ) throw new Exception("null service list");
                 history = null;

                 // start auction
                 // setup args (req desc filename and proxy list)
                 strRequestDescFilename = (String) mstate.getObject( mstaticRequestDesc );
                 if( strRequestDescFilename == null ) throw new Exception("null request desc filename");

                 vectorProxies = new Vector();
                 for( i=0;i<listServices.size();i++ ) {
                       service = (eu.gemss.components.discovery.ServiceDescription) listServices.get(i);

// TODO let user select which service providers to enter into the auction?
// Make a SelectMultipleServiceProviders signal
                       proxy = getProxyWithAnEndPoint( service.getWsdlEndpoint(), service.getAttributes() );
                       if( proxy == null ) {
                             // ignore bad URI's and warn for debugging
                             mlogger.log( Level.WARNING,"Failed to bind to a proxy URI obtained from the registry, URI = "+service.getWsdlEndpoint() );
                       }
                       else {
                             // TODO get account conv ID from the discovery metadata (or a local database ?!?)
                             strAccountID = "AccountConvID-NOT_SUPPORTED";

                             // register account ID (needed to allow startAuction to call getNegCID successfully)
                             proxy.setAccountConversationID( strAccountID );

                             // add proxy to the proxies vector
                             vectorProxies.addElement( proxy );
                             mlogger.log( Level.INFO,"Probe: bind-proxy\t"+strAccountID );
                       } // null proxy check
                 } // next service in discovery list

                 // check to see if we have ANY proxies!
                 if( vectorProxies.size() == 0 ) throw new Exception("No working proxies to start auction with");

                 // start auction with this list of services
                 bRetry = true;
                 while( bRetry ) {
                       bRetry = false;
                       try {
                             if( runThread.queryStopFlag() ) throw new GEMSSNegotiatorShutdownException( "GEMSSNegotiator component terminating - work aborted","stop flag set" );
                             history = qosnegotiation.startAuction( strRequestDescFilename, vectorProxies );
                       }
                       catch( GridException gridex ) {
                             // partial problem with some participants or something fatal?
                             if( gridex instanceof ParticipantsNotReadyException ) {
                                   // A ParticipantsNotReadyException means that, despite retries in case of network errors,
                                   // some of the participants are unable to start the auction. This code will remove them
                                   // from the list and proceed with the auction containing the remaining participants
                                   partex = (ParticipantsNotReadyException) gridex;
                                   vectorBadList = partex.getProxies();
                                   if( vectorBadList != null ) {
                                         // loop on bad list and remove them from the vectorProxies list
                                         for( i=0;i<vectorBadList.size();i++ ) {
                                               // get bad proxy
                                               proxy = (GEMSSProxy) vectorBadList.elementAt(i);
                                               if( proxy == null ) throw new Exception("null participant in bad vector");

                                               // remove bad proxy from original list of proxies
                                               if( vectorProxies.contains( proxy ) ) {
                                                     vectorProxies.removeElement( proxy );
                                                     mlogger.log( Level.INFO,"Participant was not ready and was removed : NegID = "+proxy.queryNegConversationID() );
                                                     mlogger.log( Level.INFO,"Probe: proxy-not-ready\t"+proxy.queryNegConversationID() );
                                               }
                                               else throw new Exception("Participant not found in proxy vector");

                                         } // next bad proxy

                                         // any left ?
                                         if( vectorProxies.size() == 0 ) throw new Exception("No working proxies left");
                                         bRetry = true;
                                   }
                                   else {
                                         // paranoia check
                                         throw new Exception( "Participants not ready - ill formatted exception thrown" );
                                   }
                             }
                             else {
                                   throw gridex;
                             }
                       } // end try/catch for GridException
                 } // retry loop

                 // startAuction successful :)
                 mlogger.log( Level.INFO,"Auction started ok" );
                 mlogger.log( Level.INFO,"Probe: auction-started-ok" );
                 return history;
           }
           catch( GridException gridex ) {
                 // pass on through GridExceptions
                 runThread.setGridException( gridex );
                 throw gridex;
           }
           catch( Exception ex ) {
                 // log real error fully
                 mlogger.log( Level.SEVERE,"Error starting the auction, failed to recover",ex );

                 // record a useful exception for the app to display
                 runThread.setGridException( new GEMSSNegotiatorException( "Failed to start auction, failed to recover. Please check logs for details","startAuction failed : "+ex.getMessage() ) );
                 throw ex; // pass exception on through
           }
     }

     /**
      * Get the QoS requirements. This method will block.
      * @param qosnegotiation QoSNegotiation component to use to actually do the getQoSRequirements etc
      * @param runThread thread object that spanwed this processing. This is required to check for the stop flag being
      * set and thus allowing for a graceful and timely exit
      * @return QoS requirements, or null if none are available
      */
     private QoSRequirements getQoSRequirements( QoSNegotiation qosnegotiation, NegotiationThread runThread ) throws Exception
     {
           String strServiceName, strJobDescription;
           ProposalHistory history;

           // check outside try due to catch using the run thread
           if( runThread == null ) throw new Exception("null run thread");

           try {
                 // check params
                 if( qosnegotiation == null ) throw new Exception("null qos negotiation component");

                 // make the descriptions (service and and human readble job description
                 // for a possible GUI dialogue box
                 strServiceName = (String) mstate.getObject( mstaticServiceName );
                 if( strServiceName == null ) throw new Exception("Service name state is null" );
                 strJobDescription = "Request desc file = "+ (String) mstate.getObject( mstaticRequestDesc );

                 // get proposal history to date
                 history = (ProposalHistory) mstate.getObject( mstaticHistory );

                 // get the QoS requirements. No retry needed on this since you get them or you don't.
                 if( runThread.queryStopFlag() ) throw new GEMSSNegotiatorShutdownException( "GEMSSNegotiator component terminating - work aborted","stop flag set" );
                 return qosnegotiation.getQoSRequirements( strServiceName, strJobDescription, history );
           }
           catch( GridException gridex ) {
                 // record GridException
                 runThread.setGridException( gridex );
                 throw gridex;
           }
           catch( Exception ex ) {
                 // log real error fully
                 mlogger.log( Level.SEVERE,"Error getting QoS requirements for auction, failed to recover",ex );

                 // record a useful exception for the app to display
                 runThread.setGridException( new GEMSSNegotiatorException( "Failed to start auction, failed to recover. Please check logs for details","startAuction failed : "+ex.getMessage() ) );
                 throw ex; // pass exception on through
           }
     }

     /**
      * Send CFP's to all participant service providers
      * @param qosnegotiation QoSNegotiation component to use to actually do the startAuction etc
      * @param runThread thread object that spanwed this processing. This is required to check for the stop flag being
      * @return proposal history object, ready to be added into state (to repace existing one). This is likely to be the same object as the original state history.
      */
     private ProposalHistory sendCFPs( QoSNegotiation qosnegotiation, NegotiationThread runThread ) throws Exception
     {
           ProposalHistory history;
           QoSRequirements reqs;

           // check outside try due to catch using the run thread
           if( runThread == null ) throw new Exception("null run thread");

           try {
                 // check params
                 if( qosnegotiation == null ) throw new Exception("null qos negotiation component");

                 // get proposal history to date and reqs
                 history = (ProposalHistory) mstate.getObject( mstaticHistory );
                 reqs = (QoSRequirements) mstate.getObject( mstaticQoSRequirements );

                 // for all participants call sendCFPs
                 if( runThread.queryStopFlag() ) throw new GEMSSNegotiatorShutdownException( "GEMSSNegotiator component terminating - work aborted","stop flag set" );
                 return qosnegotiation.sendCFPs( reqs, history );
           }
           catch( GridException gridex ) {
                 // record GridException
                 runThread.setGridException( gridex );
                 throw gridex;
           }
           catch( Exception ex ) {
                 // log real error fully
                 mlogger.log( Level.SEVERE,"Error sending CFP's for auction, failed to recover",ex );

                 // record a useful exception for the app to display
                 runThread.setGridException( new GEMSSNegotiatorException( "Failed to send calls for proposals during the auction. Please check logs for details","sendCFPs failed : "+ex.getMessage() ) );
                 throw ex; // pass exception on through
           }
     }

     /**
      * Get proposals from all participants
      * @param qosnegotiation QoSNegotiation component to use to actually do the startAuction etc
      * @param runThread thread object that spanwed this processing. This is required to check for the stop flag being
      * @return proposal history object, ready to be added into state (to repace existing one). This is likely to be the same object as the original state history.
      */
     private ProposalHistory getProposals( QoSNegotiation qosnegotiation, NegotiationThread runThread ) throws Exception
     {
           ProposalHistory history;

           // check outside try due to catch using the run thread
           if( runThread == null ) throw new Exception("null run thread");

           try {
                 // check params
                 if( qosnegotiation == null ) throw new Exception("null qos negotiation component");

                 // get proposal history to date
                 history = (ProposalHistory) mstate.getObject( mstaticHistory );

                 // for all participants call sendCFPs
                 if( runThread.queryStopFlag() ) throw new GEMSSNegotiatorShutdownException( "GEMSSNegotiator component terminating - work aborted","stop flag set" );
                 return qosnegotiation.getProposals( history );
           }
           catch( GridException gridex ) {
                 // record GridException
                 runThread.setGridException( gridex );
                 throw gridex;
           }
           catch( Exception ex ) {
                 // log real error fully
                 mlogger.log( Level.SEVERE,"Error getting proposals for auction, failed to recover",ex );

                 // record a useful exception for the app to display
                 runThread.setGridException( new GEMSSNegotiatorException( "Failed to get proposals during the auction. Please check logs for details","getProposals failed : "+ex.getMessage() ) );
                 throw ex; // pass exception on through
           }
     }

     /**
      * Evaluate a set of bids for the current timestamp
      * @param qosnegotiation QoSNegotiation component to use to actually do the startAuction etc
      * @param runThread thread object that spanwed this processing. This is required to check for the stop flag being
      * @return true if the auction should continue, false if it should stop and move to bid acceptance
      */
     private boolean evaluateProposals( QoSNegotiation qosnegotiation, NegotiationThread runThread ) throws Exception
     {
           ProposalHistory history;

           // check outside try due to catch using the run thread
           if( runThread == null ) throw new Exception("null run thread");

           try {
                 // check params
                 if( qosnegotiation == null ) throw new Exception("null qos negotiation component");

                 // get proposal history to date
                 history = (ProposalHistory) mstate.getObject( mstaticHistory );

                 // evaluate bids
                 if( runThread.queryStopFlag() ) throw new GEMSSNegotiatorShutdownException( "GEMSSNegotiator component terminating - work aborted","stop flag set" );
                 return qosnegotiation.evaluateProposals( history );
           }
           catch( GridException gridex ) {
                 // record GridException
                 runThread.setGridException( gridex );
                 throw gridex;
           }
           catch( Exception ex ) {
                 // log real error fully
                 mlogger.log( Level.SEVERE,"Error evaluating bids, failed to recover",ex );

                 // record a useful exception for the app to display
                 runThread.setGridException( new GEMSSNegotiatorException( "Error evaluating bids. Please check logs for details","getProposals failed : "+ex.getMessage() ) );
                 throw ex; // pass exception on through
           }
     }

     /**
      * Accept bids going from best to worst bid that still meets the requirements. Returns the proxy of the bid that
      * was accepted and contracts exchanged succefully, allowing job handling fucntions to be called.
      * @param qosnegotiation QoSNegotiation component to use to actually do the startAuction etc
      * @param runThread thread object that spanwed this processing. This is required to check for the stop flag being
      * @return proxy of the winning bidder, bound and ready for job handling
      */
     private GEMSSProxy acceptBestProposal( QoSNegotiation qosnegotiation, NegotiationThread runThread ) throws Exception
     {
           ProposalHistory history;

           // check outside try due to catch using the run thread
           if( runThread == null ) throw new Exception("null run thread");

           try {
                 // check params
                 if( qosnegotiation == null ) throw new Exception("null qos negotiation component");

                 // get proposal history to date
                 history = (ProposalHistory) mstate.getObject( mstaticHistory );

                 // evaluate bids
                 if( runThread.queryStopFlag() ) throw new GEMSSNegotiatorShutdownException( "GEMSSNegotiator component terminating - work aborted","stop flag set" );
                 return qosnegotiation.acceptBestProposal( history );
           }
           catch( GridException gridex ) {
                 // record GridException
                 runThread.setGridException( gridex );
                 throw gridex;
           }
           catch( Exception ex ) {
                 // log real error fully
                 mlogger.log( Level.SEVERE,"Error accepting best proposal, failed to recover",ex );

                 // record a useful exception for the app to display
                 runThread.setGridException( new GEMSSNegotiatorException( "Error accepting best proposal. Please check logs for details","getProposals failed : "+ex.getMessage() ) );
                 throw ex; // pass exception on through
           }
     }

	/**
	 * Write state and contents to a stream. The negotiation thread is not killed by this function,
	 * so should be terminated explicitly using a call to terminate().
	 * @param out Output stream where writes should go
	 */
	synchronized private void writeObject(java.io.ObjectOutputStream out) throws IOException
	{
		try
		{
			// write the negotiation state
			out.writeObject( mstate );

			// write proxy to stream (negotiator owns only ref to this proxy)
			out.writeObject( mproxyBound );

			// all done
			return;
		}
		catch ( Exception ex )
		{
			mlogger.log( Level.SEVERE,"Exception during writeObject",ex);
			throw new IOException("Failed to serialize negotiator object");
		}
	}

	/**
	 * read state and contents from a stream. Negotiation thread will be re-started if required, starting from
	 * the loaded state.
	 * @param in Input stream where to read from
	 */
	synchronized private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
	{
		Vector vectorResults;

		// init (no constructor)
		mlogger = Logger.getLogger("uk.ac.soton.itinnovation.gemss.proxies.GEMSSNegotiatorImp");

		try
		{
			// init stuff since constructors not called (nothing to do actually!)
			mnegotiationThread = null;
			mproxyBound = null;
			mstate = null;

			// read negotiation state
			mstate = (State) in.readObject();

			// read proxy (if it is available)
			mproxyBound = (GEMSSProxy) in.readObject();

			// if still negotiating, make a negotiation thread and start it
			// otherwise, if data not yet downloaded then start the job handling
			// thread
			vectorResults = null;
			if( mproxyBound != null ) {
				vectorResults = mproxyBound.getProxyFlags();
				if( vectorResults == null ) throw new Exception("Null proxy flags");
			}

			// start neg thread if we need to
			if( mproxyBound == null || !vectorResults.contains( GEMSSNegotiationProxy.mstaticJobPrepFlag ) ) {
				// make a thread and start it from this state
				mnegotiationThread = new NegotiationThread();
				mnegotiationThread.init( mstate,this );

				// start thread
				mnegotiationThread.start();
			}

			// all done
			return;
		}
		catch ( Exception ex )
		{
			mlogger.log( Level.SEVERE,"Exception during readObject",ex);
			throw new IOException("Failed to serialize negotiator object");
		}
	}

	/**
	 * In-line thread class to handle the running of a negotiation
	 */
	class NegotiationThread extends Thread
	{
		private State mstate = null;
		private GEMSSNegotiator mnegotiator = null;
		private boolean mbStopFlag = false;
		private GridException mgridException = null;

		/**
		 * constructor
		 */
		public NegotiationThread()
		{
			super();
			// all done
			return;
		}

		/**
		 * Init the thread, noting the state and neg instance calling it
		 * @param negotiationState state object assigned to this thread
		 * @param negotiator neg object that created this thread
		 */
		 public void init( State negotiationState, GEMSSNegotiator negotiator )
		{
			// setup member variables prior to the thread's run
			mstate = negotiationState;
			mnegotiator = negotiator;
		};

            /**
            * Flag that the thread should stop
            */
            public void flagStop()
            {
                  // set the stop flag
                  mbStopFlag = true;

                  // report
                  mlogger.log( Level.INFO,"NegotiationThread stop flag set");

                  // stop what it's currently doing so thread can end
                  interrupt();
            };

            /**
            * query the stop flag. Useful for parent code when executing code started
            * by the thread, since it can respond to termination (stop flag being set)
            */
            public boolean queryStopFlag()
            {
                  return mbStopFlag;
            };

            /**
            * get the grid exception, or null if no exception has been set.
            */
            public GridException getGridException()
            {
                  return mgridException;
            };

            /**
            * set the grid exception, useful to allow parent methods to set thread exceptions
            * when processing thread launched code
            */
            public void setGridException( GridException gridex )
            {
                  mgridException = gridex;
            };

		/**
		* Run's the thread that will process the negotiation from start to finish.
		* In phase I2 the negoitation will involve service discovery, and a very simple
		* heuristic to select the service. Later phases will conduct an acution here.
            * Will try to generate a useful GEMSSNegotiatorException if run fails.
		*/
		public void run()
		{
			Discovery discovery;
                  QoSNegotiation qosnegotiation;
                  eu.gemss.components.discovery.ServiceDescription service, selectedService;     // full package due to clashes with javax.wsdl
			ComponentManager compMan;
			Attribute[] attributeArray;
                  ProposalHistory history;
                  GEMSSProxyRequest requestProxy;
                  QoSNegotiationRequest requestQoS;
                  QoSRequirements requirements;
                  String strRequestDescFilename, strQoSConfigFilename;
                  String strJobConvID, strServiceName;
                  List listServices;
                  Vector vectorFlags, vectorProxies;
                  boolean bRetry, bOk;
                  int i;
                  long nDeadline, nDiff;
                  Date date2000GMT, dateProposalDeadline, dateAuctionDeadline;
                  GregorianCalendar calendar;

			try
			{
				// test flag
				mlogger.log( Level.INFO,"GEMSSNegotiator NegotiationThread start");

                        // init
                        listServices = null;

				// negotiation step
				vectorFlags = getNegotiatorFlags();
				if( !vectorFlags.contains( GEMSSNegotiationProxy.mstaticNegCompleteFlag ) &&
				    !vectorFlags.contains( GEMSSNegotiationProxy.mstaticJobPrepFlag ) ) {
					// negotiate qos with a set of services OR signal user to choose service if
                              // the neg config file has not been passed
					// phase I3:
					// 1) ask service discovery for a set of GEMSSProxy instances representing the potential services
                              // IF QoS config file null
                              //   1) generate a signal to ask for "single service selection"
					//   2) wait for the signal to be processed
                              //   3) bind to selected proxy
                              // ELSE
                              //   1) generate a signal to ask for "multiple service selection"
                              //   2) wait for the signal to be processed
                              //   3) start the auction (set AuctionStarted flag)
                              //   4) get QoS requirements
                              //   5) issue a call for proposals
                              //   6) wait for call deadline and then get all proposals
                              //   7) evaluate proposals (set BiddingFinished flag)
                              //   8) accept the best proposal (set AuctionEnded flag)
					//   9) bind this negotiator to the winning GEMSSProxy
					// 2) set the NegotiationComplete flag

                              mlogger.log( Level.INFO,"GEMSSNegotiator NegotiationThread service discovery step");

                              // get the component manager
                              compMan = GEMSS.getInstance();
                              if( compMan == null ) {
                                    mgridException = new GEMSSNegotiatorException("Component manager is null. Please check configuration files - see logs for more details.","null comp man");
                                    throw new Exception("null component manager");
                              }

                              // has auction started already? (if so no need for discovery)
                              if( !vectorFlags.contains( GEMSSNegotiator.mstaticAuctionStartedFlag ) ) {
                                    // get the service name
                                    strServiceName = (String ) mstate.getObject( mstaticServiceName );
                                    if( strServiceName == null ) {
                                          mgridException = new GEMSSNegotiatorException("Service name is null. Please check configuration file entry - see logs for more details.","null service name");
                                          throw new Exception("Service name state is null" );
                                    }

                                    // get discovery component
                                    discovery = (Discovery) compMan.getInstance("eu.gemss.components.discovery.Discovery");
                                    if( discovery == null ) {
                                          mgridException = new GEMSSNegotiatorException("Discovery component is null. Please check configuration files - see logs for more details.","null discovery");
                                          throw new Exception("null discovery component");
                                    }

                                    // formulate a query for the discovery component
                                    // for now we just want any service with the right service name
                                    attributeArray = new Attribute[1];
                                    attributeArray[0] = new Attribute();
                                    attributeArray[0].setValue( strServiceName );
                                    attributeArray[0].setName( mstaticTagServiceName );

                                    // get a set of services
                                    try {
                                          listServices = discovery.discoverServices( attributeArray );
                                    }
                                    catch( DiscoveryGridException dex ) {
                                          mlogger.log( Level.WARNING,"Discovery failed",dex );
                                          mgridException = dex;
                                          throw new Exception("Discovery component failed");
                                    }
                                    catch( RegistryGridException rex ) {
                                          mlogger.log( Level.WARNING,"Registry failed",rex );
                                          mgridException = rex;
                                          throw new Exception("Registry lookup failed");
                                    }

                                    // are there any services?
                                    if( listServices.size() < 1 ) {
                                          mgridException = new GEMSSNegotiatorException("No services discovered with the required service attributes. Please check with service provider about their registry details - see logs for more details.","service list size < 1");
                                          mstate.addFlag( GEMSSNegotiator.mstaticNegotiatorHaltedFlag );
                                          return;
                                    }

                                    // print out the set of services to the probe
                                    Iterator iter = listServices.iterator();
                                    while ( iter.hasNext()) {
                                          service = (eu.gemss.components.discovery.ServiceDescription) iter.next();
                                          mlogger.log( Level.INFO,"Probe: discovered-service\t"+service.getWsdlEndpoint() );
                                    }

                                    // abort check
                                    if( mbStopFlag ) {
                                          mgridException = new GEMSSNegotiatorShutdownException("GEMSS Negotiator component terminating.","stop flag set");
                                          mstate.addFlag( GEMSSNegotiator.mstaticNegotiatorHaltedFlag );
                                          return;
                                    }

                                    // did the request specify the QoS negotiation config file? If not then manual service selection will be
                                    // employed. If config file is specified them the QoSNegotiation component will be used to run an auction
                                    // and select the best service to use.
                                    strQoSConfigFilename = (String) mstate.getObject( mstaticQoSConfigFilename );
                                    if( strQoSConfigFilename.equals("") ) {
                                          // manual service selection - no need to get qos requirements OR hold an auction etc
                                          if( mbStopFlag ) {
                                                mgridException = new GEMSSNegotiatorShutdownException("GEMSS Negotiator component terminating.","stop flag set");
                                                mstate.addFlag( GEMSSNegotiator.mstaticNegotiatorHaltedFlag );
                                                return;
                                          }
                                          mproxyBound = manualServiceSelection( listServices );
                                          if( mproxyBound == null ) {
                                                mgridException = new GEMSSNegotiatorException("Failed to bind to a manually selected service end point. User probably aborted selection.","null proxy returned by manual selection");
                                                mstate.addFlag( GEMSSNegotiator.mstaticNegotiatorHaltedFlag );
                                                return;
                                          }

                                          // proxy bound
                                          mlogger.log( Level.INFO,"Probe: bind-proxy\t"+mproxyBound.queryNegConversationID() );

                                          // set the neg complete flag since no neg is needed at all
                                          mstate.addFlag( GEMSSProxy.mstaticNegCompleteFlag );
                                    }
                              } // end of start auction check

                              // check to see if manual selection occured (i.e. negotiation is over already)
                              vectorFlags = getNegotiatorFlags();
                              if( !vectorFlags.contains( GEMSSProxy.mstaticNegCompleteFlag ) ) {
                                    // get a QoS negotiation component instance
                                    strQoSConfigFilename = (String) mstate.getObject( mstaticQoSConfigFilename );
                                    if( strQoSConfigFilename.equals("") ) {
                                          mgridException = new GEMSSNegotiatorException("Null QoS configuration filename. Please check config files.","null config file");
                                          mstate.addFlag( GEMSSNegotiator.mstaticNegotiatorHaltedFlag );
                                          return;
                                    }

                                    // get QoSNegotiation component
                                    requestQoS = new QoSNegotiationRequest( strQoSConfigFilename,"eu.gemss.components.negotiation.QoSNegotiation",null,null );
                                    qosnegotiation = (QoSNegotiation) compMan.getInstance( requestQoS );
                                    if( qosnegotiation == null ) {
                                          mgridException = new GEMSSNegotiatorException("Null QoSNegotiation component. Please check config files.","null qos negotiation component");
                                          throw new Exception("null qos negotiation component");
                                    }

                                    // start auction
                                    vectorFlags = getNegotiatorFlags();
                                    if( !vectorFlags.contains( GEMSSNegotiator.mstaticAuctionStartedFlag ) ) {
                                          // start auction with this list of services (should have run discovery previously)
                                          history = startAuction( listServices,qosnegotiation,this );
                                          mstate.addObject( mstaticHistory, history );
                                          mstate.addFlag( GEMSSNegotiator.mstaticAuctionStartedFlag );
                                    }

                                    // abort check
                                    if( mbStopFlag ) {
                                          mgridException = new GEMSSNegotiatorShutdownException("GEMSS Negotiator component terminating.","stop flag set");
                                          mstate.addFlag( GEMSSNegotiator.mstaticNegotiatorHaltedFlag );
                                          return;
                                    }

                                    // get QoS requirements
                                    vectorFlags = getNegotiatorFlags();
                                    if( !vectorFlags.contains( GEMSSNegotiator.mstaticQoSPropertiesObtainedFlag ) ) {
                                          requirements = getQoSRequirements( qosnegotiation, this );
                                          if( requirements == null ) {
                                                // no signal handlers responded for get reqs (user might have aborted)
                                                // default bahaviour
                                                mgridException = new GEMSSNegotiatorException("No user requirements returned. The user probably aborted requirement request.","null requirements");
                                                mstate.addFlag( GEMSSNegotiator.mstaticNegotiatorHaltedFlag );
                                                return;
                                          }
                                          mstate.addObject( mstaticQoSRequirements,requirements );
                                          mstate.addFlag( GEMSSNegotiator.mstaticQoSPropertiesObtainedFlag );
                                    }

                                    // abort check
                                    if( mbStopFlag ) {
                                          mgridException = new GEMSSNegotiatorShutdownException("GEMSS Negotiator component terminating.","stop flag set");
                                          mstate.addFlag( GEMSSNegotiator.mstaticNegotiatorHaltedFlag );
                                          return;
                                    }

                                    // get reqs from state (in case re-starting without calling getQoSRequirements)
                                    // get history from state (in case re-starting without calling startAuction)
                                    requirements = (QoSRequirements) mstate.getObject( mstaticQoSRequirements );
                                    history = (ProposalHistory) mstate.getObject( mstaticHistory );

                                    // get overall auction deadline
                                    calendar = new GregorianCalendar( TimeZone.getTimeZone("GMT") );
                                    calendar.set( 2000,0,1,0,0,0 );
                                    date2000GMT = calendar.getTime();
                                    if( history.getAuctionDeadline() != -1 ) dateAuctionDeadline = new Date( date2000GMT.getTime() + history.getAuctionDeadline() );
                                    else dateAuctionDeadline = null;

                                    // loop until auction ends
                                    vectorFlags = getNegotiatorFlags();
                                    while( !vectorFlags.contains( GEMSSNegotiator.mstaticBiddingFinishedFlag ) ) {
                                          // issue new CFP (will override any old CFP)
                                          history = sendCFPs( qosnegotiation,this );
                                          mstate.addObject( mstaticHistory, history ); // update state

                                          // abort check
                                          if( mbStopFlag ) {
                                                mgridException = new GEMSSNegotiatorShutdownException("GEMSS Negotiator component terminating.","stop flag set");
                                                mstate.addFlag( GEMSSNegotiator.mstaticNegotiatorHaltedFlag );
                                                return;
                                          }

                                          // get proposal deadline date and auction deadlien date
                                          nDeadline = history.getProposalDeadline();
                                          if( nDeadline != -1 ) {
                                                dateProposalDeadline = new Date( date2000GMT.getTime() + nDeadline );
                                                mlogger.log( Level.INFO,"Probe: proposal-deadline\t"+dateProposalDeadline.toString() );
                                          }
                                          else {
                                                mgridException = new GEMSSNegotiatorException("Call for proposal deadline not set. This is an infrastructure error - please check logs for details.","cfp deadline = -1");
                                                throw new Exception("deadline not set after a cfp was issued");
                                          }

                                          // wait until bids are due
                                          bOk = false;
                                          while( !bOk ) {
                                                // check overall auction deadline milliseconds from now
                                                if( dateAuctionDeadline != null ) {
                                                      nDiff = dateAuctionDeadline.getTime() - System.currentTimeMillis();
                                                      if( nDiff <= 0 ) {
                                                            mgridException = new AuctionTimeoutException("Overall auction deadline of "+dateAuctionDeadline+" exceeded","auction timeout "+dateAuctionDeadline);
                                                            mstate.addFlag( GEMSSNegotiator.mstaticNegotiatorHaltedFlag );
                                                            return;
                                                      }
                                                }

                                                // get proposal deadline milliseconds from now
                                                nDiff = dateProposalDeadline.getTime() - System.currentTimeMillis();
                                                if( nDiff > 0 ) {
                                                      // sleep for 1 second and check again
                                                      try { sleep( 1000 ); } catch( InterruptedException ie ) { ; }

                                                      // abort check
                                                      if( mbStopFlag ) {
                                                            mgridException = new GEMSSNegotiatorShutdownException("GEMSS Negotiator component terminating.","stop flag set");
                                                            mstate.addFlag( GEMSSNegotiator.mstaticNegotiatorHaltedFlag );
                                                            return;
                                                      }
                                                }
                                                else bOk = true; // finish waiting
                                          }

                                          // get proposals
                                          history = getProposals( qosnegotiation,this );
                                          mstate.addObject( mstaticHistory, history ); // update state

                                          // evaluate proposals to see if we should stop
                                          // checks bid values, max rounds and overall auction deadline
                                          if( evaluateProposals( qosnegotiation,this ) ) {
                                                // stop the bidding and try to exchange contracts
                                                mstate.addFlag( GEMSSNegotiator.mstaticBiddingFinishedFlag );
                                          }

                                          // get new flag list
                                          vectorFlags = getNegotiatorFlags();
                                    } // loop until mstaticBiddingFinishedFlag flag set

                                    // accept best bid and exchange contracts, and keep trying until someone accepts
                                    // or we run out of bids to accept
                                    vectorFlags = getNegotiatorFlags();
                                    if( !vectorFlags.contains( GEMSSNegotiator.mstaticAuctionEndedFlag ) ) {
                                          // accept and finish auction or halt negotiation here if no contracts exchanged
                                          mproxyBound = acceptBestProposal( qosnegotiation,this );
                                          if( mproxyBound == null ) {
                                                mgridException = new GEMSSNegotiatorException("All proposals from service providers were unacceptable. Negotiation finished.","all bids unacceptable");
                                                mstate.addFlag( GEMSSNegotiator.mstaticNegotiatorHaltedFlag );
                                                return;
                                          }
                                          mstate.addFlag( GEMSSNegotiator.mstaticAuctionEndedFlag );

                                          // proxy bound
                                          mlogger.log( Level.INFO,"Probe: bind-proxy\t"+mproxyBound.queryNegConversationID() );
                                    }

                                    // set neg complete flag
                                    mstate.addFlag( GEMSSNegotiationProxy.mstaticNegCompleteFlag );
                              } // manual selection neg complete check

                              // log info
					mlogger.log( Level.INFO,"GEMSSNegotiator finished negotiation");
				}

				// job preperation step
				vectorFlags = getNegotiatorFlags();
				if( vectorFlags.contains( GEMSSNegotiationProxy.mstaticNegCompleteFlag ) &&
				    !vectorFlags.contains( GEMSSNegotiationProxy.mstaticJobPrepFlag ) ) {
					// prepare the job and get a job conversation ID
					// 1) call getJobCId
					// this will set the proxies JobPreperationComplete and record
					// the job conversation ID so there is nothing more to do after
					// this.

					// call getCId and retry on any network errors
					// abort on any other exception type
					bRetry = true;
					strJobConvID = null;
					while( bRetry ) {
						bRetry = false;
						try {
                                          if( mbStopFlag ) {
                                                mgridException = new GEMSSNegotiatorShutdownException("GEMSS Negotiator component terminating.","stop flag set");
                                                mstate.addFlag( GEMSSNegotiator.mstaticNegotiatorHaltedFlag );
                                                return;
                                          }
							strJobConvID = mproxyBound.getJobCID();
						}
						catch( GridException gridex ) {
							// network problem or something fatal?
                                          // The connection details might be plain wrong, in which case the app will need to
                                          // flag stop at some point.
							if( gridex instanceof NetworkException ) {
								mlogger.log( Level.INFO,"GEMSSNegotiator network connection exception doing getJobCID - retry enabled",gridex );
								bRetry = true;
							}
							else {
								mgridException = gridex;
								throw new Exception( "GridException doing getJobCId" );
							}
						} // end try/catch
					} // retry loop
					mlogger.log( Level.INFO,"GEMSSNegotiator job ID = "+strJobConvID );
				}

				// all done - client can now use ths negotiator as a job handling proxy
				mlogger.log( Level.INFO,"GEMSSNegotiator NegotiationThread finished");
				return;
			}
			catch ( Exception ex )
			{
                        // All Grid exceptions will be also caught, and should have already called the setGridException method
				// flag that the negotiator has halted
				mlogger.log( Level.SEVERE,"Exception during NegotiateThread.run",ex);
				mstate.addFlag( GEMSSNegotiator.mstaticNegotiatorHaltedFlag );
			}
		} // end run
	} // end of NegotiationThread
} // end of GEMSSNegotiatorImp
