org.opensaml.ws.soap.client.http.HttpSOAPClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openws Show documentation
Show all versions of openws Show documentation
The OpenWS library provides a growing set of tools to work with web services at a low level. These tools include
classes for creating and reading SOAP messages, transport-independent clients for connecting to web services,
and various transports for use with those clients.
/*
* Licensed to the University Corporation for Advanced Internet Development,
* Inc. (UCAID) under one or more contributor license agreements. See the
* NOTICE file distributed with this work for additional information regarding
* copyright ownership. The UCAID licenses this file to You 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.opensaml.ws.soap.client.http;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.util.List;
import net.jcip.annotations.ThreadSafe;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.opensaml.ws.security.SecurityPolicy;
import org.opensaml.ws.security.SecurityPolicyResolver;
import org.opensaml.ws.soap.client.SOAPClient;
import org.opensaml.ws.soap.client.SOAPClientException;
import org.opensaml.ws.soap.client.SOAPFaultException;
import org.opensaml.ws.soap.client.SOAPMessageContext;
import org.opensaml.ws.soap.common.SOAPException;
import org.opensaml.ws.soap.soap11.Envelope;
import org.opensaml.ws.soap.soap11.Fault;
import org.opensaml.xml.Configuration;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.io.Marshaller;
import org.opensaml.xml.io.MarshallingException;
import org.opensaml.xml.io.Unmarshaller;
import org.opensaml.xml.io.UnmarshallingException;
import org.opensaml.xml.parse.ParserPool;
import org.opensaml.xml.parse.XMLParserException;
import org.opensaml.xml.security.SecurityException;
import org.opensaml.xml.util.XMLHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;
/**
* SOAP client that uses HTTP as the underlying transport and POST as the binding.
*
* NOTE this client does not provide access to a {@link org.opensaml.ws.transport.InTransport} or
* {@link org.opensaml.ws.transport.OutTransport}. Therefore any {@link SecurityPolicy} which operates on these object
* can not be used with this client.
*/
@ThreadSafe
public class HttpSOAPClient implements SOAPClient {
/** Class logger. */
private final Logger log = LoggerFactory.getLogger(HttpSOAPClient.class);
/** HTTP client used to send requests and receive responses. */
private HttpClient httpClient;
/** Pool of XML parsers used to parser incoming responses. */
private ParserPool parserPool;
/**
* Constructor.
*
* @param client Client used to make outbound HTTP requests. This client SHOULD employ a
* {@link org.apache.commons.httpclient.MultiThreadedHttpConnectionManager} and may be shared with other
* objects.
* @param parser pool of XML parsers used to parse incoming responses
*/
public HttpSOAPClient(HttpClient client, ParserPool parser) {
if (client == null) {
throw new IllegalArgumentException("HtppClient may not be null");
}
httpClient = client;
if (parser == null) {
throw new IllegalArgumentException("ParserPool may not be null");
}
parserPool = parser;
}
/** {@inheritDoc} */
public void send(String endpoint, SOAPMessageContext messageContext) throws SOAPException, SecurityException {
PostMethod post = null;
try {
post = createPostMethod(endpoint, (HttpSOAPRequestParameters) messageContext.getSOAPRequestParameters(),
(Envelope) messageContext.getOutboundMessage());
int result = httpClient.executeMethod(post);
log.debug("Received HTTP status code of {} when POSTing SOAP message to {}", result, endpoint);
if (result == HttpStatus.SC_OK) {
processSuccessfulResponse(post, messageContext);
} else if (result == HttpStatus.SC_INTERNAL_SERVER_ERROR) {
processFaultResponse(post, messageContext);
} else {
throw new SOAPClientException("Received " + result + " HTTP response status code from HTTP request to "
+ endpoint);
}
} catch (IOException e) {
throw new SOAPClientException("Unable to send request to " + endpoint, e);
} finally {
if (post != null) {
post.releaseConnection();
}
}
}
/**
* Creates the post method used to send the SOAP request.
*
* @param endpoint endpoint to which the message is sent
* @param requestParams HTTP request parameters
* @param message message to be sent
*
* @return the post method to be used to send this message
*
* @throws SOAPClientException thrown if the message could not be marshalled
*/
protected PostMethod createPostMethod(String endpoint, HttpSOAPRequestParameters requestParams, Envelope message)
throws SOAPClientException {
log.debug("POSTing SOAP message to {}", endpoint);
PostMethod post = new PostMethod(endpoint);
post.setRequestEntity(createRequestEntity(message, Charset.forName("UTF-8")));
if (requestParams != null && requestParams.getSoapAction() != null) {
post.setRequestHeader(HttpSOAPRequestParameters.SOAP_ACTION_HEADER, requestParams.getSoapAction());
}
return post;
}
/**
* Creates the request entity that makes up the POST message body.
*
* @param message message to be sent
* @param charset character set used for the message
*
* @return request entity that makes up the POST message body
*
* @throws SOAPClientException thrown if the message could not be marshalled
*/
protected RequestEntity createRequestEntity(Envelope message, Charset charset) throws SOAPClientException {
try {
Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(message);
ByteArrayOutputStream arrayOut = new ByteArrayOutputStream();
OutputStreamWriter writer = new OutputStreamWriter(arrayOut, charset);
if (log.isDebugEnabled()) {
log.debug("Outbound SOAP message is:\n" + XMLHelper.prettyPrintXML(marshaller.marshall(message)));
}
XMLHelper.writeNode(marshaller.marshall(message), writer);
return new ByteArrayRequestEntity(arrayOut.toByteArray(), "text/xml");
} catch (MarshallingException e) {
throw new SOAPClientException("Unable to marshall SOAP envelope", e);
}
}
/**
* Processes a successful, as determined by an HTTP 200 status code, response.
*
* @param httpMethod the HTTP method used to send the request and receive the response
* @param messageContext current messages context
*
* @throws SOAPClientException thrown if there is a problem reading the response from the {@link PostMethod}
*/
protected void processSuccessfulResponse(PostMethod httpMethod, SOAPMessageContext messageContext)
throws SOAPClientException {
try {
Envelope response = unmarshallResponse(httpMethod.getResponseBodyAsStream());
messageContext.setInboundMessage(response);
evaluateSecurityPolicy(messageContext);
} catch (IOException e) {
throw new SOAPClientException("Unable to read response", e);
}
}
/**
* Processes a SOAP fault, as determined by an HTTP 500 status code, response.
*
* @param httpMethod the HTTP method used to send the request and receive the response
* @param messageContext current messages context
*
* @throws SOAPClientException thrown if the response can not be read from the {@link PostMethod}
* @throws SOAPFaultException an exception containing the SOAP fault
*/
protected void processFaultResponse(PostMethod httpMethod, SOAPMessageContext messageContext)
throws SOAPClientException, SOAPFaultException {
try {
Envelope response = unmarshallResponse(httpMethod.getResponseBodyAsStream());
messageContext.setInboundMessage(response);
List faults = response.getBody().getUnknownXMLObjects(Fault.DEFAULT_ELEMENT_NAME);
if (faults.size() < 1) {
throw new SOAPClientException("HTTP status code was 500 but SOAP response did not contain a Fault");
}
Fault fault = (Fault) faults.get(0);
log.debug("SOAP fault code {} with message {}", fault.getCode().getValue(), fault.getMessage().getValue());
SOAPFaultException faultException = new SOAPFaultException("SOAP Fault: " + fault.getCode().getValue()
+ " Fault Message: " + fault.getMessage().getValue());
faultException.setFault(fault);
throw faultException;
} catch (IOException e) {
throw new SOAPClientException("Unable to read response", e);
}
}
/**
* Unmarshalls the incoming response from a POST request.
*
* @param responseStream input stream bearing the response
*
* @return the response
*
* @throws SOAPClientException thrown if the incoming response can not be unmarshalled into an {@link Envelope}
*/
protected Envelope unmarshallResponse(InputStream responseStream) throws SOAPClientException {
try {
Element responseElem = parserPool.parse(responseStream).getDocumentElement();
if (log.isDebugEnabled()) {
log.debug("Inbound SOAP message was:\n" + XMLHelper.prettyPrintXML(responseElem));
}
Unmarshaller unmarshaller = Configuration.getUnmarshallerFactory().getUnmarshaller(responseElem);
return (Envelope) unmarshaller.unmarshall(responseElem);
} catch (XMLParserException e) {
throw new SOAPClientException("Unable to parse the XML within the response", e);
} catch (UnmarshallingException e) {
throw new SOAPClientException("unable to unmarshall the response DOM", e);
}
}
/**
* Evaluates the security policy associated with the given message context. If no policy resolver is registered or
* no policy is located during the resolution process then no policy is evaluated. Note that neither the inbound or
* outbound message transport is available.
*
* @param messageContext current message context
*
* @throws SOAPClientException thrown if there is a problem resolving or evaluating a security policy
*/
protected void evaluateSecurityPolicy(SOAPMessageContext messageContext) throws SOAPClientException {
SecurityPolicyResolver policyResolver = messageContext.getSecurityPolicyResolver();
if (policyResolver == null) {
return;
}
SecurityPolicy policy = null;
try {
policy = policyResolver.resolveSingle(messageContext);
if (policy == null) {
return;
}
} catch (SecurityException e) {
throw new SOAPClientException("Unable to resolve security policy for inbound SOAP response", e);
}
try {
log.debug("Evaluating security policy for inbound SOAP response");
policy.evaluate(messageContext);
} catch (SecurityException e) {
throw new SOAPClientException("Inbound SOAP response does not meet security policy", e);
}
}
}