aoqi@0: /* aoqi@0: * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.xml.internal.ws.encoding; aoqi@0: aoqi@0: import static com.sun.xml.internal.ws.binding.WebServiceFeatureList.getSoapVersion; aoqi@0: aoqi@0: import com.oracle.webservices.internal.impl.encoding.StreamDecoderImpl; aoqi@0: import com.oracle.webservices.internal.impl.internalspi.encoding.StreamDecoder; aoqi@0: import com.sun.istack.internal.NotNull; aoqi@0: import com.sun.istack.internal.Nullable; aoqi@0: import com.sun.xml.internal.stream.buffer.MutableXMLStreamBuffer; aoqi@0: import com.sun.xml.internal.stream.buffer.XMLStreamBuffer; aoqi@0: import com.sun.xml.internal.stream.buffer.XMLStreamBufferMark; aoqi@0: import com.sun.xml.internal.stream.buffer.stax.StreamReaderBufferCreator; aoqi@0: import com.sun.xml.internal.ws.api.SOAPVersion; aoqi@0: import com.sun.xml.internal.ws.api.WSBinding; aoqi@0: import com.sun.xml.internal.ws.api.WSFeatureList; aoqi@0: import com.sun.xml.internal.ws.api.message.AttachmentSet; aoqi@0: import com.sun.xml.internal.ws.api.message.Header; aoqi@0: import com.sun.xml.internal.ws.api.message.HeaderList; aoqi@0: import com.sun.xml.internal.ws.api.message.Message; aoqi@0: import com.sun.xml.internal.ws.api.message.Packet; aoqi@0: import com.sun.xml.internal.ws.api.pipe.ContentType; aoqi@0: import com.sun.xml.internal.ws.api.streaming.XMLStreamWriterFactory; aoqi@0: import com.sun.xml.internal.ws.developer.SerializationFeature; aoqi@0: import com.sun.xml.internal.ws.message.AttachmentSetImpl; aoqi@0: import com.sun.xml.internal.ws.message.stream.StreamMessage; aoqi@0: import com.sun.xml.internal.ws.protocol.soap.VersionMismatchException; aoqi@0: import com.sun.xml.internal.ws.server.UnsupportedMediaException; aoqi@0: import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil; aoqi@0: import com.sun.xml.internal.ws.util.ServiceFinder; aoqi@0: aoqi@0: import javax.xml.stream.XMLStreamConstants; aoqi@0: import javax.xml.stream.XMLStreamException; aoqi@0: import javax.xml.stream.XMLStreamReader; aoqi@0: import javax.xml.stream.XMLStreamWriter; aoqi@0: import javax.xml.ws.WebServiceException; aoqi@0: import java.io.IOException; aoqi@0: import java.io.InputStream; aoqi@0: import java.io.OutputStream; aoqi@0: import java.nio.channels.ReadableByteChannel; aoqi@0: import java.nio.channels.WritableByteChannel; aoqi@0: import java.nio.charset.Charset; aoqi@0: import java.util.HashMap; aoqi@0: import java.util.List; aoqi@0: import java.util.Map; aoqi@0: aoqi@0: /** aoqi@0: * A stream SOAP codec. aoqi@0: * aoqi@0: * @author Paul Sandoz aoqi@0: */ aoqi@0: @SuppressWarnings({"StringEquality"}) aoqi@0: public abstract class StreamSOAPCodec implements com.sun.xml.internal.ws.api.pipe.StreamSOAPCodec, RootOnlyCodec { aoqi@0: aoqi@0: private static final String SOAP_ENVELOPE = "Envelope"; aoqi@0: private static final String SOAP_HEADER = "Header"; aoqi@0: private static final String SOAP_BODY = "Body"; aoqi@0: aoqi@0: private final SOAPVersion soapVersion; aoqi@0: protected final SerializationFeature serializationFeature; aoqi@0: aoqi@0: private final StreamDecoder streamDecoder; aoqi@0: aoqi@0: // charset of last decoded message. Will be used for encoding server's aoqi@0: // response messages with the request message's encoding aoqi@0: // it will stored in the packet.invocationProperties aoqi@0: private final static String DECODED_MESSAGE_CHARSET = "decodedMessageCharset"; aoqi@0: aoqi@0: /*package*/ StreamSOAPCodec(SOAPVersion soapVersion) { aoqi@0: this(soapVersion, null); aoqi@0: } aoqi@0: aoqi@0: /*package*/ StreamSOAPCodec(WSBinding binding) { aoqi@0: this(binding.getSOAPVersion(), binding.getFeature(SerializationFeature.class)); aoqi@0: } aoqi@0: aoqi@0: StreamSOAPCodec(WSFeatureList features) { aoqi@0: this(getSoapVersion(features), features.get(SerializationFeature.class)); aoqi@0: } aoqi@0: aoqi@0: private StreamSOAPCodec(SOAPVersion soapVersion, @Nullable SerializationFeature sf) { aoqi@0: this.soapVersion = soapVersion; aoqi@0: this.serializationFeature = sf; aoqi@0: this.streamDecoder = selectStreamDecoder(); aoqi@0: } aoqi@0: aoqi@0: private StreamDecoder selectStreamDecoder() { aoqi@0: for (StreamDecoder sd : ServiceFinder.find(StreamDecoder.class)) { aoqi@0: return sd; aoqi@0: } aoqi@0: aoqi@0: return new StreamDecoderImpl(); aoqi@0: } aoqi@0: aoqi@0: public ContentType getStaticContentType(Packet packet) { aoqi@0: return getContentType(packet); aoqi@0: } aoqi@0: aoqi@0: public ContentType encode(Packet packet, OutputStream out) { aoqi@0: if (packet.getMessage() != null) { aoqi@0: String encoding = getPacketEncoding(packet); aoqi@0: packet.invocationProperties.remove(DECODED_MESSAGE_CHARSET); aoqi@0: XMLStreamWriter writer = XMLStreamWriterFactory.create(out, encoding); aoqi@0: try { aoqi@0: packet.getMessage().writeTo(writer); aoqi@0: writer.flush(); aoqi@0: } catch (XMLStreamException e) { aoqi@0: throw new WebServiceException(e); aoqi@0: } aoqi@0: XMLStreamWriterFactory.recycle(writer); aoqi@0: } aoqi@0: return getContentType(packet); aoqi@0: } aoqi@0: aoqi@0: protected abstract ContentType getContentType(Packet packet); aoqi@0: aoqi@0: protected abstract String getDefaultContentType(); aoqi@0: aoqi@0: public ContentType encode(Packet packet, WritableByteChannel buffer) { aoqi@0: //TODO: not yet implemented aoqi@0: throw new UnsupportedOperationException(); aoqi@0: } aoqi@0: aoqi@0: protected abstract List getExpectedContentTypes(); aoqi@0: aoqi@0: public void decode(InputStream in, String contentType, Packet packet) throws IOException { aoqi@0: decode(in, contentType, packet, new AttachmentSetImpl()); aoqi@0: } aoqi@0: aoqi@0: /* aoqi@0: * Checks against expected Content-Type headers that is handled by a codec aoqi@0: * aoqi@0: * @param ct the Content-Type of the request aoqi@0: * @param expected expected Content-Types for a codec aoqi@0: * @return true if the codec supports this Content-Type aoqi@0: * false otherwise aoqi@0: */ aoqi@0: private static boolean isContentTypeSupported(String ct, List expected) { aoqi@0: for(String contentType : expected) { aoqi@0: if (ct.contains(contentType)) { aoqi@0: return true; aoqi@0: } aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Decodes a message from {@link XMLStreamReader} that points to aoqi@0: * the beginning of a SOAP infoset. aoqi@0: * aoqi@0: * @param reader aoqi@0: * can point to the start document or the start element. aoqi@0: */ aoqi@0: public final @NotNull Message decode(@NotNull XMLStreamReader reader) { aoqi@0: return decode(reader,new AttachmentSetImpl()); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Decodes a message from {@link XMLStreamReader} that points to aoqi@0: * the beginning of a SOAP infoset. aoqi@0: * aoqi@0: * @param reader aoqi@0: * can point to the start document or the start element. aoqi@0: * @param attachmentSet aoqi@0: * {@link StreamSOAPCodec} can take attachments parsed outside, aoqi@0: * so that this codec can be used as a part of a biggre codec aoqi@0: * (like MIME multipart codec.) aoqi@0: */ aoqi@0: public final Message decode(XMLStreamReader reader, @NotNull AttachmentSet attachmentSet) { aoqi@0: return decode(soapVersion, reader, attachmentSet); aoqi@0: } aoqi@0: aoqi@0: public static final Message decode(SOAPVersion soapVersion, XMLStreamReader reader, @NotNull AttachmentSet attachmentSet) { aoqi@0: // Move to soap:Envelope and verify aoqi@0: if(reader.getEventType()!=XMLStreamConstants.START_ELEMENT) aoqi@0: XMLStreamReaderUtil.nextElementContent(reader); aoqi@0: XMLStreamReaderUtil.verifyReaderState(reader,XMLStreamConstants.START_ELEMENT); aoqi@0: if (SOAP_ENVELOPE.equals(reader.getLocalName()) && !soapVersion.nsUri.equals(reader.getNamespaceURI())) { aoqi@0: throw new VersionMismatchException(soapVersion, soapVersion.nsUri, reader.getNamespaceURI()); aoqi@0: } aoqi@0: XMLStreamReaderUtil.verifyTag(reader, soapVersion.nsUri, SOAP_ENVELOPE); aoqi@0: return new StreamMessage(soapVersion, reader, attachmentSet); aoqi@0: } aoqi@0: aoqi@0: public void decode(ReadableByteChannel in, String contentType, Packet packet ) { aoqi@0: throw new UnsupportedOperationException(); aoqi@0: } aoqi@0: aoqi@0: public final StreamSOAPCodec copy() { aoqi@0: return this; aoqi@0: } aoqi@0: aoqi@0: public void decode(InputStream in, String contentType, Packet packet, AttachmentSet att ) throws IOException { aoqi@0: List expectedContentTypes = getExpectedContentTypes(); aoqi@0: if (contentType != null && !isContentTypeSupported(contentType,expectedContentTypes)) { aoqi@0: throw new UnsupportedMediaException(contentType, expectedContentTypes); aoqi@0: } aoqi@0: com.oracle.webservices.internal.api.message.ContentType pct = packet.getInternalContentType(); aoqi@0: ContentTypeImpl cti = (pct != null && pct instanceof ContentTypeImpl) ? aoqi@0: (ContentTypeImpl)pct : new ContentTypeImpl(contentType); aoqi@0: String charset = cti.getCharSet(); aoqi@0: if (charset != null && !Charset.isSupported(charset)) { aoqi@0: throw new UnsupportedMediaException(charset); aoqi@0: } aoqi@0: if (charset != null) { aoqi@0: packet.invocationProperties.put(DECODED_MESSAGE_CHARSET, charset); aoqi@0: } else { aoqi@0: packet.invocationProperties.remove(DECODED_MESSAGE_CHARSET); aoqi@0: } aoqi@0: packet.setMessage(streamDecoder.decode(in, charset, att, soapVersion)); aoqi@0: } aoqi@0: aoqi@0: public void decode(ReadableByteChannel in, String contentType, Packet response, AttachmentSet att ) { aoqi@0: throw new UnsupportedOperationException(); aoqi@0: } aoqi@0: aoqi@0: /* aoqi@0: * Creates a new {@link StreamSOAPCodec} instance. aoqi@0: */ aoqi@0: public static StreamSOAPCodec create(SOAPVersion version) { aoqi@0: if(version==null) aoqi@0: // this decoder is for SOAP, not for XML/HTTP aoqi@0: throw new IllegalArgumentException(); aoqi@0: switch(version) { aoqi@0: case SOAP_11: aoqi@0: return new StreamSOAP11Codec(); aoqi@0: case SOAP_12: aoqi@0: return new StreamSOAP12Codec(); aoqi@0: default: aoqi@0: throw new AssertionError(); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /* aoqi@0: * Creates a new {@link StreamSOAPCodec} instance using binding aoqi@0: */ aoqi@0: public static StreamSOAPCodec create(WSFeatureList features) { aoqi@0: SOAPVersion version = getSoapVersion(features); aoqi@0: if(version==null) aoqi@0: // this decoder is for SOAP, not for XML/HTTP aoqi@0: throw new IllegalArgumentException(); aoqi@0: switch(version) { aoqi@0: case SOAP_11: aoqi@0: return new StreamSOAP11Codec(features); aoqi@0: case SOAP_12: aoqi@0: return new StreamSOAP12Codec(features); aoqi@0: default: aoqi@0: throw new AssertionError(); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Creates a new {@link StreamSOAPCodec} instance using binding aoqi@0: * aoqi@0: * @deprecated use {@link #create(WSFeatureList)} aoqi@0: */ aoqi@0: public static StreamSOAPCodec create(WSBinding binding) { aoqi@0: SOAPVersion version = binding.getSOAPVersion(); aoqi@0: if(version==null) aoqi@0: // this decoder is for SOAP, not for XML/HTTP aoqi@0: throw new IllegalArgumentException(); aoqi@0: switch(version) { aoqi@0: case SOAP_11: aoqi@0: return new StreamSOAP11Codec(binding); aoqi@0: case SOAP_12: aoqi@0: return new StreamSOAP12Codec(binding); aoqi@0: default: aoqi@0: throw new AssertionError(); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private String getPacketEncoding(Packet packet) { aoqi@0: // If SerializationFeature is set, just use that encoding aoqi@0: if (serializationFeature != null && serializationFeature.getEncoding() != null) { aoqi@0: return serializationFeature.getEncoding().equals("") aoqi@0: ? SOAPBindingCodec.DEFAULT_ENCODING : serializationFeature.getEncoding(); aoqi@0: } aoqi@0: aoqi@0: if (packet != null && packet.endpoint != null) { aoqi@0: // Use request message's encoding for Server-side response messages aoqi@0: String charset = (String)packet.invocationProperties.get(DECODED_MESSAGE_CHARSET); aoqi@0: return charset == null aoqi@0: ? SOAPBindingCodec.DEFAULT_ENCODING : charset; aoqi@0: } aoqi@0: aoqi@0: // Use default encoding for client-side request messages aoqi@0: return SOAPBindingCodec.DEFAULT_ENCODING; aoqi@0: } aoqi@0: aoqi@0: protected ContentTypeImpl.Builder getContenTypeBuilder(Packet packet) { aoqi@0: ContentTypeImpl.Builder b = new ContentTypeImpl.Builder(); aoqi@0: String encoding = getPacketEncoding(packet); aoqi@0: if (SOAPBindingCodec.DEFAULT_ENCODING.equalsIgnoreCase(encoding)) { aoqi@0: b.contentType = getDefaultContentType(); aoqi@0: b.charset = SOAPBindingCodec.DEFAULT_ENCODING; aoqi@0: return b; aoqi@0: } aoqi@0: b.contentType = getMimeType()+" ;charset="+encoding; aoqi@0: b.charset = encoding; aoqi@0: return b; aoqi@0: } aoqi@0: aoqi@0: }