ohair@286: /* alanb@368: * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. ohair@286: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ohair@286: * ohair@286: * This code is free software; you can redistribute it and/or modify it ohair@286: * under the terms of the GNU General Public License version 2 only, as ohair@286: * published by the Free Software Foundation. Oracle designates this ohair@286: * particular file as subject to the "Classpath" exception as provided ohair@286: * by Oracle in the LICENSE file that accompanied this code. ohair@286: * ohair@286: * This code is distributed in the hope that it will be useful, but WITHOUT ohair@286: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ohair@286: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ohair@286: * version 2 for more details (a copy is included in the LICENSE file that ohair@286: * accompanied this code). ohair@286: * ohair@286: * You should have received a copy of the GNU General Public License version ohair@286: * 2 along with this work; if not, write to the Free Software Foundation, ohair@286: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ohair@286: * ohair@286: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@286: * or visit www.oracle.com if you need additional information or have any ohair@286: * questions. ohair@286: */ ohair@286: ohair@286: package com.sun.xml.internal.ws.encoding; ohair@286: ohair@286: import com.sun.xml.internal.ws.api.SOAPVersion; ohair@286: import com.sun.xml.internal.ws.api.WSFeatureList; ohair@286: import com.sun.xml.internal.ws.api.message.Packet; ohair@286: import com.sun.xml.internal.ws.api.pipe.Codec; ohair@286: import com.sun.xml.internal.ws.api.pipe.ContentType; ohair@286: import com.sun.xml.internal.ws.client.ContentNegotiation; ohair@286: import com.sun.xml.internal.ws.encoding.xml.XMLCodec; ohair@286: import com.sun.xml.internal.ws.encoding.xml.XMLMessage; ohair@286: import com.sun.xml.internal.ws.encoding.xml.XMLMessage.MessageDataSource; ohair@286: import com.sun.xml.internal.ws.encoding.xml.XMLMessage.UnknownContent; ohair@286: import com.sun.xml.internal.ws.encoding.xml.XMLMessage.XMLMultiPart; ohair@286: import com.sun.xml.internal.ws.resources.StreamingMessages; ohair@286: import com.sun.xml.internal.ws.util.ByteArrayBuffer; ohair@286: ohair@286: import javax.activation.DataSource; ohair@286: import javax.xml.ws.WebServiceException; ohair@286: ohair@286: import java.io.IOException; ohair@286: import java.io.InputStream; ohair@286: import java.io.OutputStream; ohair@286: import java.lang.reflect.Method; ohair@286: import java.nio.channels.WritableByteChannel; ohair@286: import java.util.StringTokenizer; ohair@286: ohair@286: /** ohair@286: * XML (infoset) over HTTP binding {@link Codec}. ohair@286: *

ohair@286: * TODO: Support FI for multipart/related ohair@286: * Support FI for MessageDataSource ohair@286: * ohair@286: * @author Jitendra Kotamraju ohair@286: */ ohair@286: public final class XMLHTTPBindingCodec extends MimeCodec { ohair@286: /** ohair@286: * Base HTTP Accept request-header. ohair@286: */ ohair@286: private static final String BASE_ACCEPT_VALUE = ohair@286: "*"; ohair@286: ohair@286: /** ohair@286: * Fast Infoset MIME type. ohair@286: */ ohair@286: private static final String APPLICATION_FAST_INFOSET_MIME_TYPE = ohair@286: "application/fastinfoset"; ohair@286: ohair@286: /** ohair@286: * True if the Fast Infoset codec should be used ohair@286: */ ohair@286: private boolean useFastInfosetForEncoding; ohair@286: ohair@286: /** ohair@286: * The XML codec ohair@286: */ ohair@286: private final Codec xmlCodec; ohair@286: ohair@286: /** ohair@286: * The FI codec ohair@286: */ ohair@286: private final Codec fiCodec; ohair@286: ohair@286: /** ohair@286: * The Accept header for XML encodings ohair@286: */ ohair@286: private static final String xmlAccept = null; ohair@286: ohair@286: /** ohair@286: * The Accept header for Fast Infoset and XML encodings ohair@286: */ ohair@286: private static final String fiXmlAccept = APPLICATION_FAST_INFOSET_MIME_TYPE + ", " + BASE_ACCEPT_VALUE; ohair@286: alanb@368: private ContentTypeImpl setAcceptHeader(Packet p, ContentType c) { alanb@368: ContentTypeImpl ctImpl = (ContentTypeImpl)c; alanb@368: if (p.contentNegotiation == ContentNegotiation.optimistic alanb@368: || p.contentNegotiation == ContentNegotiation.pessimistic) { alanb@368: ctImpl.setAcceptHeader(fiXmlAccept); alanb@368: } else { alanb@368: ctImpl.setAcceptHeader(xmlAccept); ohair@286: } alanb@368: p.setContentType(ctImpl); alanb@368: return ctImpl; ohair@286: } ohair@286: ohair@286: public XMLHTTPBindingCodec(WSFeatureList f) { ohair@286: super(SOAPVersion.SOAP_11, f); ohair@286: ohair@286: xmlCodec = new XMLCodec(f); ohair@286: ohair@286: fiCodec = getFICodec(); ohair@286: } ohair@286: alanb@368: @Override ohair@286: public String getMimeType() { ohair@286: return null; ohair@286: } ohair@286: ohair@286: @Override ohair@286: public ContentType getStaticContentType(Packet packet) { alanb@368: ContentType ct; alanb@368: if (packet.getInternalMessage() instanceof MessageDataSource) { alanb@368: final MessageDataSource mds = (MessageDataSource)packet.getInternalMessage(); ohair@286: if (mds.hasUnconsumedDataSource()) { ohair@286: ct = getStaticContentType(mds); ohair@286: return (ct != null) alanb@368: ? setAcceptHeader(packet, ct) //_adaptingContentType.set(packet, ct) ohair@286: : null; ohair@286: } ohair@286: } ohair@286: ohair@286: ct = super.getStaticContentType(packet); ohair@286: return (ct != null) alanb@368: ? setAcceptHeader(packet, ct) //_adaptingContentType.set(packet, ct) ohair@286: : null; ohair@286: } ohair@286: ohair@286: @Override ohair@286: public ContentType encode(Packet packet, OutputStream out) throws IOException { alanb@368: if (packet.getInternalMessage() instanceof MessageDataSource) { alanb@368: final MessageDataSource mds = (MessageDataSource)packet.getInternalMessage(); ohair@286: if (mds.hasUnconsumedDataSource()) alanb@368: return setAcceptHeader(packet, encode(mds, out)); ohair@286: } ohair@286: alanb@368: return setAcceptHeader(packet, super.encode(packet, out)); ohair@286: } ohair@286: alanb@368: @Override ohair@286: public ContentType encode(Packet packet, WritableByteChannel buffer) { ohair@286: throw new UnsupportedOperationException(); ohair@286: } ohair@286: ohair@286: @Override ohair@286: public void decode(InputStream in, String contentType, Packet packet) throws IOException { ohair@286: /** ohair@286: * Reset the encoding state when on the server side for each ohair@286: * decode/encode step. ohair@286: */ ohair@286: if (packet.contentNegotiation == null) ohair@286: useFastInfosetForEncoding = false; ohair@286: ohair@286: if (contentType == null) { ohair@286: xmlCodec.decode(in, contentType, packet); ohair@286: } else if (isMultipartRelated(contentType)) { ohair@286: packet.setMessage(new XMLMultiPart(contentType, in, features)); ohair@286: } else if(isFastInfoset(contentType)) { ohair@286: if (fiCodec == null) { ohair@286: throw new RuntimeException(StreamingMessages.FASTINFOSET_NO_IMPLEMENTATION()); ohair@286: } ohair@286: ohair@286: useFastInfosetForEncoding = true; ohair@286: fiCodec.decode(in, contentType, packet); ohair@286: } else if (isXml(contentType)) { ohair@286: xmlCodec.decode(in, contentType, packet); ohair@286: } else { ohair@286: packet.setMessage(new UnknownContent(contentType, in)); ohair@286: } ohair@286: ohair@286: if (!useFastInfosetForEncoding) { ohair@286: useFastInfosetForEncoding = isFastInfosetAcceptable(packet.acceptableMimeTypes); ohair@286: } ohair@286: } ohair@286: alanb@368: @Override ohair@286: protected void decode(MimeMultipartParser mpp, Packet packet) throws IOException { ohair@286: // This method will never be invoked ohair@286: } ohair@286: alanb@368: @Override ohair@286: public MimeCodec copy() { ohair@286: return new XMLHTTPBindingCodec(features); ohair@286: } ohair@286: ohair@286: private boolean isMultipartRelated(String contentType) { ohair@286: return compareStrings(contentType, MimeCodec.MULTIPART_RELATED_MIME_TYPE); ohair@286: } ohair@286: ohair@286: private boolean isXml(String contentType) { ohair@286: return compareStrings(contentType, XMLCodec.XML_APPLICATION_MIME_TYPE) ohair@286: || compareStrings(contentType, XMLCodec.XML_TEXT_MIME_TYPE) ohair@286: || (compareStrings(contentType, "application/")&&(contentType.toLowerCase().indexOf("+xml") != -1)); ohair@286: } ohair@286: ohair@286: private boolean isFastInfoset(String contentType) { ohair@286: return compareStrings(contentType, APPLICATION_FAST_INFOSET_MIME_TYPE); ohair@286: } ohair@286: ohair@286: private boolean compareStrings(String a, String b) { ohair@286: return a.length() >= b.length() && ohair@286: b.equalsIgnoreCase( ohair@286: a.substring(0, ohair@286: b.length())); ohair@286: } ohair@286: ohair@286: private boolean isFastInfosetAcceptable(String accept) { ohair@286: if (accept == null) return false; ohair@286: ohair@286: StringTokenizer st = new StringTokenizer(accept, ","); ohair@286: while (st.hasMoreTokens()) { ohair@286: final String token = st.nextToken().trim(); ohair@286: if (token.equalsIgnoreCase(APPLICATION_FAST_INFOSET_MIME_TYPE)) { ohair@286: return true; ohair@286: } ohair@286: } ohair@286: return false; ohair@286: } ohair@286: ohair@286: private ContentType getStaticContentType(MessageDataSource mds) { ohair@286: final String contentType = mds.getDataSource().getContentType(); ohair@286: final boolean isFastInfoset = XMLMessage.isFastInfoset(contentType); ohair@286: ohair@286: if (!requiresTransformationOfDataSource(isFastInfoset, ohair@286: useFastInfosetForEncoding)) { ohair@286: return new ContentTypeImpl(contentType); ohair@286: } else { ohair@286: return null; ohair@286: } ohair@286: } ohair@286: ohair@286: private ContentType encode(MessageDataSource mds, OutputStream out) { ohair@286: try { ohair@286: final boolean isFastInfoset = XMLMessage.isFastInfoset( ohair@286: mds.getDataSource().getContentType()); ohair@286: DataSource ds = transformDataSource(mds.getDataSource(), ohair@286: isFastInfoset, useFastInfosetForEncoding, features); ohair@286: ohair@286: InputStream is = ds.getInputStream(); ohair@286: byte[] buf = new byte[1024]; ohair@286: int count; ohair@286: while((count=is.read(buf)) != -1) { ohair@286: out.write(buf, 0, count); ohair@286: } ohair@286: return new ContentTypeImpl(ds.getContentType()); ohair@286: } catch(IOException ioe) { ohair@286: throw new WebServiceException(ioe); ohair@286: } ohair@286: } ohair@286: alanb@368: @Override alanb@368: protected Codec getMimeRootCodec(Packet p) { ohair@286: /** ohair@286: * The following logic is only for outbound packets ohair@286: * to be encoded by client. ohair@286: * On the server the p.contentNegotiation == null. ohair@286: */ ohair@286: if (p.contentNegotiation == ContentNegotiation.none) { ohair@286: // The client may have changed the negotiation property from ohair@286: // pessismistic to none between invocations ohair@286: useFastInfosetForEncoding = false; ohair@286: } else if (p.contentNegotiation == ContentNegotiation.optimistic) { ohair@286: // Always encode using Fast Infoset if in optimisitic mode ohair@286: useFastInfosetForEncoding = true; ohair@286: } ohair@286: alanb@368: return (useFastInfosetForEncoding && fiCodec != null)? fiCodec : xmlCodec; ohair@286: } ohair@286: ohair@286: public static boolean requiresTransformationOfDataSource( ohair@286: boolean isFastInfoset, boolean useFastInfoset) { ohair@286: return (isFastInfoset && !useFastInfoset) || (!isFastInfoset && useFastInfoset); ohair@286: } ohair@286: ohair@286: public static DataSource transformDataSource(DataSource in, ohair@286: boolean isFastInfoset, boolean useFastInfoset, WSFeatureList f) { ohair@286: try { ohair@286: if (isFastInfoset && !useFastInfoset) { ohair@286: // Convert from Fast Infoset to XML ohair@286: Codec codec = new XMLHTTPBindingCodec(f); ohair@286: Packet p = new Packet(); ohair@286: codec.decode(in.getInputStream(), in.getContentType(), p); ohair@286: ohair@286: p.getMessage().getAttachments(); ohair@286: codec.getStaticContentType(p); ohair@286: ohair@286: ByteArrayBuffer bos = new ByteArrayBuffer(); ohair@286: ContentType ct = codec.encode(p, bos); ohair@286: return XMLMessage.createDataSource(ct.getContentType(), bos.newInputStream()); ohair@286: } else if (!isFastInfoset && useFastInfoset) { ohair@286: // Convert from XML to Fast Infoset ohair@286: Codec codec = new XMLHTTPBindingCodec(f); ohair@286: Packet p = new Packet(); ohair@286: codec.decode(in.getInputStream(), in.getContentType(), p); ohair@286: ohair@286: p.contentNegotiation = ContentNegotiation.optimistic; ohair@286: p.getMessage().getAttachments(); ohair@286: codec.getStaticContentType(p); ohair@286: ohair@286: ByteArrayBuffer bos = new ByteArrayBuffer(); ohair@286: com.sun.xml.internal.ws.api.pipe.ContentType ct = codec.encode(p, bos); ohair@286: return XMLMessage.createDataSource(ct.getContentType(), bos.newInputStream()); ohair@286: } ohair@286: } catch(Exception ex) { ohair@286: throw new WebServiceException(ex); ohair@286: } ohair@286: ohair@286: return in; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Obtain an FI SOAP codec instance using reflection. ohair@286: */ ohair@286: private static Codec getFICodec() { ohair@286: try { ohair@286: Class c = Class.forName("com.sun.xml.internal.ws.encoding.fastinfoset.FastInfosetCodec"); ohair@286: Method m = c.getMethod("create"); ohair@286: return (Codec)m.invoke(null); ohair@286: } catch (Exception e) { ohair@286: return null; ohair@286: } ohair@286: } ohair@286: }