/*=============================================================================*
* Copyright 2006 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*=============================================================================*/
package org.apache.muse.ws.addressing.soap;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.apache.muse.util.messages.Messages;
import org.apache.muse.util.messages.MessagesFactory;
import org.apache.muse.util.xml.XmlUtils;
import org.apache.muse.ws.addressing.EndpointReference;
import org.apache.muse.ws.addressing.MessageHeaders;
import org.apache.muse.ws.addressing.soap.SoapClient;
/**
*
* SimpleSoapClient is and implementation of {@linkplain SoapClient SoapClient}
* that relies on the java.net.HttpURLConnection API to send and receive
* SOAP messages. It provides complete WS-Addressing (WS-A) support, so that
* all requests are routed with WS-A EPRs and Actions. Messages are sent with
* valid reply and fault EPRs so that responses can be redirected if desired.
*
* @author Dan Jemiolo (danj)
*
*/
public class SimpleSoapClient implements SoapClient
{
//
// Used to lookup all exception messages
//
private static Messages _MESSAGES =
MessagesFactory.get(SimpleSoapClient.class);
private static final Element[] _EMPTY_BODY = new Element[0];
//
// Set to 0...65535 if TCP/SOAP monitor is in use
//
private int _monitorPort = -1;
//
// Print all SOAP messages if true - default is false.
//
private boolean _trace = false;
//
// The stream used for tracing - the default is stdout
//
private PrintWriter _traceWriter = new PrintWriter(System.out);
/**
*
* Creates a valid SOAP message, including WS-A headers that specify
* the destination and operation (action). The WS-A headers include
* proper return/fault information for the recipient to use in response.
*
* @param source
* @param destination
* @param action
* @param bodyElements
*
* @return A valid SOAP message that can be sent to the destination.
*
*/
protected Element createMessage(EndpointReference source,
EndpointReference destination,
String action,
Element[] bodyElements)
{
Document doc = XmlUtils.createDocument();
Element soapXML = XmlUtils.createElement(doc, SoapConstants.ENVELOPE_QNAME);
//
// add WS-Addressing headers
//
MessageHeaders headers = new MessageHeaders(destination, action);
//
// if there's a source EPR, we can provide a wsa:From
//
if (source != null)
headers.setFromAddress(source);
Element headersXML = headers.toXML(doc);
soapXML.appendChild(headersXML);
//
// copy data into SOAP body
//
Element bodyXML = XmlUtils.createElement(doc, SoapConstants.BODY_QNAME);
soapXML.appendChild(bodyXML);
for (int n = 0; n < bodyElements.length; ++n)
{
if (bodyElements[n].getOwnerDocument() != doc)
bodyElements[n] = (Element)doc.importNode(bodyElements[n], true);
bodyXML.appendChild(bodyElements[n]);
}
return soapXML;
}
/**
*
* @param destination
*
* @return The URL of the EPR's wsa:Address. If SOAP monitoring is on,
* the URL's port is switched to the monitor port.
*
*/
protected URL getDestinationURL(EndpointReference destination)
{
URI uri = destination.getAddress();
//
// for TCP/SOAP monitoring, copy the URI with the new port.
// we have to make a new object because URI's are immutable.
//
try
{
if (isUsingSoapMonitor())
uri = new URI(uri.getScheme(),
uri.getUserInfo(),
uri.getHost(),
getSoapMonitorPort(),
uri.getPath(),
uri.getQuery(),
uri.getFragment());
return uri.toURL();
}
catch (Throwable error)
{
throw new RuntimeException(error.getMessage(), error);
}
}
public int getSoapMonitorPort()
{
return _monitorPort;
}
public PrintWriter getTraceWriter()
{
return _traceWriter;
}
/**
*
* {@inheritDoc}
* <br><br>
* The default value is 'false'.
*
*/
public boolean isUsingSoapMonitor()
{
return _monitorPort >= 0;
}
/**
*
* {@inheritDoc}
* <br><br>
* The default value is 'false'.
*
*/
public boolean isUsingTrace()
{
return _trace;
}
public Element[] send(EndpointReference src,
EndpointReference dest,
String wsaAction,
Element[] body)
{
if (dest == null)
throw new NullPointerException(_MESSAGES.get("NullDestinationEPR"));
if (wsaAction == null)
throw new NullPointerException(_MESSAGES.get("NullActionURI"));
if (body == null)
body = _EMPTY_BODY;
//
// create the request message and turn it into bytes
//
Element soapRequest = createMessage(src, dest, wsaAction, body);
byte[] soapBytes = XmlUtils.toString(soapRequest).getBytes();
if (isUsingTrace())
trace(soapRequest, false);
Element soapResponse = null;
try
{
//
// set up the HTTP request - POST of SOAP 1.2 data
//
URL url = getDestinationURL(dest);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-type", SoapConstants.CONTENT_TYPE_HEADER);
connection.setDoOutput(true);
connection.connect();
//
// send the SOAP request...
//
OutputStream output = connection.getOutputStream();
output.write(soapBytes);
output.flush();
output.close();
//
// read in the response and build an XML document from it
//
InputStream input = connection.getInputStream();
Document responseDoc = XmlUtils.createDocument(input);
soapResponse = XmlUtils.getFirstElement(responseDoc);
input.close();
connection.disconnect();
}
catch (Throwable error)
{
SoapFault soapFault = new SoapFault(error);
return new Element[]{ soapFault.toXML() };
}
if (isUsingTrace())
trace(soapResponse, true);
//
// return the elements inside the SOAP body
//
Element responseBody = XmlUtils.getElement(soapResponse, SoapConstants.BODY_QNAME);
return XmlUtils.getAllElements(responseBody);
}
public void setTrace(boolean trace)
{
_trace = trace;
}
/**
*
* {@inheritDoc}
* <br><br>
* Note that there is a default PrintWriter set at instantiation - it
* wraps the System.out stream.
*
*/
public void setTraceWriter(PrintWriter writer)
{
if (writer == null)
throw new NullPointerException(_MESSAGES.get("NullTraceWriter"));
_traceWriter = writer;
}
public void startSoapMonitor(int monitorPort)
{
if (monitorPort < 1)
{
Object[] filler = { new Integer(monitorPort) };
throw new RuntimeException(_MESSAGES.get("InvalidPort", filler));
}
_monitorPort = monitorPort;
}
public void stopSoapMonitor()
{
_monitorPort = -1;
}
/**
*
* @param xml
* An XML fragment that will be sent to the trace log.
*
* @param incoming
* True if the message was part of an incoming SOAP message. This
* merely provides some context in the trace log.
*
*/
protected void trace(Element xml, boolean incoming)
{
PrintWriter writer = getTraceWriter();
writer.write("[CLIENT TRACE] SOAP envelope contents (");
writer.write(incoming ? "incoming" : "outgoing");
writer.write("):\n\n");
writer.write(XmlUtils.toString(xml, false));
writer.write('\n');
writer.flush();
}
/**
*
* @param message
* The message to print to the trace log.
*
*/
protected void trace(String message)
{
PrintWriter writer = getTraceWriter();
writer.write("[CLIENT TRACE] ");
writer.write(message);
writer.write('\n');
writer.flush();
}
}