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

Fri, 14 Feb 2014 11:13:45 +0100

author
mkos
date
Fri, 14 Feb 2014 11:13:45 +0100
changeset 515
6cd506508147
parent 397
b99d7e355d4b
child 637
9c07ef4934dd
permissions
-rw-r--r--

8026188: Enhance envelope factory
Summary: Avoiding caching data initialized via TCCL in static context; fix also reviewed by Alexander Fomin
Reviewed-by: ahgross, mgrebac, skoivu

ohair@286 1 /*
alanb@368 2 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
ohair@286 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
ohair@286 4 *
ohair@286 5 * This code is free software; you can redistribute it and/or modify it
ohair@286 6 * under the terms of the GNU General Public License version 2 only, as
ohair@286 7 * published by the Free Software Foundation. Oracle designates this
ohair@286 8 * particular file as subject to the "Classpath" exception as provided
ohair@286 9 * by Oracle in the LICENSE file that accompanied this code.
ohair@286 10 *
ohair@286 11 * This code is distributed in the hope that it will be useful, but WITHOUT
ohair@286 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
ohair@286 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
ohair@286 14 * version 2 for more details (a copy is included in the LICENSE file that
ohair@286 15 * accompanied this code).
ohair@286 16 *
ohair@286 17 * You should have received a copy of the GNU General Public License version
ohair@286 18 * 2 along with this work; if not, write to the Free Software Foundation,
ohair@286 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
ohair@286 20 *
ohair@286 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
ohair@286 22 * or visit www.oracle.com if you need additional information or have any
ohair@286 23 * questions.
ohair@286 24 */
ohair@286 25
ohair@286 26 package com.sun.xml.internal.ws.encoding;
ohair@286 27
ohair@286 28 import com.sun.istack.internal.NotNull;
ohair@286 29 import com.sun.xml.internal.bind.DatatypeConverterImpl;
ohair@286 30 import com.sun.xml.internal.ws.api.SOAPVersion;
ohair@286 31 import com.sun.xml.internal.ws.api.WSFeatureList;
ohair@286 32 import com.sun.xml.internal.ws.api.message.Attachment;
ohair@286 33 import com.sun.xml.internal.ws.api.message.AttachmentSet;
ohair@286 34 import com.sun.xml.internal.ws.api.message.Packet;
ohair@286 35 import com.sun.xml.internal.ws.api.pipe.ContentType;
ohair@286 36 import com.sun.xml.internal.ws.api.pipe.StreamSOAPCodec;
ohair@286 37 import com.sun.xml.internal.ws.api.streaming.XMLStreamReaderFactory;
ohair@286 38 import com.sun.xml.internal.ws.api.streaming.XMLStreamWriterFactory;
ohair@286 39 import com.sun.xml.internal.ws.developer.SerializationFeature;
alanb@368 40 import com.sun.xml.internal.ws.developer.StreamingDataHandler;
ohair@286 41 import com.sun.xml.internal.ws.message.MimeAttachmentSet;
ohair@286 42 import com.sun.xml.internal.ws.streaming.XMLStreamWriterUtil;
ohair@286 43 import com.sun.xml.internal.ws.util.ByteArrayDataSource;
mkos@397 44 import com.sun.xml.internal.ws.util.xml.NamespaceContextExAdaper;
ohair@286 45 import com.sun.xml.internal.ws.util.xml.XMLStreamReaderFilter;
ohair@286 46 import com.sun.xml.internal.ws.util.xml.XMLStreamWriterFilter;
ohair@286 47 import com.sun.xml.internal.ws.streaming.MtomStreamWriter;
ohair@286 48 import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil;
ohair@286 49 import com.sun.xml.internal.ws.server.UnsupportedMediaException;
ohair@286 50 import com.sun.xml.internal.org.jvnet.staxex.Base64Data;
ohair@286 51 import com.sun.xml.internal.org.jvnet.staxex.NamespaceContextEx;
ohair@286 52 import com.sun.xml.internal.org.jvnet.staxex.XMLStreamReaderEx;
ohair@286 53 import com.sun.xml.internal.org.jvnet.staxex.XMLStreamWriterEx;
ohair@286 54
ohair@286 55 import javax.activation.DataHandler;
ohair@286 56 import javax.xml.namespace.NamespaceContext;
ohair@286 57 import javax.xml.stream.XMLStreamConstants;
ohair@286 58 import javax.xml.stream.XMLStreamException;
ohair@286 59 import javax.xml.stream.XMLStreamReader;
ohair@286 60 import javax.xml.stream.XMLStreamWriter;
ohair@286 61 import javax.xml.ws.WebServiceException;
ohair@286 62 import javax.xml.ws.soap.MTOMFeature;
ohair@286 63 import javax.xml.bind.attachment.AttachmentMarshaller;
ohair@286 64 import java.io.IOException;
ohair@286 65 import java.io.OutputStream;
ohair@286 66 import java.io.UnsupportedEncodingException;
ohair@286 67 import java.net.URLDecoder;
ohair@286 68 import java.nio.channels.WritableByteChannel;
ohair@286 69 import java.nio.charset.Charset;
ohair@286 70 import java.util.ArrayList;
ohair@286 71 import java.util.Iterator;
ohair@286 72 import java.util.List;
ohair@286 73 import java.util.Map;
ohair@286 74 import java.util.UUID;
ohair@286 75
ohair@286 76 /**
alanb@368 77 * Mtom message Codec. It can be used even for non-soap message's mtom encoding.
ohair@286 78 *
ohair@286 79 * @author Vivek Pandey
ohair@286 80 * @author Jitendra Kotamraju
ohair@286 81 */
ohair@286 82 public class MtomCodec extends MimeCodec {
ohair@286 83
ohair@286 84 public static final String XOP_XML_MIME_TYPE = "application/xop+xml";
mkos@384 85 public static final String XOP_LOCALNAME = "Include";
mkos@384 86 public static final String XOP_NAMESPACEURI = "http://www.w3.org/2004/08/xop/include";
ohair@286 87
ohair@286 88 private final StreamSOAPCodec codec;
ohair@286 89 private final MTOMFeature mtomFeature;
ohair@286 90 private final SerializationFeature sf;
ohair@286 91 private final static String DECODED_MESSAGE_CHARSET = "decodedMessageCharset";
ohair@286 92
ohair@286 93 MtomCodec(SOAPVersion version, StreamSOAPCodec codec, WSFeatureList features){
ohair@286 94 super(version, features);
ohair@286 95 this.codec = codec;
ohair@286 96 sf = features.get(SerializationFeature.class);
ohair@286 97 MTOMFeature mtom = features.get(MTOMFeature.class);
ohair@286 98 if(mtom == null)
ohair@286 99 this.mtomFeature = new MTOMFeature();
ohair@286 100 else
ohair@286 101 this.mtomFeature = mtom;
ohair@286 102 }
ohair@286 103
alanb@368 104 /**
alanb@368 105 * Return the soap 1.1 and soap 1.2 specific XOP packaged ContentType
alanb@368 106 *
alanb@368 107 * @return A non-null content type for soap11 or soap 1.2 content type
alanb@368 108 */
alanb@368 109 @Override
alanb@368 110 public ContentType getStaticContentType(Packet packet) {
alanb@368 111 return getStaticContentTypeStatic(packet, version);
ohair@286 112 }
ohair@286 113
alanb@368 114 public static ContentType getStaticContentTypeStatic(Packet packet, SOAPVersion version) {
alanb@368 115 ContentType ct = (ContentType) packet.getInternalContentType();
alanb@368 116 if ( ct != null ) return ct;
ohair@286 117
alanb@368 118 String uuid = UUID.randomUUID().toString();
alanb@368 119 String boundary = "uuid:" + uuid;
alanb@368 120 String rootId = "<rootpart*"+uuid+"@example.jaxws.sun.com>";
alanb@368 121 String soapActionParameter = SOAPVersion.SOAP_11.equals(version) ? null : createActionParameter(packet);
alanb@368 122
ohair@286 123 String boundaryParameter = "boundary=\"" + boundary +"\"";
alanb@368 124 String messageContentType = MULTIPART_RELATED_MIME_TYPE +
ohair@286 125 ";start=\""+rootId +"\"" +
ohair@286 126 ";type=\"" + XOP_XML_MIME_TYPE + "\";" +
ohair@286 127 boundaryParameter +
ohair@286 128 ";start-info=\"" + version.contentType +
ohair@286 129 (soapActionParameter == null? "" : soapActionParameter) +
ohair@286 130 "\"";
alanb@368 131
alanb@368 132 ContentTypeImpl ctImpl = SOAPVersion.SOAP_11.equals(version) ?
alanb@368 133 new ContentTypeImpl(messageContentType, (packet.soapAction == null)?"":packet.soapAction, null) :
alanb@368 134 new ContentTypeImpl(messageContentType, null, null);
alanb@368 135 ctImpl.setBoundary(boundary);
alanb@368 136 ctImpl.setRootId(rootId);
alanb@368 137 packet.setContentType(ctImpl);
alanb@368 138 return ctImpl;
ohair@286 139 }
ohair@286 140
alanb@368 141 private static String createActionParameter(Packet packet) {
ohair@286 142 return packet.soapAction != null? ";action=\\\""+packet.soapAction+"\\\"" : "";
ohair@286 143 }
ohair@286 144
alanb@368 145 @Override
ohair@286 146 public ContentType encode(Packet packet, OutputStream out) throws IOException {
alanb@368 147 ContentTypeImpl ctImpl = (ContentTypeImpl) this.getStaticContentType(packet);
alanb@368 148 String boundary = ctImpl.getBoundary();
alanb@368 149 String rootId = ctImpl.getRootId();
ohair@286 150
ohair@286 151 if(packet.getMessage() != null){
ohair@286 152 try {
ohair@286 153 String encoding = getPacketEncoding(packet);
ohair@286 154 packet.invocationProperties.remove(DECODED_MESSAGE_CHARSET);
ohair@286 155
alanb@368 156 String actionParameter = getActionParameter(packet, version);
alanb@368 157 String soapXopContentType = getSOAPXopContentType(encoding, version, actionParameter);
ohair@286 158
ohair@286 159 writeln("--"+boundary, out);
alanb@368 160 writeMimeHeaders(soapXopContentType, rootId, out);
ohair@286 161
ohair@286 162 //mtom attachments that need to be written after the root part
ohair@286 163 List<ByteArrayBuffer> mtomAttachments = new ArrayList<ByteArrayBuffer>();
ohair@286 164 MtomStreamWriterImpl writer = new MtomStreamWriterImpl(
alanb@368 165 XMLStreamWriterFactory.create(out, encoding), mtomAttachments, boundary, mtomFeature);
ohair@286 166
ohair@286 167 packet.getMessage().writeTo(writer);
ohair@286 168 XMLStreamWriterFactory.recycle(writer);
ohair@286 169 writeln(out);
ohair@286 170
ohair@286 171 for(ByteArrayBuffer bos : mtomAttachments){
ohair@286 172 bos.write(out);
ohair@286 173 }
ohair@286 174
mkos@384 175 // now write out the attachments in the message that weren't
mkos@384 176 // previously written
mkos@384 177 writeNonMtomAttachments(packet.getMessage().getAttachments(),
mkos@384 178 out, boundary);
ohair@286 179
ohair@286 180 //write out the end boundary
ohair@286 181 writeAsAscii("--"+boundary, out);
ohair@286 182 writeAsAscii("--", out);
ohair@286 183
ohair@286 184 } catch (XMLStreamException e) {
ohair@286 185 throw new WebServiceException(e);
ohair@286 186 }
ohair@286 187 }
ohair@286 188 //now create the boundary for next encode() call
alanb@368 189 // createConteTypeHeader();
alanb@368 190 return ctImpl;
ohair@286 191 }
ohair@286 192
alanb@368 193 public static String getSOAPXopContentType(String encoding, SOAPVersion version,
alanb@368 194 String actionParameter) {
alanb@368 195 return XOP_XML_MIME_TYPE +";charset="+encoding+";type=\""+version.contentType+ actionParameter + "\"";
alanb@368 196 }
alanb@368 197
alanb@368 198 public static String getActionParameter(Packet packet, SOAPVersion version) {
alanb@368 199 return (version == SOAPVersion.SOAP_11) ? "" : createActionParameter(packet);
alanb@368 200 }
alanb@368 201
alanb@368 202 public static class ByteArrayBuffer{
ohair@286 203 final String contentId;
ohair@286 204
alanb@368 205 private final DataHandler dh;
alanb@368 206 private final String boundary;
ohair@286 207
alanb@368 208 ByteArrayBuffer(@NotNull DataHandler dh, String b) {
ohair@286 209 this.dh = dh;
mkos@384 210 String cid = null;
mkos@384 211 if (dh instanceof StreamingDataHandler) {
mkos@384 212 StreamingDataHandler sdh = (StreamingDataHandler) dh;
mkos@384 213 if (sdh.getHrefCid() != null)
mkos@384 214 cid = sdh.getHrefCid();
mkos@384 215 }
mkos@384 216 this.contentId = cid != null ? cid : encodeCid();
alanb@368 217 boundary = b;
ohair@286 218 }
ohair@286 219
alanb@368 220 public void write(OutputStream os) throws IOException {
ohair@286 221 //build attachment frame
ohair@286 222 writeln("--"+boundary, os);
ohair@286 223 writeMimeHeaders(dh.getContentType(), contentId, os);
ohair@286 224 dh.writeTo(os);
ohair@286 225 writeln(os);
ohair@286 226 }
ohair@286 227 }
ohair@286 228
alanb@368 229 public static void writeMimeHeaders(String contentType, String contentId, OutputStream out) throws IOException {
ohair@286 230 String cid = contentId;
ohair@286 231 if(cid != null && cid.length() >0 && cid.charAt(0) != '<')
ohair@286 232 cid = '<' + cid + '>';
ohair@286 233 writeln("Content-Id: " + cid, out);
ohair@286 234 writeln("Content-Type: " + contentType, out);
ohair@286 235 writeln("Content-Transfer-Encoding: binary", out);
ohair@286 236 writeln(out);
ohair@286 237 }
ohair@286 238
mkos@384 239 // Compiler warning for not calling close, but cannot call close,
mkos@384 240 // will consume attachment bytes.
mkos@384 241 @SuppressWarnings("resource")
mkos@384 242 private void writeNonMtomAttachments(AttachmentSet attachments,
mkos@384 243 OutputStream out, String boundary) throws IOException {
mkos@384 244
mkos@384 245 for (Attachment att : attachments) {
mkos@384 246
mkos@384 247 DataHandler dh = att.asDataHandler();
mkos@384 248 if (dh instanceof StreamingDataHandler) {
mkos@384 249 StreamingDataHandler sdh = (StreamingDataHandler) dh;
mkos@384 250 // If DataHandler has href Content-ID, it is MTOM, so skip.
mkos@384 251 if (sdh.getHrefCid() != null)
mkos@384 252 continue;
mkos@384 253 }
mkos@384 254
mkos@384 255 // build attachment frame
mkos@384 256 writeln("--" + boundary, out);
ohair@286 257 writeMimeHeaders(att.getContentType(), att.getContentId(), out);
ohair@286 258 att.writeTo(out);
mkos@384 259 writeln(out); // write \r\n
ohair@286 260 }
ohair@286 261 }
ohair@286 262
alanb@368 263 @Override
ohair@286 264 public ContentType encode(Packet packet, WritableByteChannel buffer) {
ohair@286 265 throw new UnsupportedOperationException();
ohair@286 266 }
ohair@286 267
alanb@368 268 @Override
ohair@286 269 public MtomCodec copy() {
ohair@286 270 return new MtomCodec(version, (StreamSOAPCodec)codec.copy(), features);
ohair@286 271 }
ohair@286 272
alanb@368 273 private static String encodeCid(){
ohair@286 274 String cid="example.jaxws.sun.com";
ohair@286 275 String name = UUID.randomUUID()+"@";
ohair@286 276 return name + cid;
ohair@286 277 }
ohair@286 278
ohair@286 279 @Override
ohair@286 280 protected void decode(MimeMultipartParser mpp, Packet packet) throws IOException {
ohair@286 281 //TODO shouldn't we check for SOAP1.1/SOAP1.2 and throw
ohair@286 282 //TODO UnsupportedMediaException like StreamSOAPCodec
ohair@286 283 String charset = null;
ohair@286 284 String ct = mpp.getRootPart().getContentType();
ohair@286 285 if (ct != null) {
ohair@286 286 charset = new ContentTypeImpl(ct).getCharSet();
ohair@286 287 }
ohair@286 288 if (charset != null && !Charset.isSupported(charset)) {
ohair@286 289 throw new UnsupportedMediaException(charset);
ohair@286 290 }
ohair@286 291
ohair@286 292 if (charset != null) {
ohair@286 293 packet.invocationProperties.put(DECODED_MESSAGE_CHARSET, charset);
ohair@286 294 } else {
ohair@286 295 packet.invocationProperties.remove(DECODED_MESSAGE_CHARSET);
ohair@286 296 }
ohair@286 297
ohair@286 298 // we'd like to reuse those reader objects but unfortunately decoder may be reused
ohair@286 299 // before the decoded message is completely used.
ohair@286 300 XMLStreamReader mtomReader = new MtomXMLStreamReaderEx( mpp,
ohair@286 301 XMLStreamReaderFactory.create(null, mpp.getRootPart().asInputStream(), charset, true)
ohair@286 302 );
ohair@286 303
ohair@286 304 packet.setMessage(codec.decode(mtomReader, new MimeAttachmentSet(mpp)));
alanb@368 305 packet.setMtomFeature(mtomFeature);
alanb@368 306 packet.setContentType(mpp.getContentType());
ohair@286 307 }
ohair@286 308
ohair@286 309 private String getPacketEncoding(Packet packet) {
ohair@286 310 // If SerializationFeature is set, just use that encoding
ohair@286 311 if (sf != null && sf.getEncoding() != null) {
ohair@286 312 return sf.getEncoding().equals("") ? SOAPBindingCodec.DEFAULT_ENCODING : sf.getEncoding();
ohair@286 313 }
alanb@368 314 return determinePacketEncoding(packet);
alanb@368 315 }
ohair@286 316
alanb@368 317 public static String determinePacketEncoding(Packet packet) {
ohair@286 318 if (packet != null && packet.endpoint != null) {
ohair@286 319 // Use request message's encoding for Server-side response messages
ohair@286 320 String charset = (String)packet.invocationProperties.get(DECODED_MESSAGE_CHARSET);
ohair@286 321 return charset == null
ohair@286 322 ? SOAPBindingCodec.DEFAULT_ENCODING : charset;
ohair@286 323 }
ohair@286 324
ohair@286 325 // Use default encoding for client-side request messages
ohair@286 326 return SOAPBindingCodec.DEFAULT_ENCODING;
ohair@286 327 }
ohair@286 328
alanb@368 329 public static class MtomStreamWriterImpl extends XMLStreamWriterFilter implements XMLStreamWriterEx,
ohair@286 330 MtomStreamWriter, HasEncoding {
ohair@286 331 private final List<ByteArrayBuffer> mtomAttachments;
alanb@368 332 private final String boundary;
alanb@368 333 private final MTOMFeature myMtomFeature;
alanb@368 334 public MtomStreamWriterImpl(XMLStreamWriter w, List<ByteArrayBuffer> mtomAttachments, String b, MTOMFeature myMtomFeature) {
ohair@286 335 super(w);
ohair@286 336 this.mtomAttachments = mtomAttachments;
alanb@368 337 this.boundary = b;
alanb@368 338 this.myMtomFeature = myMtomFeature;
ohair@286 339 }
ohair@286 340
alanb@368 341 @Override
ohair@286 342 public void writeBinary(byte[] data, int start, int len, String contentType) throws XMLStreamException {
ohair@286 343 //check threshold and if less write as base64encoded value
alanb@368 344 if(myMtomFeature.getThreshold() > len){
ohair@286 345 writeCharacters(DatatypeConverterImpl._printBase64Binary(data, start, len));
ohair@286 346 return;
ohair@286 347 }
alanb@368 348 ByteArrayBuffer bab = new ByteArrayBuffer(new DataHandler(new ByteArrayDataSource(data, start, len, contentType)), boundary);
ohair@286 349 writeBinary(bab);
ohair@286 350 }
ohair@286 351
alanb@368 352 @Override
ohair@286 353 public void writeBinary(DataHandler dataHandler) throws XMLStreamException {
ohair@286 354 // TODO how do we check threshold and if less inline the data
alanb@368 355 writeBinary(new ByteArrayBuffer(dataHandler, boundary));
ohair@286 356 }
ohair@286 357
alanb@368 358 @Override
ohair@286 359 public OutputStream writeBinary(String contentType) throws XMLStreamException {
ohair@286 360 throw new UnsupportedOperationException();
ohair@286 361 }
ohair@286 362
alanb@368 363 @Override
ohair@286 364 public void writePCDATA(CharSequence data) throws XMLStreamException {
ohair@286 365 if(data == null)
ohair@286 366 return;
ohair@286 367 if(data instanceof Base64Data){
ohair@286 368 Base64Data binaryData = (Base64Data)data;
ohair@286 369 writeBinary(binaryData.getDataHandler());
ohair@286 370 return;
ohair@286 371 }
ohair@286 372 writeCharacters(data.toString());
ohair@286 373 }
ohair@286 374
ohair@286 375 private void writeBinary(ByteArrayBuffer bab) {
ohair@286 376 try {
ohair@286 377 mtomAttachments.add(bab);
mkos@397 378 String prefix = writer.getPrefix(XOP_NAMESPACEURI);
mkos@397 379 if (prefix == null || !prefix.equals("xop")) {
mkos@397 380 writer.setPrefix("xop", XOP_NAMESPACEURI);
mkos@397 381 writer.writeNamespace("xop", XOP_NAMESPACEURI);
mkos@397 382 }
ohair@286 383 writer.writeStartElement(XOP_NAMESPACEURI, XOP_LOCALNAME);
ohair@286 384 writer.writeAttribute("href", "cid:"+bab.contentId);
ohair@286 385 writer.writeEndElement();
ohair@286 386 writer.flush();
ohair@286 387 } catch (XMLStreamException e) {
ohair@286 388 throw new WebServiceException(e);
ohair@286 389 }
ohair@286 390 }
ohair@286 391
ohair@286 392 @Override
ohair@286 393 public Object getProperty(String name) throws IllegalArgumentException {
ohair@286 394 // Hack for JDK6's SJSXP
ohair@286 395 if (name.equals("sjsxp-outputstream") && writer instanceof Map) {
ohair@286 396 Object obj = ((Map) writer).get("sjsxp-outputstream");
ohair@286 397 if (obj != null) {
ohair@286 398 return obj;
ohair@286 399 }
ohair@286 400 }
ohair@286 401 return super.getProperty(name);
ohair@286 402 }
ohair@286 403
ohair@286 404 /**
ohair@286 405 * JAXBMessage writes envelope directly to the OutputStream(for SJSXP, woodstox).
ohair@286 406 * While writing, it calls the AttachmentMarshaller methods for adding attachments.
ohair@286 407 * JAXB writes xop:Include in this case.
ohair@286 408 */
alanb@368 409 @Override
ohair@286 410 public AttachmentMarshaller getAttachmentMarshaller() {
ohair@286 411 return new AttachmentMarshaller() {
ohair@286 412
alanb@368 413 @Override
ohair@286 414 public String addMtomAttachment(DataHandler data, String elementNamespace, String elementLocalName) {
ohair@286 415 // Should we do the threshold processing on DataHandler ? But that would be
ohair@286 416 // expensive as DataHolder need to read the data again from its source
alanb@368 417 ByteArrayBuffer bab = new ByteArrayBuffer(data, boundary);
ohair@286 418 mtomAttachments.add(bab);
ohair@286 419 return "cid:"+bab.contentId;
ohair@286 420 }
ohair@286 421
alanb@368 422 @Override
ohair@286 423 public String addMtomAttachment(byte[] data, int offset, int length, String mimeType, String elementNamespace, String elementLocalName) {
ohair@286 424 // inline the data based on the threshold
alanb@368 425 if (myMtomFeature.getThreshold() > length) {
ohair@286 426 return null; // JAXB inlines the attachment data
ohair@286 427 }
alanb@368 428 ByteArrayBuffer bab = new ByteArrayBuffer(new DataHandler(new ByteArrayDataSource(data, offset, length, mimeType)), boundary);
ohair@286 429 mtomAttachments.add(bab);
ohair@286 430 return "cid:"+bab.contentId;
ohair@286 431 }
ohair@286 432
alanb@368 433 @Override
ohair@286 434 public String addSwaRefAttachment(DataHandler data) {
alanb@368 435 ByteArrayBuffer bab = new ByteArrayBuffer(data, boundary);
ohair@286 436 mtomAttachments.add(bab);
ohair@286 437 return "cid:"+bab.contentId;
ohair@286 438 }
ohair@286 439
ohair@286 440 @Override
ohair@286 441 public boolean isXOPPackage() {
ohair@286 442 return true;
ohair@286 443 }
ohair@286 444 };
ohair@286 445 }
ohair@286 446
alanb@368 447 public List<ByteArrayBuffer> getMtomAttachments() {
alanb@368 448 return this.mtomAttachments;
alanb@368 449 }
alanb@368 450
alanb@368 451 @Override
ohair@286 452 public String getEncoding() {
ohair@286 453 return XMLStreamWriterUtil.getEncoding(writer);
ohair@286 454 }
ohair@286 455
alanb@368 456 private static class MtomNamespaceContextEx implements NamespaceContextEx {
alanb@368 457 private final NamespaceContext nsContext;
ohair@286 458
ohair@286 459 public MtomNamespaceContextEx(NamespaceContext nsContext) {
ohair@286 460 this.nsContext = nsContext;
ohair@286 461 }
ohair@286 462
alanb@368 463 @Override
ohair@286 464 public Iterator<Binding> iterator() {
ohair@286 465 throw new UnsupportedOperationException();
ohair@286 466 }
ohair@286 467
alanb@368 468 @Override
ohair@286 469 public String getNamespaceURI(String prefix) {
ohair@286 470 return nsContext.getNamespaceURI(prefix);
ohair@286 471 }
ohair@286 472
alanb@368 473 @Override
ohair@286 474 public String getPrefix(String namespaceURI) {
ohair@286 475 return nsContext.getPrefix(namespaceURI);
ohair@286 476 }
ohair@286 477
alanb@368 478 @Override
ohair@286 479 public Iterator getPrefixes(String namespaceURI) {
ohair@286 480 return nsContext.getPrefixes(namespaceURI);
ohair@286 481 }
ohair@286 482 }
ohair@286 483
ohair@286 484 @Override
ohair@286 485 public NamespaceContextEx getNamespaceContext() {
ohair@286 486 NamespaceContext nsContext = writer.getNamespaceContext();
ohair@286 487 return new MtomNamespaceContextEx(nsContext);
ohair@286 488 }
ohair@286 489 }
ohair@286 490
alanb@368 491 public static class MtomXMLStreamReaderEx extends XMLStreamReaderFilter implements XMLStreamReaderEx {
ohair@286 492 /**
ohair@286 493 * The parser for the outer MIME 'shell'.
ohair@286 494 */
ohair@286 495 private final MimeMultipartParser mimeMP;
ohair@286 496
ohair@286 497 private boolean xopReferencePresent = false;
ohair@286 498 private Base64Data base64AttData;
ohair@286 499
ohair@286 500 //To be used with #getTextCharacters
ohair@286 501 private char[] base64EncodedText;
ohair@286 502
alanb@368 503 private String xopHref;
alanb@368 504
ohair@286 505 public MtomXMLStreamReaderEx(MimeMultipartParser mimeMP, XMLStreamReader reader) {
ohair@286 506 super(reader);
ohair@286 507 this.mimeMP = mimeMP;
ohair@286 508 }
ohair@286 509
alanb@368 510 @Override
ohair@286 511 public CharSequence getPCDATA() throws XMLStreamException {
ohair@286 512 if(xopReferencePresent){
ohair@286 513 return base64AttData;
ohair@286 514 }
ohair@286 515 return reader.getText();
ohair@286 516 }
ohair@286 517
alanb@368 518 @Override
ohair@286 519 public NamespaceContextEx getNamespaceContext() {
mkos@397 520 return new NamespaceContextExAdaper(reader.getNamespaceContext());
ohair@286 521 }
ohair@286 522
alanb@368 523 @Override
ohair@286 524 public String getElementTextTrim() throws XMLStreamException {
ohair@286 525 throw new UnsupportedOperationException();
ohair@286 526 }
ohair@286 527
alanb@368 528 @Override
ohair@286 529 public int getTextLength() {
ohair@286 530 if (xopReferencePresent) {
ohair@286 531 return base64AttData.length();
ohair@286 532 }
ohair@286 533 return reader.getTextLength();
ohair@286 534 }
ohair@286 535
alanb@368 536 @Override
ohair@286 537 public int getTextStart() {
ohair@286 538 if (xopReferencePresent) {
ohair@286 539 return 0;
ohair@286 540 }
ohair@286 541 return reader.getTextStart();
ohair@286 542 }
ohair@286 543
alanb@368 544 @Override
ohair@286 545 public int getEventType() {
ohair@286 546 if(xopReferencePresent)
ohair@286 547 return XMLStreamConstants.CHARACTERS;
ohair@286 548 return super.getEventType();
ohair@286 549 }
ohair@286 550
alanb@368 551 @Override
ohair@286 552 public int next() throws XMLStreamException {
ohair@286 553 int event = reader.next();
ohair@286 554 if (event == XMLStreamConstants.START_ELEMENT && reader.getLocalName().equals(XOP_LOCALNAME) && reader.getNamespaceURI().equals(XOP_NAMESPACEURI)) {
ohair@286 555 //its xop reference, take the URI reference
ohair@286 556 String href = reader.getAttributeValue(null, "href");
ohair@286 557 try {
alanb@368 558 xopHref = href;
ohair@286 559 Attachment att = getAttachment(href);
ohair@286 560 if(att != null){
alanb@368 561 DataHandler dh = att.asDataHandler();
alanb@368 562 if (dh instanceof StreamingDataHandler) {
alanb@368 563 ((StreamingDataHandler)dh).setHrefCid(att.getContentId());
alanb@368 564 }
ohair@286 565 base64AttData = new Base64Data();
alanb@368 566 base64AttData.set(dh);
ohair@286 567 }
ohair@286 568 xopReferencePresent = true;
ohair@286 569 } catch (IOException e) {
ohair@286 570 throw new WebServiceException(e);
ohair@286 571 }
ohair@286 572 //move to the </xop:Include>
ohair@286 573 XMLStreamReaderUtil.nextElementContent(reader);
ohair@286 574 return XMLStreamConstants.CHARACTERS;
ohair@286 575 }
ohair@286 576 if(xopReferencePresent){
ohair@286 577 xopReferencePresent = false;
ohair@286 578 base64EncodedText = null;
alanb@368 579 xopHref = null;
ohair@286 580 }
ohair@286 581 return event;
ohair@286 582 }
ohair@286 583
ohair@286 584 private String decodeCid(String cid) {
ohair@286 585 try {
ohair@286 586 cid = URLDecoder.decode(cid, "utf-8");
ohair@286 587 } catch (UnsupportedEncodingException e) {
ohair@286 588 //on recceiving side lets not fail now, try to look for it
ohair@286 589 }
ohair@286 590 return cid;
ohair@286 591 }
ohair@286 592
ohair@286 593 private Attachment getAttachment(String cid) throws IOException {
ohair@286 594 if (cid.startsWith("cid:"))
ohair@286 595 cid = cid.substring(4, cid.length());
ohair@286 596 if (cid.indexOf('%') != -1) {
ohair@286 597 cid = decodeCid(cid);
ohair@286 598 return mimeMP.getAttachmentPart(cid);
ohair@286 599 }
ohair@286 600 return mimeMP.getAttachmentPart(cid);
ohair@286 601 }
ohair@286 602
alanb@368 603 @Override
ohair@286 604 public char[] getTextCharacters() {
ohair@286 605 if (xopReferencePresent) {
ohair@286 606 char[] chars = new char[base64AttData.length()];
ohair@286 607 base64AttData.writeTo(chars, 0);
ohair@286 608 return chars;
ohair@286 609 }
ohair@286 610 return reader.getTextCharacters();
ohair@286 611 }
ohair@286 612
alanb@368 613 @Override
ohair@286 614 public int getTextCharacters(int sourceStart, char[] target, int targetStart, int length) throws XMLStreamException {
ohair@286 615 if(xopReferencePresent){
ohair@286 616 if(target == null){
ohair@286 617 throw new NullPointerException("target char array can't be null") ;
ohair@286 618 }
ohair@286 619
ohair@286 620 if(targetStart < 0 || length < 0 || sourceStart < 0 || targetStart >= target.length ||
ohair@286 621 (targetStart + length ) > target.length) {
ohair@286 622 throw new IndexOutOfBoundsException();
ohair@286 623 }
ohair@286 624
ohair@286 625 int textLength = base64AttData.length();
ohair@286 626 if(sourceStart > textLength)
ohair@286 627 throw new IndexOutOfBoundsException();
ohair@286 628
ohair@286 629 if(base64EncodedText == null){
ohair@286 630 base64EncodedText = new char[base64AttData.length()];
ohair@286 631 base64AttData.writeTo(base64EncodedText, 0);
ohair@286 632 }
ohair@286 633
ohair@286 634 int copiedLength = Math.min(textLength - sourceStart, length);
ohair@286 635 System.arraycopy(base64EncodedText, sourceStart , target, targetStart, copiedLength);
ohair@286 636 return copiedLength;
ohair@286 637 }
ohair@286 638 return reader.getTextCharacters(sourceStart, target, targetStart, length);
ohair@286 639 }
ohair@286 640
alanb@368 641 @Override
ohair@286 642 public String getText() {
ohair@286 643 if (xopReferencePresent) {
ohair@286 644 return base64AttData.toString();
ohair@286 645 }
ohair@286 646 return reader.getText();
ohair@286 647 }
alanb@368 648
alanb@368 649 protected boolean isXopReference() throws XMLStreamException {
alanb@368 650 return xopReferencePresent;
alanb@368 651 }
alanb@368 652
alanb@368 653 protected String getXopHref() {
alanb@368 654 return xopHref;
alanb@368 655 }
alanb@368 656
alanb@368 657 public MimeMultipartParser getMimeMultipartParser() {
alanb@368 658 return mimeMP;
alanb@368 659 }
ohair@286 660 }
ohair@286 661
ohair@286 662 }

mercurial