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.istack.internal.NotNull; aoqi@0: import com.sun.xml.internal.bind.DatatypeConverterImpl; 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.Attachment; aoqi@0: import com.sun.xml.internal.ws.api.message.AttachmentSet; aoqi@0: import com.sun.xml.internal.ws.api.message.Packet; aoqi@0: import com.sun.xml.internal.ws.api.pipe.ContentType; aoqi@0: import com.sun.xml.internal.ws.api.pipe.StreamSOAPCodec; aoqi@0: import com.sun.xml.internal.ws.api.streaming.XMLStreamReaderFactory; aoqi@0: import com.sun.xml.internal.ws.api.streaming.XMLStreamWriterFactory; aoqi@0: import com.sun.xml.internal.ws.developer.SerializationFeature; aoqi@0: import com.sun.xml.internal.ws.developer.StreamingDataHandler; aoqi@0: import com.sun.xml.internal.ws.message.MimeAttachmentSet; aoqi@0: import com.sun.xml.internal.ws.streaming.XMLStreamWriterUtil; aoqi@0: import com.sun.xml.internal.ws.util.ByteArrayDataSource; aoqi@0: import com.sun.xml.internal.ws.util.xml.NamespaceContextExAdaper; aoqi@0: import com.sun.xml.internal.ws.util.xml.XMLStreamReaderFilter; aoqi@0: import com.sun.xml.internal.ws.util.xml.XMLStreamWriterFilter; aoqi@0: import com.sun.xml.internal.ws.streaming.MtomStreamWriter; aoqi@0: import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil; aoqi@0: import com.sun.xml.internal.ws.server.UnsupportedMediaException; aoqi@0: import com.sun.xml.internal.org.jvnet.staxex.Base64Data; aoqi@0: import com.sun.xml.internal.org.jvnet.staxex.NamespaceContextEx; aoqi@0: import com.sun.xml.internal.org.jvnet.staxex.XMLStreamReaderEx; aoqi@0: import com.sun.xml.internal.org.jvnet.staxex.XMLStreamWriterEx; aoqi@0: aoqi@0: import javax.activation.DataHandler; aoqi@0: import javax.xml.namespace.NamespaceContext; aoqi@0: import javax.xml.stream.XMLStreamConstants; aoqi@0: import javax.xml.stream.XMLStreamException; aoqi@0: import javax.xml.stream.XMLStreamReader; aoqi@0: import javax.xml.stream.XMLStreamWriter; aoqi@0: import javax.xml.ws.WebServiceException; aoqi@0: import javax.xml.ws.soap.MTOMFeature; aoqi@0: import javax.xml.bind.attachment.AttachmentMarshaller; aoqi@0: import java.io.IOException; aoqi@0: import java.io.OutputStream; aoqi@0: import java.io.UnsupportedEncodingException; aoqi@0: import java.net.URLDecoder; aoqi@0: import java.nio.channels.WritableByteChannel; aoqi@0: import java.nio.charset.Charset; aoqi@0: import java.util.ArrayList; aoqi@0: import java.util.Iterator; aoqi@0: import java.util.List; aoqi@0: import java.util.Map; aoqi@0: import java.util.UUID; aoqi@0: aoqi@0: /** aoqi@0: * Mtom message Codec. It can be used even for non-soap message's mtom encoding. aoqi@0: * aoqi@0: * @author Vivek Pandey aoqi@0: * @author Jitendra Kotamraju aoqi@0: */ aoqi@0: public class MtomCodec extends MimeCodec { aoqi@0: aoqi@0: public static final String XOP_XML_MIME_TYPE = "application/xop+xml"; aoqi@0: public static final String XOP_LOCALNAME = "Include"; aoqi@0: public static final String XOP_NAMESPACEURI = "http://www.w3.org/2004/08/xop/include"; aoqi@0: aoqi@0: private final StreamSOAPCodec codec; aoqi@0: private final MTOMFeature mtomFeature; aoqi@0: private final SerializationFeature sf; aoqi@0: private final static String DECODED_MESSAGE_CHARSET = "decodedMessageCharset"; aoqi@0: aoqi@0: MtomCodec(SOAPVersion version, StreamSOAPCodec codec, WSFeatureList features){ aoqi@0: super(version, features); aoqi@0: this.codec = codec; aoqi@0: sf = features.get(SerializationFeature.class); aoqi@0: MTOMFeature mtom = features.get(MTOMFeature.class); aoqi@0: if(mtom == null) aoqi@0: this.mtomFeature = new MTOMFeature(); aoqi@0: else aoqi@0: this.mtomFeature = mtom; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Return the soap 1.1 and soap 1.2 specific XOP packaged ContentType aoqi@0: * aoqi@0: * @return A non-null content type for soap11 or soap 1.2 content type aoqi@0: */ aoqi@0: @Override aoqi@0: public ContentType getStaticContentType(Packet packet) { aoqi@0: return getStaticContentTypeStatic(packet, version); aoqi@0: } aoqi@0: aoqi@0: public static ContentType getStaticContentTypeStatic(Packet packet, SOAPVersion version) { aoqi@0: ContentType ct = (ContentType) packet.getInternalContentType(); aoqi@0: if ( ct != null ) return ct; aoqi@0: aoqi@0: String uuid = UUID.randomUUID().toString(); aoqi@0: String boundary = "uuid:" + uuid; aoqi@0: String rootId = ""; aoqi@0: String soapActionParameter = SOAPVersion.SOAP_11.equals(version) ? null : createActionParameter(packet); aoqi@0: aoqi@0: String boundaryParameter = "boundary=\"" + boundary +"\""; aoqi@0: String messageContentType = MULTIPART_RELATED_MIME_TYPE + aoqi@0: ";start=\""+rootId +"\"" + aoqi@0: ";type=\"" + XOP_XML_MIME_TYPE + "\";" + aoqi@0: boundaryParameter + aoqi@0: ";start-info=\"" + version.contentType + aoqi@0: (soapActionParameter == null? "" : soapActionParameter) + aoqi@0: "\""; aoqi@0: aoqi@0: ContentTypeImpl ctImpl = SOAPVersion.SOAP_11.equals(version) ? aoqi@0: new ContentTypeImpl(messageContentType, (packet.soapAction == null)?"":packet.soapAction, null) : aoqi@0: new ContentTypeImpl(messageContentType, null, null); aoqi@0: ctImpl.setBoundary(boundary); aoqi@0: ctImpl.setRootId(rootId); aoqi@0: packet.setContentType(ctImpl); aoqi@0: return ctImpl; aoqi@0: } aoqi@0: aoqi@0: private static String createActionParameter(Packet packet) { aoqi@0: return packet.soapAction != null? ";action=\\\""+packet.soapAction+"\\\"" : ""; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public ContentType encode(Packet packet, OutputStream out) throws IOException { aoqi@0: ContentTypeImpl ctImpl = (ContentTypeImpl) this.getStaticContentType(packet); aoqi@0: String boundary = ctImpl.getBoundary(); aoqi@0: String rootId = ctImpl.getRootId(); aoqi@0: aoqi@0: if(packet.getMessage() != null){ aoqi@0: try { aoqi@0: String encoding = getPacketEncoding(packet); aoqi@0: packet.invocationProperties.remove(DECODED_MESSAGE_CHARSET); aoqi@0: aoqi@0: String actionParameter = getActionParameter(packet, version); aoqi@0: String soapXopContentType = getSOAPXopContentType(encoding, version, actionParameter); aoqi@0: aoqi@0: writeln("--"+boundary, out); aoqi@0: writeMimeHeaders(soapXopContentType, rootId, out); aoqi@0: aoqi@0: //mtom attachments that need to be written after the root part aoqi@0: List mtomAttachments = new ArrayList(); aoqi@0: MtomStreamWriterImpl writer = new MtomStreamWriterImpl( aoqi@0: XMLStreamWriterFactory.create(out, encoding), mtomAttachments, boundary, mtomFeature); aoqi@0: aoqi@0: packet.getMessage().writeTo(writer); aoqi@0: XMLStreamWriterFactory.recycle(writer); aoqi@0: writeln(out); aoqi@0: aoqi@0: for(ByteArrayBuffer bos : mtomAttachments){ aoqi@0: bos.write(out); aoqi@0: } aoqi@0: aoqi@0: // now write out the attachments in the message that weren't aoqi@0: // previously written aoqi@0: writeNonMtomAttachments(packet.getMessage().getAttachments(), aoqi@0: out, boundary); aoqi@0: aoqi@0: //write out the end boundary aoqi@0: writeAsAscii("--"+boundary, out); aoqi@0: writeAsAscii("--", out); aoqi@0: aoqi@0: } catch (XMLStreamException e) { aoqi@0: throw new WebServiceException(e); aoqi@0: } aoqi@0: } aoqi@0: //now create the boundary for next encode() call aoqi@0: // createConteTypeHeader(); aoqi@0: return ctImpl; aoqi@0: } aoqi@0: aoqi@0: public static String getSOAPXopContentType(String encoding, SOAPVersion version, aoqi@0: String actionParameter) { aoqi@0: return XOP_XML_MIME_TYPE +";charset="+encoding+";type=\""+version.contentType+ actionParameter + "\""; aoqi@0: } aoqi@0: aoqi@0: public static String getActionParameter(Packet packet, SOAPVersion version) { aoqi@0: return (version == SOAPVersion.SOAP_11) ? "" : createActionParameter(packet); aoqi@0: } aoqi@0: aoqi@0: public static class ByteArrayBuffer{ aoqi@0: final String contentId; aoqi@0: aoqi@0: private final DataHandler dh; aoqi@0: private final String boundary; aoqi@0: aoqi@0: ByteArrayBuffer(@NotNull DataHandler dh, String b) { aoqi@0: this.dh = dh; aoqi@0: String cid = null; aoqi@0: if (dh instanceof StreamingDataHandler) { aoqi@0: StreamingDataHandler sdh = (StreamingDataHandler) dh; aoqi@0: if (sdh.getHrefCid() != null) aoqi@0: cid = sdh.getHrefCid(); aoqi@0: } aoqi@0: this.contentId = cid != null ? cid : encodeCid(); aoqi@0: boundary = b; aoqi@0: } aoqi@0: aoqi@0: public void write(OutputStream os) throws IOException { aoqi@0: //build attachment frame aoqi@0: writeln("--"+boundary, os); aoqi@0: writeMimeHeaders(dh.getContentType(), contentId, os); aoqi@0: dh.writeTo(os); aoqi@0: writeln(os); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public static void writeMimeHeaders(String contentType, String contentId, OutputStream out) throws IOException { aoqi@0: String cid = contentId; aoqi@0: if(cid != null && cid.length() >0 && cid.charAt(0) != '<') aoqi@0: cid = '<' + cid + '>'; aoqi@0: writeln("Content-Id: " + cid, out); aoqi@0: writeln("Content-Type: " + contentType, out); aoqi@0: writeln("Content-Transfer-Encoding: binary", out); aoqi@0: writeln(out); aoqi@0: } aoqi@0: aoqi@0: // Compiler warning for not calling close, but cannot call close, aoqi@0: // will consume attachment bytes. aoqi@0: @SuppressWarnings("resource") aoqi@0: private void writeNonMtomAttachments(AttachmentSet attachments, aoqi@0: OutputStream out, String boundary) throws IOException { aoqi@0: aoqi@0: for (Attachment att : attachments) { aoqi@0: aoqi@0: DataHandler dh = att.asDataHandler(); aoqi@0: if (dh instanceof StreamingDataHandler) { aoqi@0: StreamingDataHandler sdh = (StreamingDataHandler) dh; aoqi@0: // If DataHandler has href Content-ID, it is MTOM, so skip. aoqi@0: if (sdh.getHrefCid() != null) aoqi@0: continue; aoqi@0: } aoqi@0: aoqi@0: // build attachment frame aoqi@0: writeln("--" + boundary, out); aoqi@0: writeMimeHeaders(att.getContentType(), att.getContentId(), out); aoqi@0: att.writeTo(out); aoqi@0: writeln(out); // write \r\n aoqi@0: } 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 MtomCodec copy() { aoqi@0: return new MtomCodec(version, (StreamSOAPCodec)codec.copy(), features); aoqi@0: } aoqi@0: aoqi@0: private static String encodeCid(){ aoqi@0: String cid="example.jaxws.sun.com"; aoqi@0: String name = UUID.randomUUID()+"@"; aoqi@0: return name + cid; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: protected void decode(MimeMultipartParser mpp, Packet packet) throws IOException { aoqi@0: //TODO shouldn't we check for SOAP1.1/SOAP1.2 and throw aoqi@0: //TODO UnsupportedMediaException like StreamSOAPCodec aoqi@0: String charset = null; aoqi@0: String ct = mpp.getRootPart().getContentType(); aoqi@0: if (ct != null) { aoqi@0: charset = new ContentTypeImpl(ct).getCharSet(); aoqi@0: } aoqi@0: if (charset != null && !Charset.isSupported(charset)) { aoqi@0: throw new UnsupportedMediaException(charset); aoqi@0: } aoqi@0: aoqi@0: if (charset != null) { aoqi@0: packet.invocationProperties.put(DECODED_MESSAGE_CHARSET, charset); aoqi@0: } else { aoqi@0: packet.invocationProperties.remove(DECODED_MESSAGE_CHARSET); aoqi@0: } aoqi@0: aoqi@0: // we'd like to reuse those reader objects but unfortunately decoder may be reused aoqi@0: // before the decoded message is completely used. aoqi@0: XMLStreamReader mtomReader = new MtomXMLStreamReaderEx( mpp, aoqi@0: XMLStreamReaderFactory.create(null, mpp.getRootPart().asInputStream(), charset, true) aoqi@0: ); aoqi@0: aoqi@0: packet.setMessage(codec.decode(mtomReader, new MimeAttachmentSet(mpp))); aoqi@0: packet.setMtomFeature(mtomFeature); aoqi@0: packet.setContentType(mpp.getContentType()); aoqi@0: } aoqi@0: aoqi@0: private String getPacketEncoding(Packet packet) { aoqi@0: // If SerializationFeature is set, just use that encoding aoqi@0: if (sf != null && sf.getEncoding() != null) { aoqi@0: return sf.getEncoding().equals("") ? SOAPBindingCodec.DEFAULT_ENCODING : sf.getEncoding(); aoqi@0: } aoqi@0: return determinePacketEncoding(packet); aoqi@0: } aoqi@0: aoqi@0: public static String determinePacketEncoding(Packet packet) { aoqi@0: if (packet != null && packet.endpoint != null) { aoqi@0: // Use request message's encoding for Server-side response messages aoqi@0: String charset = (String)packet.invocationProperties.get(DECODED_MESSAGE_CHARSET); aoqi@0: return charset == null aoqi@0: ? SOAPBindingCodec.DEFAULT_ENCODING : charset; aoqi@0: } aoqi@0: aoqi@0: // Use default encoding for client-side request messages aoqi@0: return SOAPBindingCodec.DEFAULT_ENCODING; aoqi@0: } aoqi@0: aoqi@0: public static class MtomStreamWriterImpl extends XMLStreamWriterFilter implements XMLStreamWriterEx, aoqi@0: MtomStreamWriter, HasEncoding { aoqi@0: private final List mtomAttachments; aoqi@0: private final String boundary; aoqi@0: private final MTOMFeature myMtomFeature; aoqi@0: public MtomStreamWriterImpl(XMLStreamWriter w, List mtomAttachments, String b, MTOMFeature myMtomFeature) { aoqi@0: super(w); aoqi@0: this.mtomAttachments = mtomAttachments; aoqi@0: this.boundary = b; aoqi@0: this.myMtomFeature = myMtomFeature; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public void writeBinary(byte[] data, int start, int len, String contentType) throws XMLStreamException { aoqi@0: //check threshold and if less write as base64encoded value aoqi@0: if(myMtomFeature.getThreshold() > len){ aoqi@0: writeCharacters(DatatypeConverterImpl._printBase64Binary(data, start, len)); aoqi@0: return; aoqi@0: } aoqi@0: ByteArrayBuffer bab = new ByteArrayBuffer(new DataHandler(new ByteArrayDataSource(data, start, len, contentType)), boundary); aoqi@0: writeBinary(bab); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public void writeBinary(DataHandler dataHandler) throws XMLStreamException { aoqi@0: // TODO how do we check threshold and if less inline the data aoqi@0: writeBinary(new ByteArrayBuffer(dataHandler, boundary)); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public OutputStream writeBinary(String contentType) throws XMLStreamException { aoqi@0: throw new UnsupportedOperationException(); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public void writePCDATA(CharSequence data) throws XMLStreamException { aoqi@0: if(data == null) aoqi@0: return; aoqi@0: if(data instanceof Base64Data){ aoqi@0: Base64Data binaryData = (Base64Data)data; aoqi@0: writeBinary(binaryData.getDataHandler()); aoqi@0: return; aoqi@0: } aoqi@0: writeCharacters(data.toString()); aoqi@0: } aoqi@0: aoqi@0: private void writeBinary(ByteArrayBuffer bab) { aoqi@0: try { aoqi@0: mtomAttachments.add(bab); aoqi@0: String prefix = writer.getPrefix(XOP_NAMESPACEURI); aoqi@0: if (prefix == null || !prefix.equals("xop")) { aoqi@0: writer.setPrefix("xop", XOP_NAMESPACEURI); aoqi@0: writer.writeNamespace("xop", XOP_NAMESPACEURI); aoqi@0: } aoqi@0: writer.writeStartElement(XOP_NAMESPACEURI, XOP_LOCALNAME); aoqi@0: writer.writeAttribute("href", "cid:"+bab.contentId); aoqi@0: writer.writeEndElement(); aoqi@0: writer.flush(); aoqi@0: } catch (XMLStreamException e) { aoqi@0: throw new WebServiceException(e); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public Object getProperty(String name) throws IllegalArgumentException { aoqi@0: // Hack for JDK6's SJSXP aoqi@0: if (name.equals("sjsxp-outputstream") && writer instanceof Map) { aoqi@0: Object obj = ((Map) writer).get("sjsxp-outputstream"); aoqi@0: if (obj != null) { aoqi@0: return obj; aoqi@0: } aoqi@0: } aoqi@0: return super.getProperty(name); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * JAXBMessage writes envelope directly to the OutputStream(for SJSXP, woodstox). aoqi@0: * While writing, it calls the AttachmentMarshaller methods for adding attachments. aoqi@0: * JAXB writes xop:Include in this case. aoqi@0: */ aoqi@0: @Override aoqi@0: public AttachmentMarshaller getAttachmentMarshaller() { aoqi@0: return new AttachmentMarshaller() { aoqi@0: aoqi@0: @Override aoqi@0: public String addMtomAttachment(DataHandler data, String elementNamespace, String elementLocalName) { aoqi@0: // Should we do the threshold processing on DataHandler ? But that would be aoqi@0: // expensive as DataHolder need to read the data again from its source aoqi@0: ByteArrayBuffer bab = new ByteArrayBuffer(data, boundary); aoqi@0: mtomAttachments.add(bab); aoqi@0: return "cid:"+bab.contentId; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public String addMtomAttachment(byte[] data, int offset, int length, String mimeType, String elementNamespace, String elementLocalName) { aoqi@0: // inline the data based on the threshold aoqi@0: if (myMtomFeature.getThreshold() > length) { aoqi@0: return null; // JAXB inlines the attachment data aoqi@0: } aoqi@0: ByteArrayBuffer bab = new ByteArrayBuffer(new DataHandler(new ByteArrayDataSource(data, offset, length, mimeType)), boundary); aoqi@0: mtomAttachments.add(bab); aoqi@0: return "cid:"+bab.contentId; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public String addSwaRefAttachment(DataHandler data) { aoqi@0: ByteArrayBuffer bab = new ByteArrayBuffer(data, boundary); aoqi@0: mtomAttachments.add(bab); aoqi@0: return "cid:"+bab.contentId; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public boolean isXOPPackage() { aoqi@0: return true; aoqi@0: } aoqi@0: }; aoqi@0: } aoqi@0: aoqi@0: public List getMtomAttachments() { aoqi@0: return this.mtomAttachments; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public String getEncoding() { aoqi@0: return XMLStreamWriterUtil.getEncoding(writer); aoqi@0: } aoqi@0: aoqi@0: private static class MtomNamespaceContextEx implements NamespaceContextEx { aoqi@0: private final NamespaceContext nsContext; aoqi@0: aoqi@0: public MtomNamespaceContextEx(NamespaceContext nsContext) { aoqi@0: this.nsContext = nsContext; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public Iterator iterator() { aoqi@0: throw new UnsupportedOperationException(); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public String getNamespaceURI(String prefix) { aoqi@0: return nsContext.getNamespaceURI(prefix); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public String getPrefix(String namespaceURI) { aoqi@0: return nsContext.getPrefix(namespaceURI); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public Iterator getPrefixes(String namespaceURI) { aoqi@0: return nsContext.getPrefixes(namespaceURI); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public NamespaceContextEx getNamespaceContext() { aoqi@0: NamespaceContext nsContext = writer.getNamespaceContext(); aoqi@0: return new MtomNamespaceContextEx(nsContext); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public static class MtomXMLStreamReaderEx extends XMLStreamReaderFilter implements XMLStreamReaderEx { aoqi@0: /** aoqi@0: * The parser for the outer MIME 'shell'. aoqi@0: */ aoqi@0: private final MimeMultipartParser mimeMP; aoqi@0: aoqi@0: private boolean xopReferencePresent = false; aoqi@0: private Base64Data base64AttData; aoqi@0: aoqi@0: //To be used with #getTextCharacters aoqi@0: private char[] base64EncodedText; aoqi@0: aoqi@0: private String xopHref; aoqi@0: aoqi@0: public MtomXMLStreamReaderEx(MimeMultipartParser mimeMP, XMLStreamReader reader) { aoqi@0: super(reader); aoqi@0: this.mimeMP = mimeMP; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public CharSequence getPCDATA() throws XMLStreamException { aoqi@0: if(xopReferencePresent){ aoqi@0: return base64AttData; aoqi@0: } aoqi@0: return reader.getText(); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public NamespaceContextEx getNamespaceContext() { aoqi@0: return new NamespaceContextExAdaper(reader.getNamespaceContext()); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public String getElementTextTrim() throws XMLStreamException { aoqi@0: throw new UnsupportedOperationException(); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public int getTextLength() { aoqi@0: if (xopReferencePresent) { aoqi@0: return base64AttData.length(); aoqi@0: } aoqi@0: return reader.getTextLength(); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public int getTextStart() { aoqi@0: if (xopReferencePresent) { aoqi@0: return 0; aoqi@0: } aoqi@0: return reader.getTextStart(); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public int getEventType() { aoqi@0: if(xopReferencePresent) aoqi@0: return XMLStreamConstants.CHARACTERS; aoqi@0: return super.getEventType(); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public int next() throws XMLStreamException { aoqi@0: int event = reader.next(); aoqi@0: if (event == XMLStreamConstants.START_ELEMENT && reader.getLocalName().equals(XOP_LOCALNAME) && reader.getNamespaceURI().equals(XOP_NAMESPACEURI)) { aoqi@0: //its xop reference, take the URI reference aoqi@0: String href = reader.getAttributeValue(null, "href"); aoqi@0: try { aoqi@0: xopHref = href; aoqi@0: Attachment att = getAttachment(href); aoqi@0: if(att != null){ aoqi@0: DataHandler dh = att.asDataHandler(); aoqi@0: if (dh instanceof StreamingDataHandler) { aoqi@0: ((StreamingDataHandler)dh).setHrefCid(att.getContentId()); aoqi@0: } aoqi@0: base64AttData = new Base64Data(); aoqi@0: base64AttData.set(dh); aoqi@0: } aoqi@0: xopReferencePresent = true; aoqi@0: } catch (IOException e) { aoqi@0: throw new WebServiceException(e); aoqi@0: } aoqi@0: //move to the aoqi@0: XMLStreamReaderUtil.nextElementContent(reader); aoqi@0: return XMLStreamConstants.CHARACTERS; aoqi@0: } aoqi@0: if(xopReferencePresent){ aoqi@0: xopReferencePresent = false; aoqi@0: base64EncodedText = null; aoqi@0: xopHref = null; aoqi@0: } aoqi@0: return event; aoqi@0: } aoqi@0: aoqi@0: private String decodeCid(String cid) { aoqi@0: try { aoqi@0: cid = URLDecoder.decode(cid, "utf-8"); aoqi@0: } catch (UnsupportedEncodingException e) { aoqi@0: //on recceiving side lets not fail now, try to look for it aoqi@0: } aoqi@0: return cid; aoqi@0: } aoqi@0: aoqi@0: private Attachment getAttachment(String cid) throws IOException { aoqi@0: if (cid.startsWith("cid:")) aoqi@0: cid = cid.substring(4, cid.length()); aoqi@0: if (cid.indexOf('%') != -1) { aoqi@0: cid = decodeCid(cid); aoqi@0: return mimeMP.getAttachmentPart(cid); aoqi@0: } aoqi@0: return mimeMP.getAttachmentPart(cid); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public char[] getTextCharacters() { aoqi@0: if (xopReferencePresent) { aoqi@0: char[] chars = new char[base64AttData.length()]; aoqi@0: base64AttData.writeTo(chars, 0); aoqi@0: return chars; aoqi@0: } aoqi@0: return reader.getTextCharacters(); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public int getTextCharacters(int sourceStart, char[] target, int targetStart, int length) throws XMLStreamException { aoqi@0: if(xopReferencePresent){ aoqi@0: if(target == null){ aoqi@0: throw new NullPointerException("target char array can't be null") ; aoqi@0: } aoqi@0: aoqi@0: if(targetStart < 0 || length < 0 || sourceStart < 0 || targetStart >= target.length || aoqi@0: (targetStart + length ) > target.length) { aoqi@0: throw new IndexOutOfBoundsException(); aoqi@0: } aoqi@0: aoqi@0: int textLength = base64AttData.length(); aoqi@0: if(sourceStart > textLength) aoqi@0: throw new IndexOutOfBoundsException(); aoqi@0: aoqi@0: if(base64EncodedText == null){ aoqi@0: base64EncodedText = new char[base64AttData.length()]; aoqi@0: base64AttData.writeTo(base64EncodedText, 0); aoqi@0: } aoqi@0: aoqi@0: int copiedLength = Math.min(textLength - sourceStart, length); aoqi@0: System.arraycopy(base64EncodedText, sourceStart , target, targetStart, copiedLength); aoqi@0: return copiedLength; aoqi@0: } aoqi@0: return reader.getTextCharacters(sourceStart, target, targetStart, length); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public String getText() { aoqi@0: if (xopReferencePresent) { aoqi@0: return base64AttData.toString(); aoqi@0: } aoqi@0: return reader.getText(); aoqi@0: } aoqi@0: aoqi@0: protected boolean isXopReference() throws XMLStreamException { aoqi@0: return xopReferencePresent; aoqi@0: } aoqi@0: aoqi@0: protected String getXopHref() { aoqi@0: return xopHref; aoqi@0: } aoqi@0: aoqi@0: public MimeMultipartParser getMimeMultipartParser() { aoqi@0: return mimeMP; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: }