| /* |
| * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package com.sun.xml.internal.ws.encoding.xml; |
| |
| import com.sun.istack.internal.NotNull; |
| import com.sun.xml.internal.bind.api.Bridge; |
| import com.sun.xml.internal.ws.api.SOAPVersion; |
| import com.sun.xml.internal.ws.api.WSFeatureList; |
| import com.sun.xml.internal.ws.api.message.*; |
| import com.sun.xml.internal.ws.api.model.wsdl.WSDLPort; |
| import com.sun.xml.internal.ws.api.pipe.Codec; |
| import com.sun.xml.internal.ws.api.streaming.XMLStreamWriterFactory; |
| import com.sun.xml.internal.ws.developer.StreamingAttachmentFeature; |
| import com.sun.xml.internal.ws.encoding.ContentType; |
| import com.sun.xml.internal.ws.encoding.MimeMultipartParser; |
| import com.sun.xml.internal.ws.encoding.XMLHTTPBindingCodec; |
| import com.sun.xml.internal.ws.message.AbstractMessageImpl; |
| import com.sun.xml.internal.ws.message.EmptyMessageImpl; |
| import com.sun.xml.internal.ws.message.MimeAttachmentSet; |
| import com.sun.xml.internal.ws.message.source.PayloadSourceMessage; |
| import com.sun.xml.internal.ws.util.ByteArrayBuffer; |
| import com.sun.xml.internal.ws.util.StreamUtils; |
| import org.xml.sax.ContentHandler; |
| import org.xml.sax.ErrorHandler; |
| import org.xml.sax.SAXException; |
| |
| import javax.activation.DataSource; |
| import javax.xml.bind.JAXBException; |
| import javax.xml.bind.Unmarshaller; |
| import javax.xml.soap.SOAPException; |
| import javax.xml.soap.SOAPMessage; |
| import javax.xml.stream.XMLStreamException; |
| import javax.xml.stream.XMLStreamReader; |
| import javax.xml.stream.XMLStreamWriter; |
| import javax.xml.transform.Source; |
| import javax.xml.transform.stream.StreamSource; |
| import javax.xml.ws.WebServiceException; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| |
| /** |
| * |
| * @author Jitendra Kotamraju |
| */ |
| public final class XMLMessage { |
| |
| private static final int PLAIN_XML_FLAG = 1; // 00001 |
| private static final int MIME_MULTIPART_FLAG = 2; // 00010 |
| private static final int FI_ENCODED_FLAG = 16; // 10000 |
| |
| /* |
| * Construct a message given a content type and an input stream. |
| */ |
| public static Message create(final String ct, InputStream in, WSFeatureList f) { |
| Message data; |
| try { |
| in = StreamUtils.hasSomeData(in); |
| if (in == null) { |
| return Messages.createEmpty(SOAPVersion.SOAP_11); |
| } |
| |
| if (ct != null) { |
| final ContentType contentType = new ContentType(ct); |
| final int contentTypeId = identifyContentType(contentType); |
| if ((contentTypeId & MIME_MULTIPART_FLAG) != 0) { |
| data = new XMLMultiPart(ct, in, f); |
| } else if ((contentTypeId & PLAIN_XML_FLAG) != 0) { |
| data = new XmlContent(ct, in, f); |
| } else { |
| data = new UnknownContent(ct, in); |
| } |
| } else { |
| // According to HTTP spec 7.2.1, if the media type remain |
| // unknown, treat as application/octet-stream |
| data = new UnknownContent("application/octet-stream", in); |
| } |
| } catch(Exception ex) { |
| throw new WebServiceException(ex); |
| } |
| return data; |
| } |
| |
| |
| public static Message create(Source source) { |
| return (source == null) ? |
| Messages.createEmpty(SOAPVersion.SOAP_11) : |
| Messages.createUsingPayload(source, SOAPVersion.SOAP_11); |
| } |
| |
| public static Message create(DataSource ds, WSFeatureList f) { |
| try { |
| return (ds == null) ? |
| Messages.createEmpty(SOAPVersion.SOAP_11) : |
| create(ds.getContentType(), ds.getInputStream(), f); |
| } catch(IOException ioe) { |
| throw new WebServiceException(ioe); |
| } |
| } |
| |
| public static Message create(Exception e) { |
| return new FaultMessage(SOAPVersion.SOAP_11); |
| } |
| |
| /* |
| * Get the content type ID from the content type. |
| */ |
| private static int getContentId(String ct) { |
| try { |
| final ContentType contentType = new ContentType(ct); |
| return identifyContentType(contentType); |
| } catch(Exception ex) { |
| throw new WebServiceException(ex); |
| } |
| } |
| |
| /** |
| * Return true if the content uses fast infoset. |
| */ |
| public static boolean isFastInfoset(String ct) { |
| return (getContentId(ct) & FI_ENCODED_FLAG) != 0; |
| } |
| |
| /* |
| * Verify a contentType. |
| * |
| * @return |
| * MIME_MULTIPART_FLAG | PLAIN_XML_FLAG |
| * MIME_MULTIPART_FLAG | FI_ENCODED_FLAG; |
| * PLAIN_XML_FLAG |
| * FI_ENCODED_FLAG |
| * |
| */ |
| public static int identifyContentType(ContentType contentType) { |
| String primary = contentType.getPrimaryType(); |
| String sub = contentType.getSubType(); |
| |
| if (primary.equalsIgnoreCase("multipart") && sub.equalsIgnoreCase("related")) { |
| String type = contentType.getParameter("type"); |
| if (type != null) { |
| if (isXMLType(type)) { |
| return MIME_MULTIPART_FLAG | PLAIN_XML_FLAG; |
| } else if (isFastInfosetType(type)) { |
| return MIME_MULTIPART_FLAG | FI_ENCODED_FLAG; |
| } |
| } |
| return 0; |
| } else if (isXMLType(primary, sub)) { |
| return PLAIN_XML_FLAG; |
| } else if (isFastInfosetType(primary, sub)) { |
| return FI_ENCODED_FLAG; |
| } |
| return 0; |
| } |
| |
| protected static boolean isXMLType(@NotNull String primary, @NotNull String sub) { |
| return (primary.equalsIgnoreCase("text") && sub.equalsIgnoreCase("xml")) |
| || (primary.equalsIgnoreCase("application") && sub.equalsIgnoreCase("xml")) |
| || (primary.equalsIgnoreCase("application") && sub.toLowerCase().endsWith("+xml")); |
| } |
| |
| protected static boolean isXMLType(String type) { |
| String lowerType = type.toLowerCase(); |
| return lowerType.startsWith("text/xml") |
| || lowerType.startsWith("application/xml") |
| || (lowerType.startsWith("application/") && (lowerType.indexOf("+xml") != -1)); |
| } |
| |
| protected static boolean isFastInfosetType(String primary, String sub) { |
| return primary.equalsIgnoreCase("application") && sub.equalsIgnoreCase("fastinfoset"); |
| } |
| |
| protected static boolean isFastInfosetType(String type) { |
| return type.toLowerCase().startsWith("application/fastinfoset"); |
| } |
| |
| |
| /** |
| * Access a {@link Message} as a {@link DataSource}. |
| * <p> |
| * A {@link Message} implementation will implement this if the |
| * messages is to be access as data source. |
| * <p> |
| * TODO: consider putting as part of the API. |
| */ |
| public static interface MessageDataSource { |
| /** |
| * Check if the data source has been consumed. |
| * @return true of the data source has been consumed, otherwise false. |
| */ |
| boolean hasUnconsumedDataSource(); |
| |
| /** |
| * Get the data source. |
| * @return the data source. |
| */ |
| DataSource getDataSource(); |
| } |
| |
| /** |
| * It's conent-type is some XML type |
| * |
| */ |
| private static class XmlContent extends AbstractMessageImpl implements MessageDataSource { |
| private final XmlDataSource dataSource; |
| private boolean consumed; |
| private Message delegate; |
| private final HeaderList headerList; |
| // private final WSBinding binding; |
| private WSFeatureList features; |
| |
| public XmlContent(String ct, InputStream in, WSFeatureList f) { |
| super(SOAPVersion.SOAP_11); |
| dataSource = new XmlDataSource(ct, in); |
| this.headerList = new HeaderList(SOAPVersion.SOAP_11); |
| // this.binding = binding; |
| features = f; |
| } |
| |
| private Message getMessage() { |
| if (delegate == null) { |
| InputStream in = dataSource.getInputStream(); |
| assert in != null; |
| delegate = Messages.createUsingPayload(new StreamSource(in), SOAPVersion.SOAP_11); |
| consumed = true; |
| } |
| return delegate; |
| } |
| |
| public boolean hasUnconsumedDataSource() { |
| return !dataSource.consumed()&&!consumed; |
| } |
| |
| public DataSource getDataSource() { |
| return hasUnconsumedDataSource() ? dataSource : |
| XMLMessage.getDataSource(getMessage(), features); |
| } |
| |
| public boolean hasHeaders() { |
| return false; |
| } |
| |
| public @NotNull MessageHeaders getHeaders() { |
| return headerList; |
| } |
| |
| public String getPayloadLocalPart() { |
| return getMessage().getPayloadLocalPart(); |
| } |
| |
| public String getPayloadNamespaceURI() { |
| return getMessage().getPayloadNamespaceURI(); |
| } |
| |
| public boolean hasPayload() { |
| return true; |
| } |
| |
| public boolean isFault() { |
| return false; |
| } |
| |
| public Source readEnvelopeAsSource() { |
| return getMessage().readEnvelopeAsSource(); |
| } |
| |
| public Source readPayloadAsSource() { |
| return getMessage().readPayloadAsSource(); |
| } |
| |
| public SOAPMessage readAsSOAPMessage() throws SOAPException { |
| return getMessage().readAsSOAPMessage(); |
| } |
| |
| public SOAPMessage readAsSOAPMessage(Packet packet, boolean inbound) throws SOAPException { |
| return getMessage().readAsSOAPMessage(packet, inbound); |
| } |
| |
| public <T> T readPayloadAsJAXB(Unmarshaller unmarshaller) throws JAXBException { |
| return (T)getMessage().readPayloadAsJAXB(unmarshaller); |
| } |
| /** @deprecated */ |
| public <T> T readPayloadAsJAXB(Bridge<T> bridge) throws JAXBException { |
| return getMessage().readPayloadAsJAXB(bridge); |
| } |
| |
| public XMLStreamReader readPayload() throws XMLStreamException { |
| return getMessage().readPayload(); |
| } |
| |
| |
| public void writePayloadTo(XMLStreamWriter sw) throws XMLStreamException { |
| getMessage().writePayloadTo(sw); |
| } |
| |
| public void writeTo(XMLStreamWriter sw) throws XMLStreamException { |
| getMessage().writeTo(sw); |
| } |
| |
| public void writeTo(ContentHandler contentHandler, ErrorHandler errorHandler) throws SAXException { |
| getMessage().writeTo(contentHandler, errorHandler); |
| } |
| |
| public Message copy() { |
| return getMessage().copy(); |
| } |
| |
| protected void writePayloadTo(ContentHandler contentHandler, ErrorHandler errorHandler, boolean fragment) throws SAXException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| } |
| |
| |
| |
| /** |
| * Data represented as a multi-part MIME message. |
| * <p> |
| * The root part may be an XML or an FI document. This class |
| * parses MIME message lazily. |
| */ |
| public static final class XMLMultiPart extends AbstractMessageImpl implements MessageDataSource { |
| private final DataSource dataSource; |
| private final StreamingAttachmentFeature feature; |
| private Message delegate; |
| private HeaderList headerList;// = new HeaderList(); |
| // private final WSBinding binding; |
| private final WSFeatureList features; |
| |
| public XMLMultiPart(final String contentType, final InputStream is, WSFeatureList f) { |
| super(SOAPVersion.SOAP_11); |
| headerList = new HeaderList(SOAPVersion.SOAP_11); |
| dataSource = createDataSource(contentType, is); |
| this.feature = f.get(StreamingAttachmentFeature.class); |
| this.features = f; |
| } |
| |
| private Message getMessage() { |
| if (delegate == null) { |
| MimeMultipartParser mpp; |
| try { |
| mpp = new MimeMultipartParser(dataSource.getInputStream(), |
| dataSource.getContentType(), feature); |
| } catch(IOException ioe) { |
| throw new WebServiceException(ioe); |
| } |
| InputStream in = mpp.getRootPart().asInputStream(); |
| assert in != null; |
| delegate = new PayloadSourceMessage(headerList, new StreamSource(in), new MimeAttachmentSet(mpp), SOAPVersion.SOAP_11); |
| } |
| return delegate; |
| } |
| |
| public boolean hasUnconsumedDataSource() { |
| return delegate == null; |
| } |
| |
| public DataSource getDataSource() { |
| return hasUnconsumedDataSource() ? dataSource : |
| XMLMessage.getDataSource(getMessage(), features); |
| } |
| |
| public boolean hasHeaders() { |
| return false; |
| } |
| |
| public @NotNull MessageHeaders getHeaders() { |
| return headerList; |
| } |
| |
| public String getPayloadLocalPart() { |
| return getMessage().getPayloadLocalPart(); |
| } |
| |
| public String getPayloadNamespaceURI() { |
| return getMessage().getPayloadNamespaceURI(); |
| } |
| |
| public boolean hasPayload() { |
| return true; |
| } |
| |
| public boolean isFault() { |
| return false; |
| } |
| |
| public Source readEnvelopeAsSource() { |
| return getMessage().readEnvelopeAsSource(); |
| } |
| |
| public Source readPayloadAsSource() { |
| return getMessage().readPayloadAsSource(); |
| } |
| |
| public SOAPMessage readAsSOAPMessage() throws SOAPException { |
| return getMessage().readAsSOAPMessage(); |
| } |
| |
| public SOAPMessage readAsSOAPMessage(Packet packet, boolean inbound) throws SOAPException { |
| return getMessage().readAsSOAPMessage(packet, inbound); |
| } |
| |
| public <T> T readPayloadAsJAXB(Unmarshaller unmarshaller) throws JAXBException { |
| return (T)getMessage().readPayloadAsJAXB(unmarshaller); |
| } |
| |
| public <T> T readPayloadAsJAXB(Bridge<T> bridge) throws JAXBException { |
| return getMessage().readPayloadAsJAXB(bridge); |
| } |
| |
| public XMLStreamReader readPayload() throws XMLStreamException { |
| return getMessage().readPayload(); |
| } |
| |
| public void writePayloadTo(XMLStreamWriter sw) throws XMLStreamException { |
| getMessage().writePayloadTo(sw); |
| } |
| |
| public void writeTo(XMLStreamWriter sw) throws XMLStreamException { |
| getMessage().writeTo(sw); |
| } |
| |
| public void writeTo(ContentHandler contentHandler, ErrorHandler errorHandler) throws SAXException { |
| getMessage().writeTo(contentHandler, errorHandler); |
| } |
| |
| public Message copy() { |
| return getMessage().copy(); |
| } |
| |
| protected void writePayloadTo(ContentHandler contentHandler, ErrorHandler errorHandler, boolean fragment) throws SAXException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public boolean isOneWay(@NotNull WSDLPort port) { |
| return false; |
| } |
| |
| public @NotNull AttachmentSet getAttachments() { |
| return getMessage().getAttachments(); |
| } |
| |
| } |
| |
| private static class FaultMessage extends EmptyMessageImpl { |
| |
| public FaultMessage(SOAPVersion version) { |
| super(version); |
| } |
| |
| @Override |
| public boolean isFault() { |
| return true; |
| } |
| } |
| |
| |
| /** |
| * Don't know about this content. It's conent-type is NOT the XML types |
| * we recognize(text/xml, application/xml, multipart/related;text/xml etc). |
| * |
| * This could be used to represent image/jpeg etc |
| */ |
| public static class UnknownContent extends AbstractMessageImpl implements MessageDataSource { |
| private final DataSource ds; |
| private final HeaderList headerList; |
| |
| public UnknownContent(final String ct, final InputStream in) { |
| this(createDataSource(ct,in)); |
| } |
| |
| public UnknownContent(DataSource ds) { |
| super(SOAPVersion.SOAP_11); |
| this.ds = ds; |
| this.headerList = new HeaderList(SOAPVersion.SOAP_11); |
| } |
| |
| /* |
| * Copy constructor. |
| */ |
| private UnknownContent(UnknownContent that) { |
| super(that.soapVersion); |
| this.ds = that.ds; |
| this.headerList = HeaderList.copy(that.headerList); |
| } |
| |
| public boolean hasUnconsumedDataSource() { |
| return true; |
| } |
| |
| public DataSource getDataSource() { |
| assert ds != null; |
| return ds; |
| } |
| |
| protected void writePayloadTo(ContentHandler contentHandler, |
| ErrorHandler errorHandler, boolean fragment) throws SAXException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public boolean hasHeaders() { |
| return false; |
| } |
| |
| public boolean isFault() { |
| return false; |
| } |
| |
| public MessageHeaders getHeaders() { |
| return headerList; |
| } |
| |
| public String getPayloadLocalPart() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public String getPayloadNamespaceURI() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public boolean hasPayload() { |
| return false; |
| } |
| |
| public Source readPayloadAsSource() { |
| return null; |
| } |
| |
| public XMLStreamReader readPayload() throws XMLStreamException { |
| throw new WebServiceException("There isn't XML payload. Shouldn't come here."); |
| } |
| |
| public void writePayloadTo(XMLStreamWriter sw) throws XMLStreamException { |
| // No XML. Nothing to do |
| } |
| |
| public Message copy() { |
| return new UnknownContent(this); |
| } |
| |
| } |
| |
| public static DataSource getDataSource(Message msg, WSFeatureList f) { |
| if (msg == null) |
| return null; |
| if (msg instanceof MessageDataSource) { |
| return ((MessageDataSource)msg).getDataSource(); |
| } else { |
| AttachmentSet atts = msg.getAttachments(); |
| if (atts != null && !atts.isEmpty()) { |
| final ByteArrayBuffer bos = new ByteArrayBuffer(); |
| try { |
| Codec codec = new XMLHTTPBindingCodec(f); |
| Packet packet = new Packet(msg); |
| com.sun.xml.internal.ws.api.pipe.ContentType ct = codec.getStaticContentType(packet); |
| codec.encode(packet, bos); |
| return createDataSource(ct.getContentType(), bos.newInputStream()); |
| } catch(IOException ioe) { |
| throw new WebServiceException(ioe); |
| } |
| |
| } else { |
| final ByteArrayBuffer bos = new ByteArrayBuffer(); |
| XMLStreamWriter writer = XMLStreamWriterFactory.create(bos); |
| try { |
| msg.writePayloadTo(writer); |
| writer.flush(); |
| } catch (XMLStreamException e) { |
| throw new WebServiceException(e); |
| } |
| return XMLMessage.createDataSource("text/xml", bos.newInputStream()); |
| } |
| } |
| } |
| |
| public static DataSource createDataSource(final String contentType, final InputStream is) { |
| return new XmlDataSource(contentType, is); |
| } |
| |
| private static class XmlDataSource implements DataSource { |
| private final String contentType; |
| private final InputStream is; |
| private boolean consumed; |
| |
| XmlDataSource(String contentType, final InputStream is) { |
| this.contentType = contentType; |
| this.is = is; |
| } |
| |
| public boolean consumed() { |
| return consumed; |
| } |
| |
| public InputStream getInputStream() { |
| consumed = !consumed; |
| return is; |
| } |
| |
| public OutputStream getOutputStream() { |
| return null; |
| } |
| |
| public String getContentType() { |
| return contentType; |
| } |
| |
| public String getName() { |
| return ""; |
| } |
| } |
| } |