/////////////////////////////////////////////////////////////////////////
//
// © 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:             Darren Marvin
//      Created date:           2003/09/19
//      Created for project:    GEMSS
//
/////////////////////////////////////////////////////////////////////////
//
//      Dependencies: None
//
/////////////////////////////////////////////////////////////////////////
//
//      Last commit info:       $Author: $
//                              $Date: $
//                              $Revision: $
//
/////////////////////////////////////////////////////////////////////////

package uk.ac.soton.itinnovation.gemss.transportmessaging.messaging;

import eu.gemss.components.transport.*;
import eu.gemss.components.transport.payload.*;
import eu.gemss.components.transport.servicedescription.*;
import eu.gemss.components.transport.servicedescription.interfacetypes.*;
import java.io.*;
import java.util.*;
import java.util.logging.*;
import eu.gemss.signals.SignalHandler;
import uk.ac.soton.itinnovation.gemss.transportmessaging.*;
import uk.ac.soton.itinnovation.gemss.transportmessaging.configuration.*;
import uk.ac.soton.itinnovation.gemss.transportmessaging.connection.*;
import uk.ac.soton.itinnovation.gemss.transportmessaging.messaging.codec.*;
import uk.ac.soton.itinnovation.gemss.transportmessaging.messaging.context.*;
import org.apache.axis.utils.XMLUtils;
import javax.xml.namespace.*;

/**
 * The Messenger class acts a manager for a message based invocation, it orchestrates
 * the message creation and sending of the message to a service.
 */
public class Messenger {

    private static Logger mLogger = Logger.getLogger("uk.ac.soton.itinnovation.gemss.transportmessaging.messaging.Messenger");
    private TransportMessagingConfiguration mConfiguration;
    private SignalHandler mSignalHandler;

    public Messenger(TransportMessagingConfiguration config,SignalHandler handler) {
        mConfiguration = config;
        mSignalHandler = handler;
    }

    public InvocationOutput invokeService(ServiceDescription serviceDesc, InvocationInput input, Map fileHandleMap,boolean usePolicyFile) throws MessagingException, ServiceException, ConnectionException {
	    Payload inputPayload, payload;
	    CoDecProviderRegistry registry;
	    ServiceInterface intf;
	    InterfaceTypeDescriptor interfaceType;
	    MessageCoDecProvider codecProvider;
	    MessageCoDec messageCodec;
	    MessageContext msgContext;
	    Message message;
	    ServiceEndpoint endpoint;
	    ConnectionFactory connectionFactory;
	    Connection connection = null;
	    ConnectionResponse response;

	    try{

              //create a messagecontext
              msgContext = new MessageContext(mConfiguration);
              //initialise connection factory if it has not already been done
            ConnectionFactory.getInstance(mConfiguration);
            //have to retrieve the endpoint URI from the endpoint description but it may not be set
            //so we use the connection factory to verify what it is and populate if necessary
            endpoint = serviceDesc.getServiceEndpoint();
            connectionFactory = ConnectionFactory.getInstance(mConfiguration);
            connection = connectionFactory.createConnection(endpoint,msgContext.getSecurityContext());

            inputPayload = input.getPayload();

            //get the enforcer to create the message? No this thing gets the MessengeGenerator
            //and leaves the MessageEnforcer to enforce message encoding policy
            //create a service object
            //service = new Service(serviceDesc,inputPayload);

            //ask the provider registry for a suitable message codec
            registry = CoDecProviderRegistry.getInstance(mConfiguration);
            intf = serviceDesc.getServiceInterface();
            interfaceType = intf.getTypeDescriptor();
            codecProvider = registry.getProvider(interfaceType);
            messageCodec = codecProvider.getMessageCoDec(interfaceType);

            //create a message using the codec

            message = messageCodec.encodeMessage(intf,inputPayload,msgContext);

            //don't need input payload anymore so release its contents
            DataItem[] inputDataItems = inputPayload.getDataItems();
            for(int i=0;i<inputDataItems.length;i++) {
                inputDataItems[i].releaseData();
            }

            //enforce the encoding policies

            MessageEnforcer msgEnforcer = new MessageEnforcer(mConfiguration,mSignalHandler,usePolicyFile);
            Message securedMsg = msgEnforcer.enforceOutgoingMessage(serviceDesc,message);

            //don't need original unsecured message
            message.release();
            message = null;

            //run garbage collector so that it releases some the original messages
            //can do this now since the server will be munging the input data and
            //jvm will spawn a thread that will clean up while the lenghty download
            //goes on below.
            System.gc();

            //send the message

            response = connection.send(securedMsg);

            //don't need original secured message
            securedMsg.release();
            securedMsg = null;
            //run garbage collector so that it releases the secured message
            //can do this now since the server will be munging the input data and
            //jvm will spawn a thread that will clean up while the lenghty download
            //goes on below.
            System.gc();

            InputStream responseInStream = response.getResponseInputStream();

            msgContext.setConnectionProperties(response.getConnectionProperties());

            SOAPMessage responseMsg = new SOAPMessage(responseInStream,msgContext);
            //by now should know if 404 thrown back
            //check to see if the input stream is either not SOAP or is a fault


            //at this point the response should be parsable as XML
              //try to get the SOAP message
            org.w3c.dom.Document basicDoc = null;
            try{
              basicDoc = responseMsg.getDOMPart();
            }
            catch(javax.xml.parsers.ParserConfigurationException ex) {
              mLogger.log(Level.SEVERE,"Unable to process the response message, this is likely a bug - please send a bug report including all logs",ex);
              throw new MessagingException("Unable to process the response message, this is likely a bug - please send a bug report including all logs");
            }
            catch(org.xml.sax.SAXException ex) {
              mLogger.log(Level.SEVERE,"Unable to process the response message, it is likely that the service is returning HTML error page and not a message",ex);
              throw new MessagingException("Unable to process the response message");
            }
            catch(javax.xml.soap.SOAPException ex) {
              mLogger.log(Level.SEVERE,"Unable to process the response message, this is likely a bug - please send a bug report including all logs",ex);
              throw new MessagingException("Unable to process the response message, this is likely a bug - please send a bug report including all logs");
            }
            catch(java.io.IOException ex) {
              mLogger.log(Level.SEVERE,"Unable to process the response message, this is likely a bug - please send a bug report including all logs",ex);
              throw new MessagingException("Unable to process the response message, this is likely a bug - please send a bug report including all logs");
            }
            //have a representation of the message now - lets see if the enforcement works, if it does not
            //then you can log out the message alone
            payload = null;
            try{
              Message enforcedResponseMsg = msgEnforcer.enforceIncomingMessage(serviceDesc,responseMsg);

              //create a payload
              payload = messageCodec.decodeMessage(intf,enforcedResponseMsg,fileHandleMap,msgContext.getConnectionProperties());
            }
            catch(MessagingException ex) {
              try{
                //failed to process so dump out the untrusted soap message
                mLogger.log(Level.SEVERE,"Service response has not been verified but is provided here for information only, you must decide if you trust the content:\n\n" + XMLUtils.DocumentToString(basicDoc));
                //see if it is possible to create a ServiceException
                throw new ServiceException(XMLUtils.findNode(basicDoc,new QName("faultstring")).getNodeValue());
              }
              catch(Exception e) {
                mLogger.log(Level.WARNING,"Could not create service exception",e);
                //best efforts anyway
              }
              throw ex;
            }
              //release the connection
            connection.releaseConnection();
            //should be able to delete the the temporary files for the attachments
            responseMsg.deleteAttachments();
            //dont need response message
            responseMsg.release();
            responseMsg = null;
            return new InvocationOutputImp(payload,true,"Service invocation completed successfully");
        }
        finally {
            //release the connection
            if(connection!=null)
                connection.releaseConnection();
        }

    }
}