Wed, 27 Apr 2016 01:27:09 +0800
Initial load
http://hg.openjdk.java.net/jdk8u/jdk8u/jaxws/
changeset: 657:d47a47f961ee
tag: jdk8u25-b17
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 */
26 package com.sun.xml.internal.ws.encoding;
28 import com.sun.xml.internal.ws.api.SOAPVersion;
29 import com.sun.xml.internal.ws.api.WSFeatureList;
30 import com.sun.xml.internal.ws.api.client.SelectOptimalEncodingFeature;
31 import com.sun.xml.internal.ws.api.fastinfoset.FastInfosetFeature;
32 import com.sun.xml.internal.ws.api.message.Message;
33 import com.sun.xml.internal.ws.api.message.Packet;
34 import com.sun.xml.internal.ws.api.message.ExceptionHasMessage;
35 import com.sun.xml.internal.ws.api.pipe.Codec;
36 import com.sun.xml.internal.ws.api.pipe.Codecs;
37 import com.sun.xml.internal.ws.api.pipe.ContentType;
38 import com.sun.xml.internal.ws.api.pipe.StreamSOAPCodec;
39 import com.sun.xml.internal.ws.client.ContentNegotiation;
40 import com.sun.xml.internal.ws.protocol.soap.MessageCreationException;
41 import com.sun.xml.internal.ws.resources.StreamingMessages;
42 import com.sun.xml.internal.ws.server.UnsupportedMediaException;
43 import static com.sun.xml.internal.ws.binding.WebServiceFeatureList.getSoapVersion;
45 import javax.xml.ws.WebServiceException;
46 import javax.xml.ws.WebServiceFeature;
47 import javax.xml.ws.soap.MTOMFeature;
48 import java.io.IOException;
49 import java.io.InputStream;
50 import java.io.OutputStream;
51 import java.lang.reflect.Method;
52 import java.nio.channels.ReadableByteChannel;
53 import java.nio.channels.WritableByteChannel;
54 //import java.util.StringTokenizer;
56 /**
57 * SOAP binding {@link Codec} that can handle MTOM, SwA, and SOAP messages
58 * encoded using XML or Fast Infoset.
59 *
60 * <p>
61 * This is used when we need to determine the encoding from what we received (for decoding)
62 * and from configuration and {@link Message} contents (for encoding)
63 *
64 * <p>
65 * TODO: Split this Codec into two, one that supports FI and one that does not.
66 * Then further split the FI Codec into two, one for client and one for
67 * server. This will simplify the logic and make it easier to understand/maintain.
68 *
69 * @author Vivek Pandey
70 * @author Kohsuke Kawaguchi
71 */
72 public class SOAPBindingCodec extends MimeCodec implements com.sun.xml.internal.ws.api.pipe.SOAPBindingCodec {
74 public static final String UTF8_ENCODING = "utf-8";
75 public static final String DEFAULT_ENCODING = UTF8_ENCODING;
78 /**
79 * True if Fast Infoset functionality has been
80 * configured to be disabled, or the Fast Infoset
81 * runtime is not available.
82 */
83 private boolean isFastInfosetDisabled;
85 /**
86 * True if the Fast Infoset codec should be used for encoding.
87 */
88 private boolean useFastInfosetForEncoding;
90 /**
91 * True if the content negotiation property should
92 * be ignored by the client. This will be used in
93 * the case of Fast Infoset being configured to be
94 * disabled or automatically selected.
95 */
96 private boolean ignoreContentNegotiationProperty;
98 // The XML SOAP codec
99 private final StreamSOAPCodec xmlSoapCodec;
101 // The Fast Infoset SOAP codec
102 private final Codec fiSoapCodec;
104 // The XML MTOM codec
105 private final MimeCodec xmlMtomCodec;
107 // The XML SWA codec
108 private final MimeCodec xmlSwaCodec;
110 // The Fast Infoset SWA codec
111 private final MimeCodec fiSwaCodec;
113 /**
114 * The XML SOAP MIME type
115 */
116 private final String xmlMimeType;
118 /**
119 * The Fast Infoset SOAP MIME type
120 */
121 private final String fiMimeType;
123 /**
124 * The Accept header for XML encodings
125 */
126 private final String xmlAccept;
128 /**
129 * The Accept header for Fast Infoset and XML encodings
130 */
131 private final String connegXmlAccept;
133 public StreamSOAPCodec getXMLCodec() {
134 return xmlSoapCodec;
135 }
137 private ContentTypeImpl setAcceptHeader(Packet p, ContentTypeImpl c) {
138 String _accept;
139 if (!ignoreContentNegotiationProperty && p.contentNegotiation != ContentNegotiation.none) {
140 _accept = connegXmlAccept;
141 } else {
142 _accept = xmlAccept;
143 }
144 c.setAcceptHeader(_accept);
145 return c;
146 }
148 public SOAPBindingCodec(WSFeatureList features) {
149 this(features, Codecs.createSOAPEnvelopeXmlCodec(features));
150 }
152 public SOAPBindingCodec(WSFeatureList features, StreamSOAPCodec xmlSoapCodec) {
153 super(getSoapVersion(features), features);
155 this.xmlSoapCodec = xmlSoapCodec;
156 xmlMimeType = xmlSoapCodec.getMimeType();
158 xmlMtomCodec = new MtomCodec(version, xmlSoapCodec, features);
160 xmlSwaCodec = new SwACodec(version, features, xmlSoapCodec);
162 String clientAcceptedContentTypes = xmlSoapCodec.getMimeType() + ", " +
163 xmlMtomCodec.getMimeType();
165 WebServiceFeature fi = features.get(FastInfosetFeature.class);
166 isFastInfosetDisabled = (fi != null && !fi.isEnabled());
167 if (!isFastInfosetDisabled) {
168 fiSoapCodec = getFICodec(xmlSoapCodec, version);
169 if (fiSoapCodec != null) {
170 fiMimeType = fiSoapCodec.getMimeType();
171 fiSwaCodec = new SwACodec(version, features, fiSoapCodec);
172 connegXmlAccept = fiMimeType + ", " + clientAcceptedContentTypes;
174 /**
175 * This feature will only be present on the client side.
176 *
177 * Fast Infoset is enabled on the client if the service
178 * explicitly supports Fast Infoset.
179 */
180 WebServiceFeature select = features.get(SelectOptimalEncodingFeature.class);
181 if (select != null) { // if the client FI feature is set - ignore negotiation property
182 ignoreContentNegotiationProperty = true;
183 if (select.isEnabled()) {
184 // If the client's FI encoding feature is enabled, and server's is not disabled
185 if (fi != null) { // if server's FI feature also enabled
186 useFastInfosetForEncoding = true;
187 }
189 clientAcceptedContentTypes = connegXmlAccept;
190 } else { // If client FI feature is disabled
191 isFastInfosetDisabled = true;
192 }
193 }
194 } else {
195 // Fast Infoset could not be loaded by the runtime
196 isFastInfosetDisabled = true;
197 fiSwaCodec = null;
198 fiMimeType = "";
199 connegXmlAccept = clientAcceptedContentTypes;
200 ignoreContentNegotiationProperty = true;
201 }
202 } else {
203 // Fast Infoset is explicitly not supported by the service
204 fiSoapCodec = fiSwaCodec = null;
205 fiMimeType = "";
206 connegXmlAccept = clientAcceptedContentTypes;
207 ignoreContentNegotiationProperty = true;
208 }
210 xmlAccept = clientAcceptedContentTypes;
212 if(getSoapVersion(features) == null)
213 throw new WebServiceException("Expecting a SOAP binding but found ");
214 }
216 public String getMimeType() {
217 return null;
218 }
220 public ContentType getStaticContentType(Packet packet) {
221 ContentType toAdapt = getEncoder(packet).getStaticContentType(packet);
222 return setAcceptHeader(packet, (ContentTypeImpl)toAdapt);
223 }
225 public ContentType encode(Packet packet, OutputStream out) throws IOException {
226 preEncode(packet);
227 ContentType ct = getEncoder(packet).encode(packet, out);
228 ct = setAcceptHeader(packet, (ContentTypeImpl)ct);
229 postEncode();
230 return ct;
231 }
233 public ContentType encode(Packet packet, WritableByteChannel buffer) {
234 preEncode(packet);
235 ContentType ct = getEncoder(packet).encode(packet, buffer);
236 ct = setAcceptHeader(packet, (ContentTypeImpl)ct);
237 postEncode();
238 return ct;
239 }
241 /**
242 * Should be called before encode().
243 * Set the state so that such state is used by encode process.
244 */
245 private void preEncode(Packet p) {
246 }
248 /**
249 * Should be called after encode()
250 * Reset the encoding state.
251 */
252 private void postEncode() {
253 }
255 /**
256 * Should be called before decode().
257 * Set the state so that such state is used by decode().
258 */
259 private void preDecode(Packet p) {
260 if (p.contentNegotiation == null)
261 useFastInfosetForEncoding = false;
262 }
264 /**
265 * Should be called after decode().
266 * Set the state so that such state is used by encode().
267 */
268 private void postDecode(Packet p) {
269 p.setFastInfosetDisabled(isFastInfosetDisabled);
270 if(features.isEnabled(MTOMFeature.class)) p.checkMtomAcceptable();
271 // p.setMtomAcceptable( isMtomAcceptable(p.acceptableMimeTypes) );
272 MTOMFeature mtomFeature = features.get(MTOMFeature.class);
273 if (mtomFeature != null) {
274 p.setMtomFeature(mtomFeature);
275 }
276 if (!useFastInfosetForEncoding) {
277 useFastInfosetForEncoding = p.getFastInfosetAcceptable(fiMimeType);
278 // useFastInfosetForEncoding = isFastInfosetAcceptable(p.acceptableMimeTypes);
279 }
280 }
282 public void decode(InputStream in, String contentType, Packet packet) throws IOException {
283 if (contentType == null) {
284 contentType = xmlMimeType;
285 }
286 packet.setContentType(new ContentTypeImpl(contentType));
287 preDecode(packet);
288 try {
289 if(isMultipartRelated(contentType))
290 // parse the multipart portion and then decide whether it's MTOM or SwA
291 super.decode(in, contentType, packet);
292 else if(isFastInfoset(contentType)) {
293 if (!ignoreContentNegotiationProperty && packet.contentNegotiation == ContentNegotiation.none)
294 throw noFastInfosetForDecoding();
296 useFastInfosetForEncoding = true;
297 fiSoapCodec.decode(in, contentType, packet);
298 } else
299 xmlSoapCodec.decode(in, contentType, packet);
300 } catch(RuntimeException we) {
301 if (we instanceof ExceptionHasMessage || we instanceof UnsupportedMediaException) {
302 throw we;
303 } else {
304 throw new MessageCreationException(version, we);
305 }
306 }
307 postDecode(packet);
308 }
310 public void decode(ReadableByteChannel in, String contentType, Packet packet) {
311 if (contentType == null) {
312 throw new UnsupportedMediaException();
313 }
315 preDecode(packet);
316 try {
317 if(isMultipartRelated(contentType))
318 super.decode(in, contentType, packet);
319 else if(isFastInfoset(contentType)) {
320 if (packet.contentNegotiation == ContentNegotiation.none)
321 throw noFastInfosetForDecoding();
323 useFastInfosetForEncoding = true;
324 fiSoapCodec.decode(in, contentType, packet);
325 } else
326 xmlSoapCodec.decode(in, contentType, packet);
327 } catch(RuntimeException we) {
328 if (we instanceof ExceptionHasMessage || we instanceof UnsupportedMediaException) {
329 throw we;
330 } else {
331 throw new MessageCreationException(version, we);
332 }
333 }
334 postDecode(packet);
335 }
337 public SOAPBindingCodec copy() {
338 return new SOAPBindingCodec(features, (StreamSOAPCodec)xmlSoapCodec.copy());
339 }
341 @Override
342 protected void decode(MimeMultipartParser mpp, Packet packet) throws IOException {
343 // is this SwA or XOP?
344 final String rootContentType = mpp.getRootPart().getContentType();
345 boolean isMTOM = isApplicationXopXml(rootContentType);
346 packet.setMtomRequest(isMTOM);
347 if(isMTOM) {
348 xmlMtomCodec.decode(mpp,packet);
349 } else if (isFastInfoset(rootContentType)) {
350 if (packet.contentNegotiation == ContentNegotiation.none)
351 throw noFastInfosetForDecoding();
353 useFastInfosetForEncoding = true;
354 fiSwaCodec.decode(mpp,packet);
355 } else if (isXml(rootContentType))
356 xmlSwaCodec.decode(mpp,packet);
357 else {
358 // TODO localize exception
359 throw new IOException("");
360 }
361 // checkDuplicateKnownHeaders(packet);
362 }
364 private boolean isMultipartRelated(String contentType) {
365 return compareStrings(contentType, MimeCodec.MULTIPART_RELATED_MIME_TYPE);
366 }
368 private boolean isApplicationXopXml(String contentType) {
369 return compareStrings(contentType, MtomCodec.XOP_XML_MIME_TYPE);
370 }
372 private boolean isXml(String contentType) {
373 return compareStrings(contentType, xmlMimeType);
374 }
376 private boolean isFastInfoset(String contentType) {
377 if (isFastInfosetDisabled) return false;
379 return compareStrings(contentType, fiMimeType);
380 }
382 private boolean compareStrings(String a, String b) {
383 return a.length() >= b.length() &&
384 b.equalsIgnoreCase(
385 a.substring(0,
386 b.length()));
387 }
389 // private boolean isFastInfosetAcceptable(String accept) {
390 // if (accept == null || isFastInfosetDisabled) return false;
391 //
392 // StringTokenizer st = new StringTokenizer(accept, ",");
393 // while (st.hasMoreTokens()) {
394 // final String token = st.nextToken().trim();
395 // if (token.equalsIgnoreCase(fiMimeType)) {
396 // return true;
397 // }
398 // }
399 // return false;
400 // }
402 /*
403 * Just check if the Accept header contains application/xop+xml,
404 * no need to worry about q values.
405 */
406 // private boolean isMtomAcceptable(String accept) {
407 // if (accept == null || isFastInfosetDisabled) return false;
408 // StringTokenizer st = new StringTokenizer(accept, ",");
409 // while (st.hasMoreTokens()) {
410 // final String token = st.nextToken().trim();
411 // if (token.toLowerCase().contains(MtomCodec.XOP_XML_MIME_TYPE)) {
412 // return true;
413 // }
414 // }
415 // return false;
416 // }
418 /**
419 * Determines the encoding codec.
420 */
421 private Codec getEncoder(Packet p) {
422 /**
423 * The following logic is only for outbound packets
424 * to be encoded by a client.
425 * For a server the p.contentNegotiation == null.
426 */
427 if (!ignoreContentNegotiationProperty) {
428 if (p.contentNegotiation == ContentNegotiation.none) {
429 // The client may have changed the negotiation property from
430 // pessismistic to none between invocations
431 useFastInfosetForEncoding = false;
432 } else if (p.contentNegotiation == ContentNegotiation.optimistic) {
433 // Always encode using Fast Infoset if in optimisitic mode
434 useFastInfosetForEncoding = true;
435 }
436 }
438 // Override the MTOM binding for now
439 // Note: Using FI with MTOM does not make sense
440 if (useFastInfosetForEncoding) {
441 final Message m = p.getMessage();
442 if(m==null || m.getAttachments().isEmpty() || features.isEnabled(MTOMFeature.class))
443 return fiSoapCodec;
444 else
445 return fiSwaCodec;
446 }
448 //If the packet does not have a binding, explicitly set the MTOMFeature
449 //on the packet so that it has a way to determine whether to use MTOM
450 if (p.getBinding() == null) {
451 if (features != null) {
452 p.setMtomFeature(features.get(MTOMFeature.class));
453 }
454 }
456 if (p.shouldUseMtom()) {
457 return xmlMtomCodec;
458 }
460 Message m = p.getMessage();
461 if(m==null || m.getAttachments().isEmpty())
462 return xmlSoapCodec;
463 else
464 return xmlSwaCodec;
465 }
467 private RuntimeException noFastInfosetForDecoding() {
468 return new RuntimeException(StreamingMessages.FASTINFOSET_DECODING_NOT_ACCEPTED());
469 }
471 /**
472 * Obtain an FI SOAP codec instance using reflection.
473 */
474 private static Codec getFICodec(StreamSOAPCodec soapCodec, SOAPVersion version) {
475 try {
476 Class c = Class.forName("com.sun.xml.internal.ws.encoding.fastinfoset.FastInfosetStreamSOAPCodec");
477 Method m = c.getMethod("create", StreamSOAPCodec.class, SOAPVersion.class);
478 return (Codec)m.invoke(null, soapCodec, version);
479 } catch (Exception e) {
480 // TODO Log that FI cannot be loaded
481 return null;
482 }
483 }
484 }