aoqi@0: /* aoqi@0: * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.xml.internal.ws.encoding; aoqi@0: aoqi@0: import com.sun.xml.internal.ws.api.SOAPVersion; aoqi@0: import com.sun.xml.internal.ws.api.WSFeatureList; aoqi@0: import com.sun.xml.internal.ws.api.message.Packet; aoqi@0: import com.sun.xml.internal.ws.api.pipe.Codec; aoqi@0: import com.sun.xml.internal.ws.api.pipe.ContentType; aoqi@0: import com.sun.xml.internal.ws.client.ContentNegotiation; aoqi@0: import com.sun.xml.internal.ws.encoding.xml.XMLCodec; aoqi@0: import com.sun.xml.internal.ws.encoding.xml.XMLMessage; aoqi@0: import com.sun.xml.internal.ws.encoding.xml.XMLMessage.MessageDataSource; aoqi@0: import com.sun.xml.internal.ws.encoding.xml.XMLMessage.UnknownContent; aoqi@0: import com.sun.xml.internal.ws.encoding.xml.XMLMessage.XMLMultiPart; aoqi@0: import com.sun.xml.internal.ws.resources.StreamingMessages; aoqi@0: import com.sun.xml.internal.ws.util.ByteArrayBuffer; aoqi@0: aoqi@0: import javax.activation.DataSource; aoqi@0: import javax.xml.ws.WebServiceException; aoqi@0: aoqi@0: import java.io.IOException; aoqi@0: import java.io.InputStream; aoqi@0: import java.io.OutputStream; aoqi@0: import java.lang.reflect.Method; aoqi@0: import java.nio.channels.WritableByteChannel; aoqi@0: import java.util.StringTokenizer; aoqi@0: aoqi@0: /** aoqi@0: * XML (infoset) over HTTP binding {@link Codec}. aoqi@0: *
aoqi@0: * TODO: Support FI for multipart/related aoqi@0: * Support FI for MessageDataSource aoqi@0: * aoqi@0: * @author Jitendra Kotamraju aoqi@0: */ aoqi@0: public final class XMLHTTPBindingCodec extends MimeCodec { aoqi@0: /** aoqi@0: * Base HTTP Accept request-header. aoqi@0: */ aoqi@0: private static final String BASE_ACCEPT_VALUE = aoqi@0: "*"; aoqi@0: aoqi@0: /** aoqi@0: * Fast Infoset MIME type. aoqi@0: */ aoqi@0: private static final String APPLICATION_FAST_INFOSET_MIME_TYPE = aoqi@0: "application/fastinfoset"; aoqi@0: aoqi@0: /** aoqi@0: * True if the Fast Infoset codec should be used aoqi@0: */ aoqi@0: private boolean useFastInfosetForEncoding; aoqi@0: aoqi@0: /** aoqi@0: * The XML codec aoqi@0: */ aoqi@0: private final Codec xmlCodec; aoqi@0: aoqi@0: /** aoqi@0: * The FI codec aoqi@0: */ aoqi@0: private final Codec fiCodec; aoqi@0: aoqi@0: /** aoqi@0: * The Accept header for XML encodings aoqi@0: */ aoqi@0: private static final String xmlAccept = null; aoqi@0: aoqi@0: /** aoqi@0: * The Accept header for Fast Infoset and XML encodings aoqi@0: */ aoqi@0: private static final String fiXmlAccept = APPLICATION_FAST_INFOSET_MIME_TYPE + ", " + BASE_ACCEPT_VALUE; aoqi@0: aoqi@0: private ContentTypeImpl setAcceptHeader(Packet p, ContentType c) { aoqi@0: ContentTypeImpl ctImpl = (ContentTypeImpl)c; aoqi@0: if (p.contentNegotiation == ContentNegotiation.optimistic aoqi@0: || p.contentNegotiation == ContentNegotiation.pessimistic) { aoqi@0: ctImpl.setAcceptHeader(fiXmlAccept); aoqi@0: } else { aoqi@0: ctImpl.setAcceptHeader(xmlAccept); aoqi@0: } aoqi@0: p.setContentType(ctImpl); aoqi@0: return ctImpl; aoqi@0: } aoqi@0: aoqi@0: public XMLHTTPBindingCodec(WSFeatureList f) { aoqi@0: super(SOAPVersion.SOAP_11, f); aoqi@0: aoqi@0: xmlCodec = new XMLCodec(f); aoqi@0: aoqi@0: fiCodec = getFICodec(); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public String getMimeType() { aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public ContentType getStaticContentType(Packet packet) { aoqi@0: ContentType ct; aoqi@0: if (packet.getInternalMessage() instanceof MessageDataSource) { aoqi@0: final MessageDataSource mds = (MessageDataSource)packet.getInternalMessage(); aoqi@0: if (mds.hasUnconsumedDataSource()) { aoqi@0: ct = getStaticContentType(mds); aoqi@0: return (ct != null) aoqi@0: ? setAcceptHeader(packet, ct) //_adaptingContentType.set(packet, ct) aoqi@0: : null; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: ct = super.getStaticContentType(packet); aoqi@0: return (ct != null) aoqi@0: ? setAcceptHeader(packet, ct) //_adaptingContentType.set(packet, ct) aoqi@0: : null; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public ContentType encode(Packet packet, OutputStream out) throws IOException { aoqi@0: if (packet.getInternalMessage() instanceof MessageDataSource) { aoqi@0: final MessageDataSource mds = (MessageDataSource)packet.getInternalMessage(); aoqi@0: if (mds.hasUnconsumedDataSource()) aoqi@0: return setAcceptHeader(packet, encode(mds, out)); aoqi@0: } aoqi@0: aoqi@0: return setAcceptHeader(packet, super.encode(packet, out)); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public ContentType encode(Packet packet, WritableByteChannel buffer) { aoqi@0: throw new UnsupportedOperationException(); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public void decode(InputStream in, String contentType, Packet packet) throws IOException { aoqi@0: /** aoqi@0: * Reset the encoding state when on the server side for each aoqi@0: * decode/encode step. aoqi@0: */ aoqi@0: if (packet.contentNegotiation == null) aoqi@0: useFastInfosetForEncoding = false; aoqi@0: aoqi@0: if (contentType == null) { aoqi@0: xmlCodec.decode(in, contentType, packet); aoqi@0: } else if (isMultipartRelated(contentType)) { aoqi@0: packet.setMessage(new XMLMultiPart(contentType, in, features)); aoqi@0: } else if(isFastInfoset(contentType)) { aoqi@0: if (fiCodec == null) { aoqi@0: throw new RuntimeException(StreamingMessages.FASTINFOSET_NO_IMPLEMENTATION()); aoqi@0: } aoqi@0: aoqi@0: useFastInfosetForEncoding = true; aoqi@0: fiCodec.decode(in, contentType, packet); aoqi@0: } else if (isXml(contentType)) { aoqi@0: xmlCodec.decode(in, contentType, packet); aoqi@0: } else { aoqi@0: packet.setMessage(new UnknownContent(contentType, in)); aoqi@0: } aoqi@0: aoqi@0: if (!useFastInfosetForEncoding) { aoqi@0: useFastInfosetForEncoding = isFastInfosetAcceptable(packet.acceptableMimeTypes); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: protected void decode(MimeMultipartParser mpp, Packet packet) throws IOException { aoqi@0: // This method will never be invoked aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public MimeCodec copy() { aoqi@0: return new XMLHTTPBindingCodec(features); aoqi@0: } aoqi@0: aoqi@0: private boolean isMultipartRelated(String contentType) { aoqi@0: return compareStrings(contentType, MimeCodec.MULTIPART_RELATED_MIME_TYPE); aoqi@0: } aoqi@0: aoqi@0: private boolean isXml(String contentType) { aoqi@0: return compareStrings(contentType, XMLCodec.XML_APPLICATION_MIME_TYPE) aoqi@0: || compareStrings(contentType, XMLCodec.XML_TEXT_MIME_TYPE) aoqi@0: || (compareStrings(contentType, "application/")&&(contentType.toLowerCase().indexOf("+xml") != -1)); aoqi@0: } aoqi@0: aoqi@0: private boolean isFastInfoset(String contentType) { aoqi@0: return compareStrings(contentType, APPLICATION_FAST_INFOSET_MIME_TYPE); aoqi@0: } aoqi@0: aoqi@0: private boolean compareStrings(String a, String b) { aoqi@0: return a.length() >= b.length() && aoqi@0: b.equalsIgnoreCase( aoqi@0: a.substring(0, aoqi@0: b.length())); aoqi@0: } aoqi@0: aoqi@0: private boolean isFastInfosetAcceptable(String accept) { aoqi@0: if (accept == null) return false; aoqi@0: aoqi@0: StringTokenizer st = new StringTokenizer(accept, ","); aoqi@0: while (st.hasMoreTokens()) { aoqi@0: final String token = st.nextToken().trim(); aoqi@0: if (token.equalsIgnoreCase(APPLICATION_FAST_INFOSET_MIME_TYPE)) { aoqi@0: return true; aoqi@0: } aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: private ContentType getStaticContentType(MessageDataSource mds) { aoqi@0: final String contentType = mds.getDataSource().getContentType(); aoqi@0: final boolean isFastInfoset = XMLMessage.isFastInfoset(contentType); aoqi@0: aoqi@0: if (!requiresTransformationOfDataSource(isFastInfoset, aoqi@0: useFastInfosetForEncoding)) { aoqi@0: return new ContentTypeImpl(contentType); aoqi@0: } else { aoqi@0: return null; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private ContentType encode(MessageDataSource mds, OutputStream out) { aoqi@0: try { aoqi@0: final boolean isFastInfoset = XMLMessage.isFastInfoset( aoqi@0: mds.getDataSource().getContentType()); aoqi@0: DataSource ds = transformDataSource(mds.getDataSource(), aoqi@0: isFastInfoset, useFastInfosetForEncoding, features); aoqi@0: aoqi@0: InputStream is = ds.getInputStream(); aoqi@0: byte[] buf = new byte[1024]; aoqi@0: int count; aoqi@0: while((count=is.read(buf)) != -1) { aoqi@0: out.write(buf, 0, count); aoqi@0: } aoqi@0: return new ContentTypeImpl(ds.getContentType()); aoqi@0: } catch(IOException ioe) { aoqi@0: throw new WebServiceException(ioe); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: protected Codec getMimeRootCodec(Packet p) { aoqi@0: /** aoqi@0: * The following logic is only for outbound packets aoqi@0: * to be encoded by client. aoqi@0: * On the server the p.contentNegotiation == null. aoqi@0: */ aoqi@0: if (p.contentNegotiation == ContentNegotiation.none) { aoqi@0: // The client may have changed the negotiation property from aoqi@0: // pessismistic to none between invocations aoqi@0: useFastInfosetForEncoding = false; aoqi@0: } else if (p.contentNegotiation == ContentNegotiation.optimistic) { aoqi@0: // Always encode using Fast Infoset if in optimisitic mode aoqi@0: useFastInfosetForEncoding = true; aoqi@0: } aoqi@0: aoqi@0: return (useFastInfosetForEncoding && fiCodec != null)? fiCodec : xmlCodec; aoqi@0: } aoqi@0: aoqi@0: public static boolean requiresTransformationOfDataSource( aoqi@0: boolean isFastInfoset, boolean useFastInfoset) { aoqi@0: return (isFastInfoset && !useFastInfoset) || (!isFastInfoset && useFastInfoset); aoqi@0: } aoqi@0: aoqi@0: public static DataSource transformDataSource(DataSource in, aoqi@0: boolean isFastInfoset, boolean useFastInfoset, WSFeatureList f) { aoqi@0: try { aoqi@0: if (isFastInfoset && !useFastInfoset) { aoqi@0: // Convert from Fast Infoset to XML aoqi@0: Codec codec = new XMLHTTPBindingCodec(f); aoqi@0: Packet p = new Packet(); aoqi@0: codec.decode(in.getInputStream(), in.getContentType(), p); aoqi@0: aoqi@0: p.getMessage().getAttachments(); aoqi@0: codec.getStaticContentType(p); aoqi@0: aoqi@0: ByteArrayBuffer bos = new ByteArrayBuffer(); aoqi@0: ContentType ct = codec.encode(p, bos); aoqi@0: return XMLMessage.createDataSource(ct.getContentType(), bos.newInputStream()); aoqi@0: } else if (!isFastInfoset && useFastInfoset) { aoqi@0: // Convert from XML to Fast Infoset aoqi@0: Codec codec = new XMLHTTPBindingCodec(f); aoqi@0: Packet p = new Packet(); aoqi@0: codec.decode(in.getInputStream(), in.getContentType(), p); aoqi@0: aoqi@0: p.contentNegotiation = ContentNegotiation.optimistic; aoqi@0: p.getMessage().getAttachments(); aoqi@0: codec.getStaticContentType(p); aoqi@0: aoqi@0: ByteArrayBuffer bos = new ByteArrayBuffer(); aoqi@0: com.sun.xml.internal.ws.api.pipe.ContentType ct = codec.encode(p, bos); aoqi@0: return XMLMessage.createDataSource(ct.getContentType(), bos.newInputStream()); aoqi@0: } aoqi@0: } catch(Exception ex) { aoqi@0: throw new WebServiceException(ex); aoqi@0: } aoqi@0: aoqi@0: return in; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Obtain an FI SOAP codec instance using reflection. aoqi@0: */ aoqi@0: private static Codec getFICodec() { aoqi@0: try { aoqi@0: Class c = Class.forName("com.sun.xml.internal.ws.encoding.fastinfoset.FastInfosetCodec"); aoqi@0: Method m = c.getMethod("create"); aoqi@0: return (Codec)m.invoke(null); aoqi@0: } catch (Exception e) { aoqi@0: return null; aoqi@0: } aoqi@0: } aoqi@0: }