src/share/jaxws_classes/com/sun/xml/internal/ws/encoding/SOAPBindingCodec.java

Thu, 31 Aug 2017 15:18:52 +0800

author
aoqi
date
Thu, 31 Aug 2017 15:18:52 +0800
changeset 637
9c07ef4934dd
parent 368
0989ad8c0860
parent 0
373ffda63c9a
permissions
-rw-r--r--

merge

     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 }

mercurial