All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opensaml.ws.soap.client.http.HttpSOAPClient Maven / Gradle / Ivy

Go to download

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.

The newest version!
/*
 * 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);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy