// 
// The Waveset SPML General License 
// 
// Version 0.1, April 2003
// Copyright (C) 2003 Waveset Technologies, Inc..
// 6034 West Courtyard Drive, Suite 210, Austin, Texas 78730
// All rights reserved.
// 
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
// 
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions, and the disclaimers in Sections 6
//    and 7 below.
// 
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the disclaimers in Sections 6
//    and 7 below in the documentation and/or other materials provided
//    with the distribution.
// 
// 3. The end-user documentation included with the redistribution, if
//    any, must include the following acknowledgment:
// 
//    "This product includes software developed by 
//     Waveset Technologies, Inc. (www.waveset.com)."
// 
//    Alternately, this acknowledgment may appear in the software itself, if
//    and wherever such third-party acknowledgments normally appear.
//  
// 4. The names "Waveset" and "Waveset Technologies, Inc." must not be
//    used to endorse or promote products derived from this software
//    without the prior written permission of Waveset. For written
//    permission, please contact www.waveset.com.
//  
// 5. Products derived from this software may not be called "Waveset",
//    nor may "Waveset" appear in their name, without the prior written
//    permission of Waveset.
//  
// 6. NO WARRANTY 
// 
//    THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED
//    OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
//    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
//    DISCLAIMED.
// 
// 7.  LIMITATION OF LIABILITY
// 
//    IN NO EVENT SHALL THE WAVESET OR ITS LICENSORS BE LIABLE FOR ANY
//    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
//    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
//    GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
//    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
//    IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
//    OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
//    ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 
// End of Terms and Conditions
// ====================================================================
//
// Component Name: org.openspml.client.SpmlClient
//
// Author(s): Jeff Larson
//
// Description:
//
// A class providing a convenient interface for sending and receiving
// SPML messages.  Using the SPML object model.
//
// Currently uses SOAPClient which in turn requres the Apache soap.jar
// version 2.2.
//

package org.openspml.client;

import java.util.*;
import java.net.URL;
import org.w3c.dom.*;

import org.openspml.util.*;
import org.openspml.message.*;

/**
 * A class providing a mechaism to send and recieve SPML requests
 * using the SpmlRequest and SpmlResponse classes.
 * <p>
 * Once the request is formatted as XML, it uses the <code>SOAPClient</code> 
 * class to handle the actual communication.  When the XML response is
 * received, it is parsed into a SpmlResponse and returned.
 */
public class SpmlClient {

    //////////////////////////////////////////////////////////////////////
    //
    // Fields
    //
    //////////////////////////////////////////////////////////////////////

    /**
     * Use one of these to manage the basic SOAP communications.	
     */
    SOAPClient _client;

    /**
     * The URL we post to.
     */
    URL _url;

    /**
     * The "SOAP action" name.  If you are using the Apache SOAP router	
     * this is used to dispatch the request.  If you are using the
     * OpenSPML router, it is not required.
     */
    String _action;

    /**
     * Buffer we use to format the XML messages.
     */
    SpmlBuffer _buffer;

    /**
     * Trace flag.      
     */
    boolean _trace;

    boolean _autoAction;

    //////////////////////////////////////////////////////////////////////
    //
    // Constructors
    //
    //////////////////////////////////////////////////////////////////////

    public SpmlClient() {
	_client = new SOAPClient();
	_buffer = new SpmlBuffer();
    }

    /**
     * Set the URL of a web service responding to SOAP requests.
     */
    public void setUrl(String s) throws java.net.MalformedURLException {
	setUrl(new URL(s));
    }

    /**
     * Set the URL of a web service responding to SOAP requests.
     */
    public void setUrl(URL url) {
	_url = url;
    }

    /**
     * Set the "SOAP action name" which may be requried by
     * the SOAP router in the application server.
     */
    public void setAction(String action) {
	_action = action;
    }


    /**
     * Set an optional SOAP Header.  
     * The string is expected to contain well formed XML.
     */
    public void setHeader(String s) {
	_client.setHeader(s);
    }

    /**
     * Set an optional list of attributes for the SOAP Body.
     * The string is expected to contain a fragment of well formed
     * XML attribute definitions, "wsu:Id='myBody'"
     * It is assumed for now that any namespace references in the 
     * attribute names do not need to be formally declared in
     * the soap:Envelope.
     */
    public void setBodyAttributes(String s) {	
	_client.setBodyAttributes(s);
    }

    public void setTrace(boolean b) {
        _trace = b;
    }

    /**
     * Install a SOAP message monitor. 
     */
    public void setMonitor(SOAPMonitor m) {
        _client.setMonitor(m);
    }

    //////////////////////////////////////////////////////////////////////
    //
    // Requests
    //
    //////////////////////////////////////////////////////////////////////

    /**
     * Send any request, return the corresponding response.
     */
    public SpmlResponse request(SpmlRequest req) 
        throws SpmlException {

        // formerly had logic here to call wrapResponse
        // but the spec has since been modified to allow all
        // requests as top-level requests

        return send(req);
    }

    /**
     * Inner request/response processing method.        
     */
    private SpmlResponse send(SpmlRequest req)
	throws SpmlException {

	SpmlResponse response = null;
	try {
	    _buffer.setLength(0);
	    req.toXml(_buffer);
            String xml = _buffer.toString();

            if (_trace) {
                println("SpmlClient: sending to " + _url.toString());
                println(xml);
            }

	    // unless action is explicitly specified, derive one
	    // using the OpenNetworks convention
	    String action = _action;
	    if (action == null && _autoAction)
		action = Constants.ACTION_PREFIX + "/" +
		    req.getElementName();

	    _client.send(_url, action, xml);

	    xml = _client.receive();

            if (_trace) {
                println("SpmlClient: received");
                println(xml);
            }

            response = SpmlResponse.parseResponse(xml);
	    if (response == null)
		throw new SpmlException("Unable to parse SpmlResponse:" + 
					xml);
	}
	catch (org.apache.soap.SOAPException e) {
	    throw new SpmlException(e);
	}
	catch (javax.mail.MessagingException e) {
	    throw new SpmlException(e);
	}
	catch (java.io.IOException e) {
	    throw new SpmlException(e);
	}
	
	return response;
    }

    public static void println(String msg) {
        System.out.println(msg);
    }

    /**
     * Wrap a single request in a batch request.
     * <p>
     * This is a convenience method to process single requests which
     * is much more common that batches of multiple requests.
     * Operational attributes in the inner request are promoted
     * to operatinal attributes of the batch request.
     * <p>
     * UPDATE: The spec has since been modified to allow any requst
     * as top level requests, so there is no need to wrap anymore.
     */
    private SpmlResponse wrapRequest(SpmlRequest req) 
        throws SpmlException {

        SpmlResponse response = null;

	BatchRequest batch = new BatchRequest();
	batch.addRequest(req);
        batch.setOperationalAttributes(req.getOperationalAttributes());
        req.setOperationalAttributes(null);

	BatchResponse br = batchRequest(batch);

	List responses = br.getResponses();
	if (responses != null && responses.size() > 0)
	    response = (SpmlResponse)responses.get(0);
        else {
            // have to have something to contain errors
            response = req.createResponse();
	}

        response.setOperationalAttributes(br.getOperationalAttributes());
        response.setErrorMessage(br.getErrorMessage());

        return response;
    }

    /**
     * Send a BatchRequst, return a BatchResponse.
     */
    public BatchResponse batchRequest(BatchRequest req)
	throws SpmlException {

	BatchResponse response = null;
	SpmlResponse sr = request(req);
        if (sr instanceof BatchResponse)
            response = (BatchResponse)sr;
        else
            throw new SpmlException("Invalid response");
        
        return response;
    }

    /**
     * Submit a single SearchRequest.
     */
    public SearchResponse searchRequest(SearchRequest req)
	throws SpmlException {

        SearchResponse response = null;
        SpmlResponse sr = request(req);
        if (sr instanceof SearchResponse)
            response = (SearchResponse)sr;
        else 
            throw new SpmlException("Invalid SearchResponse");
	
	return response;
    }

    /**
     * Summit a single ExtendedRequest.
     */
    public ExtendedResponse extendedRequest(ExtendedRequest req)
	throws SpmlException {

	ExtendedResponse response = null;
        SpmlResponse sr = wrapRequest(req);
        if (sr instanceof ExtendedResponse)
            response = (ExtendedResponse)sr;
        else
            throw new SpmlException("Invalid ExtendedResponse");

	return response;
    }

    //////////////////////////////////////////////////////////////////////
    //
    // Responses
    //
    //////////////////////////////////////////////////////////////////////

    /**
     * Convert error messages in a response into an SpmlException.
     */
    public void throwErrors(SpmlResponse res) throws SpmlException {

        // now moved in here
        if (res != null)
            res.throwErrors();
    }

}
