/*  Copyright (c) 2002, Oracle Corporation. All Rights Reserved. */
package oracle.portal.provider.v2.webservice;

import java.io.File;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Vector;
import java.util.Enumeration;
import java.net.URL;
import java.net.MalformedURLException;
import org.xml.sax.SAXException;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Element;
import org.w3c.dom.Document;
import org.apache.soap.SOAPException;
import org.apache.soap.Body;
import org.apache.soap.Envelope;
import org.apache.soap.messaging.Message;
import org.apache.soap.transport.SOAPTransport;
import oracle.webdb.utils.SimpleStringBuffer;
import oracle.soap.transport.http.OracleSOAPHTTPConnection;
import oracle.portal.utils.xml.v2.NodeHandlerException;
import oracle.portal.provider.v2.PortletException;
import oracle.portal.provider.v2.ProviderSession;

/**
 * <code>DocWebServiceRenderer</code> is a specialized {@link
 * WebServiceRenderer} for invoking 'doc style' web services. A doc style web
 * service communicates through the interchange of XML documents of a
 * service-specific format, and can be identified from a <code>&lt;soap:binding
 * style="doc"></code> declaration in a WSDL (Web Services Description
 * Language) interface description.<p>
 *
 * The structure of the XML request to the web service and parameter bindings
 * into this structure should be defined by setting the <code>literal</code>
 * property to an appropriate {@link LiteralXML} object.
 */
public class DocWebServiceRenderer extends WebServiceRenderer
{
    // The key under which we store a user-specific connection instance in the
    // session (if available)
    private static final String CONNECTION_SESSION_ATTRIBUTE = "DWSR_CONNECTION";

    private URL mEndpointURL;
    private String mSoapAction = "";

    /**
     * Performs the extra steps required to initialize and validate a
     * <code>DocWebServiceRenderer</code> once its properties have been set
     * with the appropriate accessor methods.
     * @exception NodeHandlerException if not all properties have been set
     * correctly
     */
    public void postInitialize()
        throws NodeHandlerException
    {
        super.postInitialize();
        if (mEndpointURL == null)
        {
            throw new NodeHandlerException("endpointURL not set");
        }
    }

    /**
     * Defines the URL through which the web service should be contacted. This
     * can be determined from the <code>&lt;soap:address></code> element of the
     * service's WSDL document.
     */
    public void setEndpointURL(String endpointURL)
        throws MalformedURLException
    {
        // Make sure we are not in read-only 'locked down' state
        checkLocked("setEndpointURL");

        mEndpointURL = new URL(endpointURL);
    }

    /**
     * Defines the value to be set for the <code>SOAPAction</code> HTTP header,
     * if required. This can be determined from a web service's WSDL document
     * by the <code>soapAction</code> attribute of the
     * <code>&lt;soap:operation></code> element corresponding to the particular
     * operation you want to invoke.
     */
    public void setSoapAction(String soapAction)
    {
        // Make sure we are not in read-only 'locked down' state
        checkLocked("setSoapAction");

        mSoapAction = soapAction;
    }

    /**
     * Uses the given <code>WebServiceRenderRequest</code> to instantiate the
     * XML request structure described by the <code>literal</code> property (if
     * specified), invokes the web service, and adds its XML response to the
     * response XML document.
     * @param wrr an object exposing the relevant properties of the current
     * render request
     * @param docEl the document element of the XML document to which an XML
     * representation of the response should be added
     * @exception PortletException if an error occurs.
     */
    public void invokeService (WebServiceRenderRequest wrr, Element docEl)
        throws PortletException
    {
        // Get the document to use as a factory for elements to be added to the
        // response
        Document responseDoc = docEl.getOwnerDocument();
        String repositoryPath = wrr.getRepositoryPath();

        Envelope responseEnv;
        try
        {
            // Get hold of our literal XML parameter list
            LiteralXML literalXML = getLiteral();
            NodeList nl = literalXML == null ? null : literalXML.generateElements(wrr);

            // Get a vector of argument elements - the children of the given
            // request element
            Vector args = new Vector();

            // Load up the arguments
            if (nl != null)
            {
                int length = nl.getLength();
                for (int i = 0; i<length; i++)
                {
                    args.addElement(nl.item(i));
                }
            }

            // Construct the SOAP Envelope
            Body requestBody = new Body();
            requestBody.setBodyEntries(args);
            Envelope requestEnv = new Envelope();
            requestEnv.setBody(requestBody);

            // Construct the message and send it over a connection.
            Message msg = new Message();

            // If we don't have a session, we simply create a new connection
            // instance every time
            ProviderSession session = wrr.getSession();
            if (session == null)
            {
                msg.setSOAPTransport(new OracleSOAPHTTPConnection());
                msg.send(mEndpointURL, mSoapAction, requestEnv);
                responseEnv = msg.receiveEnvelope();
            }
            // If we have a session, we try to re-use a connection for this
            // user stored in the session, thus allowing limited connection
            // pooling and 'stateful' web services. Unfortunately, this
            // requires synchronization.
            else 
            {
                // Find or create the re-used stateful connection for this user
                // in the session
                SOAPTransport st;
                String attribName = new SimpleStringBuffer(100)
                    .append(wrr.getParameterPrefix())
                    .append(CONNECTION_SESSION_ATTRIBUTE).toString();

                synchronized (session)
                {
                    st = (SOAPTransport)session.getAttribute(attribName);
                    if (st == null)
                    {
                        st = new OracleSOAPHTTPConnection();
                        session.setAttribute(attribName, st);
                    }
                }

                msg.setSOAPTransport(st);

                // The connection is not thread safe, so we can only send one
                // request over it at a time
                synchronized (st)
                {
                    msg.send(mEndpointURL, mSoapAction, requestEnv);
                    responseEnv = msg.receiveEnvelope();
                }
            }
        }
        catch (RuntimeException re)
        {
            throw re;
        }
        catch (Exception e)
        {
            throw new PortletException(e);
        }

        Element responseEl = responseDoc.createElement("response");
        docEl.appendChild(responseEl);

        Body responseBody = responseEnv.getBody();
        Enumeration responseEls = responseBody.getBodyEntries().elements();

        // Add the result elements as children of the response element
        while(responseEls.hasMoreElements())
        {
            Node result = responseDoc.importNode((Node)responseEls.nextElement(), true);
            responseEl.appendChild(result);
        }            
    }
}
