/* * Copyright 2005-2010 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.stroap; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import javax.activation.DataHandler; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.ParserConfigurationException; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLEventWriter; import javax.xml.stream.XMLStreamException; import javax.xml.stream.events.EndDocument; import javax.xml.stream.events.StartDocument; import javax.xml.stream.events.XMLEvent; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.dom.DOMSource; import org.springframework.util.Assert; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; 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.SoapEnvelopeException; import org.springframework.ws.soap.SoapVersion; import org.springframework.ws.soap.support.SoapUtils; import org.springframework.ws.stream.StreamingPayload; import org.springframework.ws.stream.StreamingWebServiceMessage; import org.springframework.ws.transport.TransportConstants; import org.springframework.ws.transport.TransportInputStream; import org.springframework.ws.transport.TransportOutputStream; import org.springframework.xml.stream.AbstractXMLEventWriter; 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; import org.xml.sax.SAXException; /** * @author Arjen Poutsma */ public class StroapMessage extends AbstractSoapMessage implements StreamingWebServiceMessage { private final MultiValueMap<String, String> mimeHeaders = new LinkedMultiValueMap<String, String>(); private StroapEnvelope envelope; private final StroapMessageFactory messageFactory; private final StartDocument startDocument; private final EndDocument endDocument; public StroapMessage(StroapMessageFactory messageFactory) { this(null, null, messageFactory); } public StroapMessage(MultiValueMap<String, String> mimeHeaders, StroapEnvelope envelope, StroapMessageFactory messageFactory) { Assert.notNull(messageFactory, "'messageFactory' must not be null"); this.messageFactory = messageFactory; if (mimeHeaders != null) { this.mimeHeaders.putAll(mimeHeaders); } this.envelope = envelope != null ? envelope : new StroapEnvelope(messageFactory); if (!this.mimeHeaders.containsKey(TransportConstants.HEADER_CONTENT_TYPE)) { this.mimeHeaders .set(TransportConstants.HEADER_CONTENT_TYPE, messageFactory.getSoapVersion().getContentType()); } if (!this.mimeHeaders.containsKey(TransportConstants.HEADER_ACCEPT)) { this.mimeHeaders.set(TransportConstants.HEADER_ACCEPT, messageFactory.getSoapVersion().getContentType()); } this.startDocument = messageFactory.getEventFactory().createStartDocument(); this.endDocument = messageFactory.getEventFactory().createEndDocument(); } static StroapMessage build(InputStream inputStream, StroapMessageFactory messageFactory) throws XMLStreamException, IOException { MultiValueMap<String, String> mimeHeaders = parseMimeHeaders(inputStream); XMLEventReader eventReader = messageFactory.getInputFactory().createXMLEventReader(inputStream); StroapEnvelope envelope = StroapEnvelope.build(eventReader, messageFactory); return new StroapMessage(mimeHeaders, envelope, messageFactory); } private static MultiValueMap<String, String> parseMimeHeaders(InputStream inputStream) throws IOException { MultiValueMap<String, String> mimeHeaders = new LinkedMultiValueMap<String, String>(); if (inputStream instanceof TransportInputStream) { TransportInputStream transportInputStream = (TransportInputStream) inputStream; for (Iterator<String> headerNames = transportInputStream.getHeaderNames(); headerNames.hasNext();) { String headerName = headerNames.next(); for (Iterator<String> headerValues = transportInputStream.getHeaders(headerName); headerValues.hasNext();) { String headerValue = headerValues.next(); StringTokenizer tokenizer = new StringTokenizer(headerValue, ","); while (tokenizer.hasMoreTokens()) { mimeHeaders.add(headerName, tokenizer.nextToken().trim()); } } } } return mimeHeaders; } public SoapEnvelope getEnvelope() throws SoapEnvelopeException { return envelope; } public void setStreamingPayload(StreamingPayload payload) { StroapBody soapBody = (StroapBody) getSoapBody(); soapBody.setStreamingPayload(payload); } public String getSoapAction() { String soapAction = mimeHeaders.getFirst(TransportConstants.HEADER_SOAP_ACTION); return StringUtils.hasLength(soapAction) ? soapAction : TransportConstants.EMPTY_SOAP_ACTION; } public void setSoapAction(String soapAction) { soapAction = SoapUtils.escapeAction(soapAction); mimeHeaders.set(TransportConstants.HEADER_SOAP_ACTION, soapAction); } @Override public SoapVersion getVersion() { return messageFactory.getSoapVersion(); } public Document getDocument() { try { DocumentBuilder documentBuilder = messageFactory.getDocumentBuilderFactory().newDocumentBuilder(); try { Document result = documentBuilder.newDocument(); DOMResult domResult = new DOMResult(result); XMLEventWriter eventWriter = messageFactory.getOutputFactory().createXMLEventWriter(domResult); eventWriter.add(startDocument); envelope.writeTo(new NoStartEndDocumentWriter(eventWriter)); eventWriter.add(endDocument); eventWriter.flush(); return result; } catch (XMLStreamException ignored) { // ignored } catch (UnsupportedOperationException ignored) { // ignored } // XMLOutputFactory does not support DOMResults, so let's do it the hard way ByteArrayOutputStream bos = new ByteArrayOutputStream(); writeTo(bos); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); return documentBuilder.parse(bis); } catch (ParserConfigurationException ex) { throw new StroapMessageException("Could not create DocumentBuilderFactory", ex); } catch (SAXException ex) { throw new StroapMessageException("Could not save message as Document", ex); } catch (IOException ex) { throw new StroapMessageException("Could not save message as Document", ex); } } public void setDocument(Document document) { try { try { DOMSource domSource = new DOMSource(document); XMLEventReader eventReader = messageFactory.getInputFactory().createXMLEventReader(domSource); this.envelope = StroapEnvelope.build(eventReader, messageFactory); return; } catch (XMLStreamException ignored) { // ignored } catch (UnsupportedOperationException ignored) { // ignored } // XMLInputFactory does not support DOMSources, so let's do it the hard way 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()); XMLEventReader eventReader = messageFactory.getInputFactory().createXMLEventReader(bis); this.envelope = StroapEnvelope.build(eventReader, messageFactory); } catch (XMLStreamException ex) { throw new StroapMessageException("Could not read Document", ex); } } public void writeTo(OutputStream outputStream) throws IOException { if (outputStream instanceof TransportOutputStream) { TransportOutputStream tos = (TransportOutputStream) outputStream; for (Map.Entry<String, List<String>> entry : mimeHeaders.entrySet()) { String name = entry.getKey(); for (String value : entry.getValue()) { tos.addHeader(name, value); } } } try { XMLEventWriter eventWriter = messageFactory.getOutputFactory().createXMLEventWriter(outputStream); eventWriter.add(startDocument); envelope.writeTo(new NoStartEndDocumentWriter(eventWriter)); eventWriter.add(endDocument); eventWriter.flush(); } catch (XMLStreamException ex) { throw new StroapMessageException("Could not write message to OutputStream: " + ex.getMessage(), ex); } } public boolean isXopPackage() { return false; } public boolean convertToXopPackage() { return false; } public Attachment getAttachment(String contentId) throws AttachmentException { throw new UnsupportedOperationException(); } public Iterator<Attachment> getAttachments() throws AttachmentException { return Collections.<Attachment>emptyList().iterator(); } public Attachment addAttachment(String contentId, DataHandler dataHandler) { throw new UnsupportedOperationException(); } @Override public String toString() { StringBuilder builder = new StringBuilder("StroapMessage"); StroapBody body = (StroapBody) envelope.getBody(); if (body != null) { builder.append(' '); builder.append(body.getPayloadName()); } return builder.toString(); } private static class NoStartEndDocumentWriter extends AbstractXMLEventWriter { private final XMLEventWriter delegate; private NoStartEndDocumentWriter(XMLEventWriter delegate) { this.delegate = delegate; } public void add(XMLEvent event) throws XMLStreamException { if (!event.isStartDocument() && !event.isEndDocument()) { delegate.add(event); } } } }