1/* 2 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package com.sun.xml.internal.ws.encoding; 27 28import static com.sun.xml.internal.ws.binding.WebServiceFeatureList.getSoapVersion; 29 30import com.oracle.webservices.internal.impl.encoding.StreamDecoderImpl; 31import com.oracle.webservices.internal.impl.internalspi.encoding.StreamDecoder; 32import com.sun.istack.internal.NotNull; 33import com.sun.istack.internal.Nullable; 34import com.sun.xml.internal.stream.buffer.MutableXMLStreamBuffer; 35import com.sun.xml.internal.stream.buffer.XMLStreamBuffer; 36import com.sun.xml.internal.stream.buffer.XMLStreamBufferMark; 37import com.sun.xml.internal.stream.buffer.stax.StreamReaderBufferCreator; 38import com.sun.xml.internal.ws.api.SOAPVersion; 39import com.sun.xml.internal.ws.api.WSBinding; 40import com.sun.xml.internal.ws.api.WSFeatureList; 41import com.sun.xml.internal.ws.api.message.AttachmentSet; 42import com.sun.xml.internal.ws.api.message.Header; 43import com.sun.xml.internal.ws.api.message.HeaderList; 44import com.sun.xml.internal.ws.api.message.Message; 45import com.sun.xml.internal.ws.api.message.Packet; 46import com.sun.xml.internal.ws.api.pipe.ContentType; 47import com.sun.xml.internal.ws.api.streaming.XMLStreamWriterFactory; 48import com.sun.xml.internal.ws.developer.SerializationFeature; 49import com.sun.xml.internal.ws.message.AttachmentSetImpl; 50import com.sun.xml.internal.ws.message.stream.StreamMessage; 51import com.sun.xml.internal.ws.protocol.soap.VersionMismatchException; 52import com.sun.xml.internal.ws.server.UnsupportedMediaException; 53import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil; 54import com.sun.xml.internal.ws.util.ServiceFinder; 55 56import javax.xml.stream.XMLStreamConstants; 57import javax.xml.stream.XMLStreamException; 58import javax.xml.stream.XMLStreamReader; 59import javax.xml.stream.XMLStreamWriter; 60import javax.xml.ws.WebServiceException; 61import java.io.IOException; 62import java.io.InputStream; 63import java.io.OutputStream; 64import java.nio.channels.ReadableByteChannel; 65import java.nio.channels.WritableByteChannel; 66import java.nio.charset.Charset; 67import java.util.HashMap; 68import java.util.List; 69import java.util.Map; 70 71/** 72 * A stream SOAP codec. 73 * 74 * @author Paul Sandoz 75 */ 76@SuppressWarnings({"StringEquality"}) 77public abstract class StreamSOAPCodec implements com.sun.xml.internal.ws.api.pipe.StreamSOAPCodec, RootOnlyCodec { 78 79 private static final String SOAP_ENVELOPE = "Envelope"; 80 private static final String SOAP_HEADER = "Header"; 81 private static final String SOAP_BODY = "Body"; 82 83 private final SOAPVersion soapVersion; 84 protected final SerializationFeature serializationFeature; 85 86 private final StreamDecoder streamDecoder; 87 88 // charset of last decoded message. Will be used for encoding server's 89 // response messages with the request message's encoding 90 // it will stored in the packet.invocationProperties 91 private final static String DECODED_MESSAGE_CHARSET = "decodedMessageCharset"; 92 93 /*package*/ StreamSOAPCodec(SOAPVersion soapVersion) { 94 this(soapVersion, null); 95 } 96 97 /*package*/ StreamSOAPCodec(WSBinding binding) { 98 this(binding.getSOAPVersion(), binding.getFeature(SerializationFeature.class)); 99 } 100 101 StreamSOAPCodec(WSFeatureList features) { 102 this(getSoapVersion(features), features.get(SerializationFeature.class)); 103 } 104 105 private StreamSOAPCodec(SOAPVersion soapVersion, @Nullable SerializationFeature sf) { 106 this.soapVersion = soapVersion; 107 this.serializationFeature = sf; 108 this.streamDecoder = selectStreamDecoder(); 109 } 110 111 private StreamDecoder selectStreamDecoder() { 112 for (StreamDecoder sd : ServiceFinder.find(StreamDecoder.class)) { 113 return sd; 114 } 115 116 return new StreamDecoderImpl(); 117 } 118 119 public ContentType getStaticContentType(Packet packet) { 120 return getContentType(packet); 121 } 122 123 public ContentType encode(Packet packet, OutputStream out) { 124 if (packet.getMessage() != null) { 125 String encoding = getPacketEncoding(packet); 126 packet.invocationProperties.remove(DECODED_MESSAGE_CHARSET); 127 XMLStreamWriter writer = XMLStreamWriterFactory.create(out, encoding); 128 try { 129 packet.getMessage().writeTo(writer); 130 writer.flush(); 131 } catch (XMLStreamException e) { 132 throw new WebServiceException(e); 133 } 134 XMLStreamWriterFactory.recycle(writer); 135 } 136 return getContentType(packet); 137 } 138 139 protected abstract ContentType getContentType(Packet packet); 140 141 protected abstract String getDefaultContentType(); 142 143 public ContentType encode(Packet packet, WritableByteChannel buffer) { 144 //TODO: not yet implemented 145 throw new UnsupportedOperationException(); 146 } 147 148 protected abstract List<String> getExpectedContentTypes(); 149 150 public void decode(InputStream in, String contentType, Packet packet) throws IOException { 151 decode(in, contentType, packet, new AttachmentSetImpl()); 152 } 153 154 /* 155 * Checks against expected Content-Type headers that is handled by a codec 156 * 157 * @param ct the Content-Type of the request 158 * @param expected expected Content-Types for a codec 159 * @return true if the codec supports this Content-Type 160 * false otherwise 161 */ 162 private static boolean isContentTypeSupported(String ct, List<String> expected) { 163 for(String contentType : expected) { 164 if (ct.contains(contentType)) { 165 return true; 166 } 167 } 168 return false; 169 } 170 171 /** 172 * Decodes a message from {@link XMLStreamReader} that points to 173 * the beginning of a SOAP infoset. 174 * 175 * @param reader 176 * can point to the start document or the start element. 177 */ 178 public final @NotNull Message decode(@NotNull XMLStreamReader reader) { 179 return decode(reader,new AttachmentSetImpl()); 180 } 181 182 /** 183 * Decodes a message from {@link XMLStreamReader} that points to 184 * the beginning of a SOAP infoset. 185 * 186 * @param reader 187 * can point to the start document or the start element. 188 * @param attachmentSet 189 * {@link StreamSOAPCodec} can take attachments parsed outside, 190 * so that this codec can be used as a part of a biggre codec 191 * (like MIME multipart codec.) 192 */ 193 public final Message decode(XMLStreamReader reader, @NotNull AttachmentSet attachmentSet) { 194 return decode(soapVersion, reader, attachmentSet); 195 } 196 197 public static final Message decode(SOAPVersion soapVersion, XMLStreamReader reader, @NotNull AttachmentSet attachmentSet) { 198 // Move to soap:Envelope and verify 199 if(reader.getEventType()!=XMLStreamConstants.START_ELEMENT) 200 XMLStreamReaderUtil.nextElementContent(reader); 201 XMLStreamReaderUtil.verifyReaderState(reader,XMLStreamConstants.START_ELEMENT); 202 if (SOAP_ENVELOPE.equals(reader.getLocalName()) && !soapVersion.nsUri.equals(reader.getNamespaceURI())) { 203 throw new VersionMismatchException(soapVersion, soapVersion.nsUri, reader.getNamespaceURI()); 204 } 205 XMLStreamReaderUtil.verifyTag(reader, soapVersion.nsUri, SOAP_ENVELOPE); 206 return new StreamMessage(soapVersion, reader, attachmentSet); 207 } 208 209 public void decode(ReadableByteChannel in, String contentType, Packet packet ) { 210 throw new UnsupportedOperationException(); 211 } 212 213 public final StreamSOAPCodec copy() { 214 return this; 215 } 216 217 public void decode(InputStream in, String contentType, Packet packet, AttachmentSet att ) throws IOException { 218 List<String> expectedContentTypes = getExpectedContentTypes(); 219 if (contentType != null && !isContentTypeSupported(contentType,expectedContentTypes)) { 220 throw new UnsupportedMediaException(contentType, expectedContentTypes); 221 } 222 com.oracle.webservices.internal.api.message.ContentType pct = packet.getInternalContentType(); 223 ContentTypeImpl cti = (pct != null && pct instanceof ContentTypeImpl) ? 224 (ContentTypeImpl)pct : new ContentTypeImpl(contentType); 225 String charset = cti.getCharSet(); 226 if (charset != null && !Charset.isSupported(charset)) { 227 throw new UnsupportedMediaException(charset); 228 } 229 if (charset != null) { 230 packet.invocationProperties.put(DECODED_MESSAGE_CHARSET, charset); 231 } else { 232 packet.invocationProperties.remove(DECODED_MESSAGE_CHARSET); 233 } 234 packet.setMessage(streamDecoder.decode(in, charset, att, soapVersion)); 235 } 236 237 public void decode(ReadableByteChannel in, String contentType, Packet response, AttachmentSet att ) { 238 throw new UnsupportedOperationException(); 239 } 240 241 /* 242 * Creates a new {@link StreamSOAPCodec} instance. 243 */ 244 public static StreamSOAPCodec create(SOAPVersion version) { 245 if(version==null) 246 // this decoder is for SOAP, not for XML/HTTP 247 throw new IllegalArgumentException(); 248 switch(version) { 249 case SOAP_11: 250 return new StreamSOAP11Codec(); 251 case SOAP_12: 252 return new StreamSOAP12Codec(); 253 default: 254 throw new AssertionError(); 255 } 256 } 257 258 /* 259 * Creates a new {@link StreamSOAPCodec} instance using binding 260 */ 261 public static StreamSOAPCodec create(WSFeatureList features) { 262 SOAPVersion version = getSoapVersion(features); 263 if(version==null) 264 // this decoder is for SOAP, not for XML/HTTP 265 throw new IllegalArgumentException(); 266 switch(version) { 267 case SOAP_11: 268 return new StreamSOAP11Codec(features); 269 case SOAP_12: 270 return new StreamSOAP12Codec(features); 271 default: 272 throw new AssertionError(); 273 } 274 } 275 276 /** 277 * Creates a new {@link StreamSOAPCodec} instance using binding 278 * 279 * @deprecated use {@link #create(WSFeatureList)} 280 */ 281 public static StreamSOAPCodec create(WSBinding binding) { 282 SOAPVersion version = binding.getSOAPVersion(); 283 if(version==null) 284 // this decoder is for SOAP, not for XML/HTTP 285 throw new IllegalArgumentException(); 286 switch(version) { 287 case SOAP_11: 288 return new StreamSOAP11Codec(binding); 289 case SOAP_12: 290 return new StreamSOAP12Codec(binding); 291 default: 292 throw new AssertionError(); 293 } 294 } 295 296 private String getPacketEncoding(Packet packet) { 297 // If SerializationFeature is set, just use that encoding 298 if (serializationFeature != null && serializationFeature.getEncoding() != null) { 299 return serializationFeature.getEncoding().equals("") 300 ? SOAPBindingCodec.DEFAULT_ENCODING : serializationFeature.getEncoding(); 301 } 302 303 if (packet != null && packet.endpoint != null) { 304 // Use request message's encoding for Server-side response messages 305 String charset = (String)packet.invocationProperties.get(DECODED_MESSAGE_CHARSET); 306 return charset == null 307 ? SOAPBindingCodec.DEFAULT_ENCODING : charset; 308 } 309 310 // Use default encoding for client-side request messages 311 return SOAPBindingCodec.DEFAULT_ENCODING; 312 } 313 314 protected ContentTypeImpl.Builder getContenTypeBuilder(Packet packet) { 315 ContentTypeImpl.Builder b = new ContentTypeImpl.Builder(); 316 String encoding = getPacketEncoding(packet); 317 if (SOAPBindingCodec.DEFAULT_ENCODING.equalsIgnoreCase(encoding)) { 318 b.contentType = getDefaultContentType(); 319 b.charset = SOAPBindingCodec.DEFAULT_ENCODING; 320 return b; 321 } 322 b.contentType = getMimeType()+" ;charset="+encoding; 323 b.charset = encoding; 324 return b; 325 } 326 327} 328