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

org.springframework.ws.soap.saaj.SaajSoapMessage Maven / Gradle / Ivy

There is a newer version: 4.0.11
Show newest version
/*
 * Copyright 2005-2022 the original author or authors.
 *
 * 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.springframework.ws.soap.saaj;

import jakarta.activation.DataHandler;
import jakarta.xml.soap.*;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;

import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.ws.mime.Attachment;
import org.springframework.ws.mime.AttachmentException;
import org.springframework.ws.soap.AbstractSoapMessage;
import org.springframework.ws.soap.SoapEnvelope;
import org.springframework.ws.soap.SoapMessage;
import org.springframework.ws.soap.SoapVersion;
import org.springframework.ws.soap.saaj.support.SaajUtils;
import org.springframework.ws.soap.support.SoapUtils;
import org.springframework.ws.transport.TransportConstants;
import org.springframework.ws.transport.TransportOutputStream;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSOutput;
import org.w3c.dom.ls.LSSerializer;

/**
 * SAAJ-specific implementation of the {@link SoapMessage} interface. Created via the {@link SaajSoapMessageFactory},
 * wraps a {@link SOAPMessage}.
 *
 * @author Arjen Poutsma
 * @author Greg Turnquist
 * @see SOAPMessage
 * @since 1.0.0
 */
public class SaajSoapMessage extends AbstractSoapMessage {

	private static final String CONTENT_TYPE_XOP = "application/xop+xml";

	private final MessageFactory messageFactory;

	private SOAPMessage saajMessage;

	private SoapEnvelope envelope;

	private final boolean langAttributeOnSoap11FaultString;

	/**
	 * Create a new {@code SaajSoapMessage} based on the given SAAJ {@code SOAPMessage}.
	 *
	 * @param soapMessage the SAAJ SOAPMessage
	 */
	public SaajSoapMessage(SOAPMessage soapMessage) {
		this(soapMessage, true, null);
	}

	/**
	 * Create a new {@code SaajSoapMessage} based on the given SAAJ {@code SOAPMessage}.
	 *
	 * @param soapMessage the SAAJ SOAPMessage
	 * @param messageFactory the SAAJ message factory
	 */
	public SaajSoapMessage(SOAPMessage soapMessage, MessageFactory messageFactory) {
		this(soapMessage, true, messageFactory);
	}

	/**
	 * Create a new {@code SaajSoapMessage} based on the given SAAJ {@code SOAPMessage}.
	 *
	 * @param soapMessage the SAAJ SOAPMessage
	 * @param langAttributeOnSoap11FaultString whether a {@code xml:lang} attribute is allowed on SOAP 1.1
	 *          {@code } elements
	 */
	public SaajSoapMessage(SOAPMessage soapMessage, boolean langAttributeOnSoap11FaultString) {
		this(soapMessage, langAttributeOnSoap11FaultString, null);
	}

	/**
	 * Create a new {@code SaajSoapMessage} based on the given SAAJ {@code SOAPMessage}.
	 *
	 * @param soapMessage the SAAJ SOAPMessage
	 * @param langAttributeOnSoap11FaultString whether a {@code xml:lang} attribute is allowed on SOAP 1.1
	 *          {@code } elements
	 * @param messageFactory the message factory
	 */
	public SaajSoapMessage(SOAPMessage soapMessage, boolean langAttributeOnSoap11FaultString,
			MessageFactory messageFactory) {
		Assert.notNull(soapMessage, "soapMessage must not be null");
		saajMessage = soapMessage;
		this.langAttributeOnSoap11FaultString = langAttributeOnSoap11FaultString;
		this.messageFactory = messageFactory;
	}

	/** Return the SAAJ {@code SOAPMessage} that this {@code SaajSoapMessage} is based on. */
	public SOAPMessage getSaajMessage() {
		return saajMessage;
	}

	/** Sets the SAAJ {@code SOAPMessage} that this {@code SaajSoapMessage} is based on. */
	public void setSaajMessage(SOAPMessage soapMessage) {
		Assert.notNull(soapMessage, "soapMessage must not be null");
		saajMessage = soapMessage;
		envelope = null;
	}

	@Override
	public SoapEnvelope getEnvelope() {
		if (envelope == null) {
			try {
				SOAPEnvelope saajEnvelope = getSaajMessage().getSOAPPart().getEnvelope();
				envelope = new SaajSoapEnvelope(saajEnvelope, langAttributeOnSoap11FaultString);
			} catch (SOAPException ex) {
				throw new SaajSoapEnvelopeException(ex);
			}
		}
		return envelope;
	}

	@Override
	public String getSoapAction() {
		MimeHeaders mimeHeaders = getSaajMessage().getMimeHeaders();
		if (SoapVersion.SOAP_11 == getVersion()) {
			String[] actions = mimeHeaders.getHeader(TransportConstants.HEADER_SOAP_ACTION);
			return ObjectUtils.isEmpty(actions) ? TransportConstants.EMPTY_SOAP_ACTION : actions[0];
		} else if (SoapVersion.SOAP_12 == getVersion()) {
			String[] contentTypes = mimeHeaders.getHeader(TransportConstants.HEADER_CONTENT_TYPE);
			return !ObjectUtils.isEmpty(contentTypes) ? SoapUtils.extractActionFromContentType(contentTypes[0])
					: TransportConstants.EMPTY_SOAP_ACTION;
		} else {
			throw new IllegalStateException("Unsupported SOAP version: " + getVersion());
		}
	}

	@Override
	public void setSoapAction(String soapAction) {
		MimeHeaders mimeHeaders = getSaajMessage().getMimeHeaders();
		soapAction = SoapUtils.escapeAction(soapAction);
		if (SoapVersion.SOAP_11 == getVersion()) {
			mimeHeaders.setHeader(TransportConstants.HEADER_SOAP_ACTION, soapAction);
		} else if (SoapVersion.SOAP_12 == getVersion()) {
			// force save of Content Type header
			try {
				saajMessage.saveChanges();
			} catch (SOAPException ex) {
				throw new SaajSoapMessageException("Could not save message", ex);
			}
			String[] contentTypes = mimeHeaders.getHeader(TransportConstants.HEADER_CONTENT_TYPE);
			String contentType = !ObjectUtils.isEmpty(contentTypes) ? contentTypes[0] : getVersion().getContentType();
			contentType = SoapUtils.setActionInContentType(contentType, soapAction);
			mimeHeaders.setHeader(TransportConstants.HEADER_CONTENT_TYPE, contentType);
			mimeHeaders.removeHeader(TransportConstants.HEADER_SOAP_ACTION);
		} else {
			throw new IllegalStateException("Unsupported SOAP version: " + getVersion());
		}

	}

	@Override
	public Document getDocument() {
		Assert.state(messageFactory != null, "Could find message factory to use");
		// return saajSoapMessage.getSaajMessage().getSOAPPart(); // does not work, see SWS-345
		try {
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			getSaajMessage().writeTo(bos);
			ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
			SOAPMessage saajMessage = messageFactory.createMessage(getSaajMessage().getMimeHeaders(), bis);
			setSaajMessage(saajMessage);
			return saajMessage.getSOAPPart();
		} catch (SOAPException | IOException ex) {
			throw new SaajSoapMessageException("Could not save changes", ex);
		}
	}

	@Override
	public void setDocument(Document document) {
		if (saajMessage.getSOAPPart() != document) {
			Assert.state(messageFactory != null, "Could find message factory to use");
			try {
				DOMImplementation implementation = document.getImplementation();
				Assert.isInstanceOf(DOMImplementationLS.class, implementation);

				DOMImplementationLS loadSaveImplementation = (DOMImplementationLS) implementation;
				LSOutput output = loadSaveImplementation.createLSOutput();
				ByteArrayOutputStream bos = new ByteArrayOutputStream();
				output.setByteStream(bos);

				LSSerializer serializer = loadSaveImplementation.createLSSerializer();
				serializer.write(document, output);

				ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());

				this.saajMessage = messageFactory.createMessage(saajMessage.getMimeHeaders(), bis);

			} catch (SOAPException | IOException ex) {
				throw new SaajSoapMessageException("Could not read input stream", ex);
			}
		}
	}

	@Override
	public void writeTo(OutputStream outputStream) throws IOException {
		MimeHeaders mimeHeaders = getSaajMessage().getMimeHeaders();
		if (ObjectUtils.isEmpty(mimeHeaders.getHeader(TransportConstants.HEADER_ACCEPT))) {
			mimeHeaders.setHeader(TransportConstants.HEADER_ACCEPT, getVersion().getContentType());
		}
		try {
			SOAPMessage message = getSaajMessage();
			message.saveChanges();
			if (outputStream instanceof TransportOutputStream) {
				TransportOutputStream transportOutputStream = (TransportOutputStream) outputStream;
				// some SAAJ implementations (Axis 1) do not have a Content-Type header by default
				MimeHeaders headers = message.getMimeHeaders();
				if (ObjectUtils.isEmpty(headers.getHeader(TransportConstants.HEADER_CONTENT_TYPE))) {
					SOAPEnvelope envelope1 = message.getSOAPPart().getEnvelope();
					if (envelope1.getElementQName().getNamespaceURI().equals(SoapVersion.SOAP_11.getEnvelopeNamespaceUri())) {
						headers.addHeader(TransportConstants.HEADER_CONTENT_TYPE, SoapVersion.SOAP_11.getContentType());
					} else {
						headers.addHeader(TransportConstants.HEADER_CONTENT_TYPE, SoapVersion.SOAP_12.getContentType());
					}
					message.saveChanges();
				}
				for (Iterator iterator = headers.getAllHeaders(); iterator.hasNext();) {
					MimeHeader mimeHeader = (MimeHeader) iterator.next();
					transportOutputStream.addHeader(mimeHeader.getName(), mimeHeader.getValue());
				}
			}
			message.writeTo(outputStream);

			outputStream.flush();
		} catch (SOAPException ex) {
			throw new SaajSoapMessageException("Could not write message to OutputStream: " + ex.getMessage(), ex);
		}
	}

	@Override
	public boolean isXopPackage() {
		SOAPPart saajPart = saajMessage.getSOAPPart();
		String[] contentTypes = saajPart.getMimeHeader(TransportConstants.HEADER_CONTENT_TYPE);
		for (String contentType : contentTypes) {
			if (contentType.contains(CONTENT_TYPE_XOP)) {
				return true;
			}
		}
		return false;
	}

	@Override
	public boolean convertToXopPackage() {
		convertMessageToXop();
		convertPartToXop();
		return true;
	}

	private void convertMessageToXop() {
		MimeHeaders mimeHeaders = saajMessage.getMimeHeaders();
		String[] oldContentTypes = mimeHeaders.getHeader(TransportConstants.HEADER_CONTENT_TYPE);
		String oldContentType = !ObjectUtils.isEmpty(oldContentTypes) ? oldContentTypes[0] : getVersion().getContentType();
		mimeHeaders.setHeader(TransportConstants.HEADER_CONTENT_TYPE,
				CONTENT_TYPE_XOP + ";type=" + '"' + oldContentType + '"');
	}

	private void convertPartToXop() {
		SOAPPart saajPart = saajMessage.getSOAPPart();
		String[] oldContentTypes = saajPart.getMimeHeader(TransportConstants.HEADER_CONTENT_TYPE);
		String oldContentType = !ObjectUtils.isEmpty(oldContentTypes) ? oldContentTypes[0] : getVersion().getContentType();
		saajPart.setMimeHeader(TransportConstants.HEADER_CONTENT_TYPE,
				CONTENT_TYPE_XOP + ";type=" + '"' + oldContentType + '"');
	}

	@Override
	@SuppressWarnings("unchecked")
	public Iterator getAttachments() throws AttachmentException {
		Iterator iterator = getSaajMessage().getAttachments();
		return new SaajAttachmentIterator(iterator);
	}

	@Override
	@SuppressWarnings("unchecked")
	public Attachment getAttachment(String contentId) {
		Assert.hasLength(contentId, "contentId must not be empty");
		MimeHeaders mimeHeaders = new MimeHeaders();
		mimeHeaders.setHeader(TransportConstants.HEADER_CONTENT_ID, contentId);
		Iterator iterator = getSaajMessage().getAttachments(mimeHeaders);
		if (!iterator.hasNext()) {
			return null;
		}
		AttachmentPart saajAttachment = iterator.next();
		return new SaajAttachment(saajAttachment);
	}

	@Override
	public Attachment addAttachment(String contentId, DataHandler dataHandler) {
		Assert.hasLength(contentId, "contentId must not be empty");
		Assert.notNull(dataHandler, "dataHandler must not be null");
		SOAPMessage message = getSaajMessage();
		AttachmentPart attachmentPart = message.createAttachmentPart(dataHandler);
		message.addAttachmentPart(attachmentPart);
		attachmentPart.setContentId(contentId);
		attachmentPart.setMimeHeader(TransportConstants.HEADER_CONTENT_TRANSFER_ENCODING, "binary");
		return new SaajAttachment(attachmentPart);
	}

	public String toString() {
		StringBuilder builder = new StringBuilder("SaajSoapMessage");
		try {
			SOAPEnvelope envelope = saajMessage.getSOAPPart().getEnvelope();
			if (envelope != null) {
				SOAPBody body = envelope.getBody();
				if (body != null) {
					SOAPElement bodyElement = SaajUtils.getFirstBodyElement(body);
					if (bodyElement != null) {
						builder.append(' ');
						builder.append(bodyElement.getElementQName());
					}
				}
			}
		} catch (SOAPException ex) {
			// ignore
		}
		return builder.toString();
	}

	private static class SaajAttachmentIterator implements Iterator {

		private final Iterator saajIterator;

		private SaajAttachmentIterator(Iterator saajIterator) {
			this.saajIterator = saajIterator;
		}

		@Override
		public boolean hasNext() {
			return saajIterator.hasNext();
		}

		@Override
		public Attachment next() {
			AttachmentPart saajAttachment = saajIterator.next();
			return new SaajAttachment(saajAttachment);
		}

		@Override
		public void remove() {
			saajIterator.remove();
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy