org.jibx.ws.soap.SoapProcessor Maven / Gradle / Ivy
/*
* Copyright (c) 2007, Sosnoski Software Associates Limited. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following
* disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of
* JiBX nor the names of its contributors may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 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.
*/
package org.jibx.ws.soap;
import java.io.IOException;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jibx.runtime.IXMLReader;
import org.jibx.runtime.IXMLWriter;
import org.jibx.runtime.JiBXException;
import org.jibx.ws.WsException;
import org.jibx.ws.context.ExchangeContext;
import org.jibx.ws.context.InContext;
import org.jibx.ws.context.MessageContext;
import org.jibx.ws.context.OutContext;
import org.jibx.ws.io.XmlReaderWrapper;
import org.jibx.ws.io.handler.OutHandler;
import org.jibx.ws.process.Processor;
import org.jibx.ws.transport.InConnection;
import org.jibx.ws.transport.OutConnection;
import org.jibx.ws.transport.WsTransportException;
/**
* Processor for SOAP messages. The sequence of message exchanges is defined by the {@link ExchangeContext}, which
* contains a list of {@link MessageContext}s, corresponding to the expected sequence of outbound and inbound messages.
* Each MessageContext
is configured with appropriate handlers to handle the content of the message.
*
* The processor can be invoked using the {@link #invoke(OutConnection, InConnection)} method, which sequentially sends
* and receives messages according to the {@link MessageContext}.
*
* Alternatively, the caller can send and receive messages using the individual send and receive methods.
*
* @author Nigel Charman
*/
public final class SoapProcessor implements Processor
{
private static final Log logger = LogFactory.getLog(SoapProcessor.class);
// private final SoapVersion m_soapVersion;
private ExchangeContext m_exchangeCtx;
/** The optional SOAP encoding style to set in the SOAP message. */
private String m_encodingStyle;
/**
* Creates a SoapProcessor with the specified SoapVersion. The transport defined in the outbound context is used for
* both the outbound and inbound messages. The outbound message is formatted according to the XML formatting options
* in the outbound context.
*
* @param soapVersion the version of the SOAP standard to use
*/
public SoapProcessor(SoapVersion soapVersion) {
// m_soapVersion = soapVersion;
}
/**
* Creates a SoapProcessor with the specified SoapVersion. The transport defined in the outbound context is used for
* both the outbound and inbound messages. The outbound message is formatted according to the XML formatting options
* in the outbound context.
*
* Each phase of the message processing calls the commands and handlers associated with that phase.
*
* @param soapVersion the version of the SOAP standard to use
* @param exchangeContext the context of the message exchange. Contains the outbound and inbound contexts.
*/
public SoapProcessor(SoapVersion soapVersion, ExchangeContext exchangeContext) {
// m_soapVersion = soapVersion;
m_exchangeCtx = exchangeContext;
}
/**
* Sets the optional SOAP encodingStyle
* attribute on the SOAP envelope. If this method is not called, or the encodingStyle parameter is set to
* null
, then no encodingStyle attribute is added.
*
* @param encodingStyle value to set in encodingStyle attribute
*/
public void setSoapEncodingStyle(String encodingStyle) {
m_encodingStyle = encodingStyle;
}
/**
* {@inheritDoc}
*/
public void invoke(OutConnection oconn, InConnection iconn) throws IOException, WsException {
MessageContext msgCtx = m_exchangeCtx.getCurrentMessageContext();
while (msgCtx != null) {
if (msgCtx.isOutbound()) {
sendMessage(oconn);
} else {
receiveMessage(iconn);
}
m_exchangeCtx.switchMessageContext();
msgCtx = m_exchangeCtx.getCurrentMessageContext();
}
}
/**
* {@inheritDoc}
*/
public void sendMessage(OutConnection conn) throws IOException, WsException {
if (m_exchangeCtx.getCurrentMessageContext() == null) {
throw new IllegalStateException("No message context available for sending message");
}
if (!m_exchangeCtx.getCurrentMessageContext().isOutbound()) {
throw new IllegalStateException("Cannot send message when current message context is inbound");
}
OutContext context = (OutContext) m_exchangeCtx.getCurrentMessageContext();
SoapWriter soapWriter = new SoapWriter(conn);
boolean requestCompleted = false;
try {
logger.debug("Starting send message");
soapWriter.startMessage(m_encodingStyle);
IXMLWriter xmlWriter = soapWriter.getWriter();
if (context.hasHandlers(SoapPhase.HEADER)) {
soapWriter.startHeader();
context.invokeHandlers(SoapPhase.HEADER, xmlWriter);
soapWriter.endHeader();
}
soapWriter.startBody();
context.invokeBodyWriter(xmlWriter);
logger.debug("Ending send message body");
soapWriter.endBody();
soapWriter.sendMessageCompletely();
logger.debug("Message sent");
requestCompleted = true;
} finally {
conn.outputComplete();
// Abort the request on error
if (!requestCompleted) {
try {
logger.debug("Aborting send");
soapWriter.abortMessage();
} catch (IOException e) {
logger.error("Error aborting send", e);
}
}
}
}
/**
* Send a SOAP fault message.
*
* @param error the error message to send
* @param conn transport connection
* @throws IOException on an I/O error, for example unable to connect to server
* @throws WsException on any error other than I/O, for example invalid format of the response message
*/
public void sendFaultMessage(SoapFault error, OutConnection conn) throws IOException, WsException {
if (m_exchangeCtx.getCurrentMessageContext() == null) {
throw new IllegalStateException("No message context available for sending fault message");
}
if (!m_exchangeCtx.getCurrentMessageContext().isOutbound()) {
throw new IllegalStateException("Cannot send fault message when current message context is inbound");
}
OutContext context = (OutContext) m_exchangeCtx.getCurrentMessageContext();
SoapWriter soapWriter = new SoapWriter(conn);
boolean requestCompleted = false;
try {
soapWriter.startMessage(m_encodingStyle);
IXMLWriter xmlWriter = soapWriter.getWriter();
logger.debug("Starting send fault");
soapWriter.startBody();
SoapFault fault = (SoapFault) error;
soapWriter.startFault(fault);
List detailWriters = fault.getDetailWriters();
if (detailWriters != null && detailWriters.size() > 0) {
soapWriter.startFaultDetail();
for (int i = 0; i < detailWriters.size(); i++) {
((OutHandler) detailWriters.get(i)).invoke(context, xmlWriter);
}
soapWriter.endFaultDetail();
}
soapWriter.endFault();
logger.debug("Ending send fault body");
soapWriter.endBody();
soapWriter.sendMessageCompletely();
logger.debug("Fault sent");
requestCompleted = true;
} finally {
conn.outputComplete();
// Abort the request on error
if (!requestCompleted) {
try {
logger.debug("Aborting send fault");
soapWriter.abortMessage();
} catch (IOException e) {
logger.error("Error aborting send fault", e);
}
}
}
}
/**
* {@inheritDoc}
*/
public void receiveMessage(InConnection conn) throws IOException, WsException {
if (m_exchangeCtx.getCurrentMessageContext() == null) {
throw new IllegalStateException("No message context available for receiving message");
}
if (m_exchangeCtx.getCurrentMessageContext().isOutbound()) {
throw new IllegalStateException("Cannot receive message when current message context is outbound");
}
InContext context = (InContext) m_exchangeCtx.getCurrentMessageContext();
SoapReader soapReader = new SoapReader(conn);
try {
logger.debug("Starting receive message");
soapReader.startMessage();
IXMLReader xmlReader = soapReader.getReader();
if (soapReader.hasHeaders()) {
readSoapHeaders(xmlReader, context);
soapReader.endHeader();
}
soapReader.startBody();
if (soapReader.hasNonEmptyBody()) {
if (soapReader.isBodyFault()) {
// Handle SOAP Faults. Will need changes for SOAP 1.2
SoapFault fault = soapReader.startFault();
if (soapReader.startFaultDetail()) {
readSoapFaultDetail(xmlReader, context, fault);
soapReader.endFaultDetail();
}
soapReader.endFault();
context.setBody(fault);
} else {
// Handle SOAP body
context.invokeBodyReader(xmlReader);
if (context.getBody() == null) {
throw new WsException("No handlers could be found for unmarshalling the SOAP body payload");
}
}
}
soapReader.endBody();
soapReader.endMessage();
} catch (WsException e) {
if (conn.hasError()) {
throw new WsTransportException(conn.getErrorMessage());
} else {
throw e;
}
} finally {
conn.inputComplete();
}
}
/**
* Reads SOAP fault details from the xmlReader. For each element in the SOAP fault details, any handlers that have
* been defined for the bodyFaultPhase are given a chance to read the fault details. If no handlers read the
* details, the element is skipped.
*
* @param xmlReader the reader to read the SOAP fault from
* @param context
* @param fault the SOAP fault to add the details to
* @throws IOException
* @throws WsException
*/
private void readSoapFaultDetail(IXMLReader xmlReader, InContext context, SoapFault fault) throws IOException,
WsException {
try {
XmlReaderWrapper wrpr = XmlReaderWrapper.createXmlReaderWrapper(xmlReader);
while (wrpr.toTag() == IXMLReader.START_TAG) {
Object detail = null;
/**
* If handlers are defined on the body fault phase, they have the opportunity to add details to the
* SoapFault. Otherwise the SOAP details will be skipped.
*/
if (context.hasHandlers(SoapPhase.BODY_FAULT)) {
detail = context.invokeInHandlers(SoapPhase.BODY_FAULT, xmlReader);
}
if (detail != null) {
fault.addDetail(detail);
} else {
wrpr.skipPastEndTag(xmlReader.getNamespace(), xmlReader.getName());
}
}
} catch (JiBXException e) {
throw new WsException("Error while unmarshalling SOAP fault details", e);
}
}
/**
* Reads SOAP header from the xmlReader. For each element in the SOAP header, all handlers that have been defined
* for the header are sequentially given a chance to read the fault details. If no handlers read the details, the
* element is skipped, unless the SOAP mustUnderstand attribute is set, when a WsNotUnderstoodException is raised.
*
* @param xmlReader the reader to read the SOAP fault from
* @param context
* @throws IOException
* @throws WsException
*/
private void readSoapHeaders(IXMLReader xmlReader, InContext context) throws IOException, WsException {
try {
XmlReaderWrapper wrpr = XmlReaderWrapper.createXmlReaderWrapper(xmlReader);
while (wrpr.toTag() == IXMLReader.START_TAG) {
/**
* If handlers are defined on the headers, they have the opportunity to read the header. Otherwise the
* header will be skipped.
*/
Object header = context.invokeInHandlers(SoapPhase.HEADER, xmlReader);
if (header == null) {
String mustUnderstand = xmlReader.getAttributeValue(SoapConstants.SOAP_URI,
SoapConstants.MUSTUNDERSTAND_NAME);
if (SoapConstants.MUSTUNDERSTAND_TRUE.equals(mustUnderstand)) {
throw new WsNotUnderstoodException("");
}
wrpr.skipPastEndTag(xmlReader.getNamespace(), xmlReader.getName());
}
}
} catch (JiBXException e) {
throw new WsException("Error while unmarshalling SOAP header details", e);
}
}
/**
* {@inheritDoc}
*/
public void setExchangeContext(ExchangeContext exchangeCtx) {
m_exchangeCtx = exchangeCtx;
}
/**
* {@inheritDoc}
*/
public void reset() {
m_exchangeCtx.reset();
}
/**
* {@inheritDoc}
*/
public MessageContext getCurrentMessageContext() {
return m_exchangeCtx.getCurrentMessageContext();
}
/**
* {@inheritDoc}
*/
public MessageContext getNextMessageContext() {
return m_exchangeCtx.getNextMessageContext();
}
/**
* {@inheritDoc}
*/
public void switchMessageContext() {
m_exchangeCtx.switchMessageContext();
}
}