1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/jaxws_classes/com/sun/xml/internal/ws/encoding/XMLHTTPBindingCodec.java Tue Mar 06 16:09:35 2012 -0800 1.3 @@ -0,0 +1,360 @@ 1.4 +/* 1.5 + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. Oracle designates this 1.11 + * particular file as subject to the "Classpath" exception as provided 1.12 + * by Oracle in the LICENSE file that accompanied this code. 1.13 + * 1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.17 + * version 2 for more details (a copy is included in the LICENSE file that 1.18 + * accompanied this code). 1.19 + * 1.20 + * You should have received a copy of the GNU General Public License version 1.21 + * 2 along with this work; if not, write to the Free Software Foundation, 1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.23 + * 1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.25 + * or visit www.oracle.com if you need additional information or have any 1.26 + * questions. 1.27 + */ 1.28 + 1.29 +package com.sun.xml.internal.ws.encoding; 1.30 + 1.31 +import com.sun.xml.internal.ws.api.SOAPVersion; 1.32 +import com.sun.xml.internal.ws.api.WSFeatureList; 1.33 +import com.sun.xml.internal.ws.api.message.Packet; 1.34 +import com.sun.xml.internal.ws.api.pipe.Codec; 1.35 +import com.sun.xml.internal.ws.api.pipe.ContentType; 1.36 +import com.sun.xml.internal.ws.client.ContentNegotiation; 1.37 +import com.sun.xml.internal.ws.encoding.xml.XMLCodec; 1.38 +import com.sun.xml.internal.ws.encoding.xml.XMLMessage; 1.39 +import com.sun.xml.internal.ws.encoding.xml.XMLMessage.MessageDataSource; 1.40 +import com.sun.xml.internal.ws.encoding.xml.XMLMessage.UnknownContent; 1.41 +import com.sun.xml.internal.ws.encoding.xml.XMLMessage.XMLMultiPart; 1.42 +import com.sun.xml.internal.ws.resources.StreamingMessages; 1.43 +import com.sun.xml.internal.ws.util.ByteArrayBuffer; 1.44 + 1.45 +import javax.activation.DataSource; 1.46 +import javax.xml.ws.WebServiceException; 1.47 +import javax.xml.ws.WebServiceFeature; 1.48 + 1.49 +import java.io.IOException; 1.50 +import java.io.InputStream; 1.51 +import java.io.OutputStream; 1.52 +import java.lang.reflect.Method; 1.53 +import java.nio.channels.WritableByteChannel; 1.54 +import java.util.StringTokenizer; 1.55 + 1.56 +/** 1.57 + * XML (infoset) over HTTP binding {@link Codec}. 1.58 + * <p> 1.59 + * TODO: Support FI for multipart/related 1.60 + * Support FI for MessageDataSource 1.61 + * 1.62 + * @author Jitendra Kotamraju 1.63 + */ 1.64 +public final class XMLHTTPBindingCodec extends MimeCodec { 1.65 + /** 1.66 + * Base HTTP Accept request-header. 1.67 + */ 1.68 + private static final String BASE_ACCEPT_VALUE = 1.69 + "*"; 1.70 + 1.71 + /** 1.72 + * Fast Infoset MIME type. 1.73 + */ 1.74 + private static final String APPLICATION_FAST_INFOSET_MIME_TYPE = 1.75 + "application/fastinfoset"; 1.76 + 1.77 + /** 1.78 + * True if the Fast Infoset codec should be used 1.79 + */ 1.80 + private boolean useFastInfosetForEncoding; 1.81 + 1.82 + /** 1.83 + * The XML codec 1.84 + */ 1.85 + private final Codec xmlCodec; 1.86 + 1.87 + /** 1.88 + * The FI codec 1.89 + */ 1.90 + private final Codec fiCodec; 1.91 + 1.92 + /** 1.93 + * The Accept header for XML encodings 1.94 + */ 1.95 + private static final String xmlAccept = null; 1.96 + 1.97 + /** 1.98 + * The Accept header for Fast Infoset and XML encodings 1.99 + */ 1.100 + private static final String fiXmlAccept = APPLICATION_FAST_INFOSET_MIME_TYPE + ", " + BASE_ACCEPT_VALUE; 1.101 + 1.102 + private class AcceptContentType implements ContentType { 1.103 + private ContentType _c; 1.104 + private String _accept; 1.105 + 1.106 + public AcceptContentType set(Packet p, ContentType c) { 1.107 + // TODO: need to compose based on underlying codecs 1.108 + if (p.contentNegotiation == ContentNegotiation.optimistic 1.109 + || p.contentNegotiation == ContentNegotiation.pessimistic) { 1.110 + _accept = fiXmlAccept; 1.111 + } else { 1.112 + _accept = xmlAccept; 1.113 + } 1.114 + _c = c; 1.115 + return this; 1.116 + } 1.117 + 1.118 + public String getContentType() { 1.119 + return _c.getContentType(); 1.120 + } 1.121 + 1.122 + public String getSOAPActionHeader() { 1.123 + return _c.getSOAPActionHeader(); 1.124 + } 1.125 + 1.126 + public String getAcceptHeader() { 1.127 + return _accept; 1.128 + } 1.129 + } 1.130 + 1.131 + private AcceptContentType _adaptingContentType = new AcceptContentType(); 1.132 + 1.133 + public XMLHTTPBindingCodec(WSFeatureList f) { 1.134 + super(SOAPVersion.SOAP_11, f); 1.135 + 1.136 + xmlCodec = new XMLCodec(f); 1.137 + 1.138 + fiCodec = getFICodec(); 1.139 + } 1.140 + 1.141 + public String getMimeType() { 1.142 + return null; 1.143 + } 1.144 + 1.145 + @Override 1.146 + public ContentType getStaticContentType(Packet packet) { 1.147 + setRootCodec(packet); 1.148 + 1.149 + ContentType ct = null; 1.150 + if (packet.getMessage() instanceof MessageDataSource) { 1.151 + final MessageDataSource mds = (MessageDataSource)packet.getMessage(); 1.152 + if (mds.hasUnconsumedDataSource()) { 1.153 + ct = getStaticContentType(mds); 1.154 + return (ct != null) 1.155 + ? _adaptingContentType.set(packet, ct) 1.156 + : null; 1.157 + } 1.158 + } 1.159 + 1.160 + ct = super.getStaticContentType(packet); 1.161 + return (ct != null) 1.162 + ? _adaptingContentType.set(packet, ct) 1.163 + : null; 1.164 + } 1.165 + 1.166 + @Override 1.167 + public ContentType encode(Packet packet, OutputStream out) throws IOException { 1.168 + setRootCodec(packet); 1.169 + 1.170 + if (packet.getMessage() instanceof MessageDataSource) { 1.171 + final MessageDataSource mds = (MessageDataSource)packet.getMessage(); 1.172 + if (mds.hasUnconsumedDataSource()) 1.173 + return _adaptingContentType.set(packet, encode(mds, out)); 1.174 + } 1.175 + 1.176 + return _adaptingContentType.set(packet, super.encode(packet, out)); 1.177 + } 1.178 + 1.179 + public ContentType encode(Packet packet, WritableByteChannel buffer) { 1.180 + throw new UnsupportedOperationException(); 1.181 + } 1.182 + 1.183 + @Override 1.184 + public void decode(InputStream in, String contentType, Packet packet) throws IOException { 1.185 + /** 1.186 + * Reset the encoding state when on the server side for each 1.187 + * decode/encode step. 1.188 + */ 1.189 + if (packet.contentNegotiation == null) 1.190 + useFastInfosetForEncoding = false; 1.191 + 1.192 + if (contentType == null) { 1.193 + xmlCodec.decode(in, contentType, packet); 1.194 + } else if (isMultipartRelated(contentType)) { 1.195 + packet.setMessage(new XMLMultiPart(contentType, in, features)); 1.196 + } else if(isFastInfoset(contentType)) { 1.197 + if (fiCodec == null) { 1.198 + throw new RuntimeException(StreamingMessages.FASTINFOSET_NO_IMPLEMENTATION()); 1.199 + } 1.200 + 1.201 + useFastInfosetForEncoding = true; 1.202 + fiCodec.decode(in, contentType, packet); 1.203 + } else if (isXml(contentType)) { 1.204 + xmlCodec.decode(in, contentType, packet); 1.205 + } else { 1.206 + packet.setMessage(new UnknownContent(contentType, in)); 1.207 + } 1.208 + 1.209 + if (!useFastInfosetForEncoding) { 1.210 + useFastInfosetForEncoding = isFastInfosetAcceptable(packet.acceptableMimeTypes); 1.211 + } 1.212 + } 1.213 + 1.214 + protected void decode(MimeMultipartParser mpp, Packet packet) throws IOException { 1.215 + // This method will never be invoked 1.216 + } 1.217 + 1.218 + public MimeCodec copy() { 1.219 + return new XMLHTTPBindingCodec(features); 1.220 + } 1.221 + 1.222 + private boolean isMultipartRelated(String contentType) { 1.223 + return compareStrings(contentType, MimeCodec.MULTIPART_RELATED_MIME_TYPE); 1.224 + } 1.225 + 1.226 + private boolean isApplicationXopXml(String contentType) { 1.227 + return compareStrings(contentType, MtomCodec.XOP_XML_MIME_TYPE); 1.228 + } 1.229 + 1.230 + private boolean isXml(String contentType) { 1.231 + return compareStrings(contentType, XMLCodec.XML_APPLICATION_MIME_TYPE) 1.232 + || compareStrings(contentType, XMLCodec.XML_TEXT_MIME_TYPE) 1.233 + || (compareStrings(contentType, "application/")&&(contentType.toLowerCase().indexOf("+xml") != -1)); 1.234 + } 1.235 + 1.236 + private boolean isFastInfoset(String contentType) { 1.237 + return compareStrings(contentType, APPLICATION_FAST_INFOSET_MIME_TYPE); 1.238 + } 1.239 + 1.240 + private boolean compareStrings(String a, String b) { 1.241 + return a.length() >= b.length() && 1.242 + b.equalsIgnoreCase( 1.243 + a.substring(0, 1.244 + b.length())); 1.245 + } 1.246 + 1.247 + private boolean isFastInfosetAcceptable(String accept) { 1.248 + if (accept == null) return false; 1.249 + 1.250 + StringTokenizer st = new StringTokenizer(accept, ","); 1.251 + while (st.hasMoreTokens()) { 1.252 + final String token = st.nextToken().trim(); 1.253 + if (token.equalsIgnoreCase(APPLICATION_FAST_INFOSET_MIME_TYPE)) { 1.254 + return true; 1.255 + } 1.256 + } 1.257 + return false; 1.258 + } 1.259 + 1.260 + private ContentType getStaticContentType(MessageDataSource mds) { 1.261 + final String contentType = mds.getDataSource().getContentType(); 1.262 + final boolean isFastInfoset = XMLMessage.isFastInfoset(contentType); 1.263 + 1.264 + if (!requiresTransformationOfDataSource(isFastInfoset, 1.265 + useFastInfosetForEncoding)) { 1.266 + return new ContentTypeImpl(contentType); 1.267 + } else { 1.268 + return null; 1.269 + } 1.270 + } 1.271 + 1.272 + private ContentType encode(MessageDataSource mds, OutputStream out) { 1.273 + try { 1.274 + final boolean isFastInfoset = XMLMessage.isFastInfoset( 1.275 + mds.getDataSource().getContentType()); 1.276 + DataSource ds = transformDataSource(mds.getDataSource(), 1.277 + isFastInfoset, useFastInfosetForEncoding, features); 1.278 + 1.279 + InputStream is = ds.getInputStream(); 1.280 + byte[] buf = new byte[1024]; 1.281 + int count; 1.282 + while((count=is.read(buf)) != -1) { 1.283 + out.write(buf, 0, count); 1.284 + } 1.285 + return new ContentTypeImpl(ds.getContentType()); 1.286 + } catch(IOException ioe) { 1.287 + throw new WebServiceException(ioe); 1.288 + } 1.289 + } 1.290 + 1.291 + private void setRootCodec(Packet p) { 1.292 + /** 1.293 + * The following logic is only for outbound packets 1.294 + * to be encoded by client. 1.295 + * On the server the p.contentNegotiation == null. 1.296 + */ 1.297 + if (p.contentNegotiation == ContentNegotiation.none) { 1.298 + // The client may have changed the negotiation property from 1.299 + // pessismistic to none between invocations 1.300 + useFastInfosetForEncoding = false; 1.301 + } else if (p.contentNegotiation == ContentNegotiation.optimistic) { 1.302 + // Always encode using Fast Infoset if in optimisitic mode 1.303 + useFastInfosetForEncoding = true; 1.304 + } 1.305 + 1.306 + rootCodec = (useFastInfosetForEncoding && fiCodec != null) 1.307 + ? fiCodec : xmlCodec; 1.308 + } 1.309 + 1.310 + public static boolean requiresTransformationOfDataSource( 1.311 + boolean isFastInfoset, boolean useFastInfoset) { 1.312 + return (isFastInfoset && !useFastInfoset) || (!isFastInfoset && useFastInfoset); 1.313 + } 1.314 + 1.315 + public static DataSource transformDataSource(DataSource in, 1.316 + boolean isFastInfoset, boolean useFastInfoset, WSFeatureList f) { 1.317 + try { 1.318 + if (isFastInfoset && !useFastInfoset) { 1.319 + // Convert from Fast Infoset to XML 1.320 + Codec codec = new XMLHTTPBindingCodec(f); 1.321 + Packet p = new Packet(); 1.322 + codec.decode(in.getInputStream(), in.getContentType(), p); 1.323 + 1.324 + p.getMessage().getAttachments(); 1.325 + codec.getStaticContentType(p); 1.326 + 1.327 + ByteArrayBuffer bos = new ByteArrayBuffer(); 1.328 + ContentType ct = codec.encode(p, bos); 1.329 + return XMLMessage.createDataSource(ct.getContentType(), bos.newInputStream()); 1.330 + } else if (!isFastInfoset && useFastInfoset) { 1.331 + // Convert from XML to Fast Infoset 1.332 + Codec codec = new XMLHTTPBindingCodec(f); 1.333 + Packet p = new Packet(); 1.334 + codec.decode(in.getInputStream(), in.getContentType(), p); 1.335 + 1.336 + p.contentNegotiation = ContentNegotiation.optimistic; 1.337 + p.getMessage().getAttachments(); 1.338 + codec.getStaticContentType(p); 1.339 + 1.340 + ByteArrayBuffer bos = new ByteArrayBuffer(); 1.341 + com.sun.xml.internal.ws.api.pipe.ContentType ct = codec.encode(p, bos); 1.342 + return XMLMessage.createDataSource(ct.getContentType(), bos.newInputStream()); 1.343 + } 1.344 + } catch(Exception ex) { 1.345 + throw new WebServiceException(ex); 1.346 + } 1.347 + 1.348 + return in; 1.349 + } 1.350 + 1.351 + /** 1.352 + * Obtain an FI SOAP codec instance using reflection. 1.353 + */ 1.354 + private static Codec getFICodec() { 1.355 + try { 1.356 + Class c = Class.forName("com.sun.xml.internal.ws.encoding.fastinfoset.FastInfosetCodec"); 1.357 + Method m = c.getMethod("create"); 1.358 + return (Codec)m.invoke(null); 1.359 + } catch (Exception e) { 1.360 + return null; 1.361 + } 1.362 + } 1.363 +}