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