1 /* 2 * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.xml.internal.messaging.saaj.util.stax; 27 28 import java.io.OutputStream; 29 import java.util.Arrays; 30 import java.util.Iterator; 31 import java.util.UUID; 32 33 import javax.activation.DataHandler; 34 import javax.xml.bind.attachment.AttachmentMarshaller; 35 import javax.xml.soap.SOAPException; 36 import javax.xml.soap.SOAPMessage; 37 import javax.xml.stream.XMLStreamException; 38 39 import com.sun.xml.internal.org.jvnet.staxex.Base64Data; 40 import com.sun.xml.internal.org.jvnet.staxex.BinaryText; 41 import com.sun.xml.internal.org.jvnet.staxex.MtomEnabled; 42 import com.sun.xml.internal.org.jvnet.staxex.NamespaceContextEx; 43 import com.sun.xml.internal.org.jvnet.staxex.StreamingDataHandler; 44 import com.sun.xml.internal.org.jvnet.staxex.XMLStreamWriterEx; 45 import com.sun.xml.internal.org.jvnet.staxex.util.MtomStreamWriter; 46 // 47 //import com.sun.xml.internal.ws.api.message.saaj.SaajStaxWriter; 48 //import com.sun.xml.internal.ws.developer.StreamingDataHandler; 49 //import com.sun.xml.internal.ws.streaming.MtomStreamWriter; 50 51 /** 52 * SaajStaxWriterEx converts XMLStreamWriterEx calls to build an orasaaj SOAPMessage with BinaryTextImpl. 53 * 54 * @author shih-chang.chen@oracle.com 55 */ 56 public class SaajStaxWriterEx extends SaajStaxWriter implements XMLStreamWriterEx, MtomStreamWriter { 57 58 static final protected String xopNS = "http://www.w3.org/2004/08/xop/include"; 59 static final protected String Include = "Include"; 60 static final protected String href = "href"; 61 62 private enum State {xopInclude, others}; 63 private State state = State.others; 64 private BinaryText binaryText; 65 66 public SaajStaxWriterEx(SOAPMessage msg, String uri) throws SOAPException { 67 super(msg, uri); 68 } 69 70 public void writeStartElement(String prefix, String ln, String ns) throws XMLStreamException { 71 if (xopNS.equals(ns) && Include.equals(ln)) { 72 state = State.xopInclude; 73 return; 74 } else { 75 super.writeStartElement(prefix, ln, ns); 76 } 77 } 78 79 @Override 80 public void writeEndElement() throws XMLStreamException { 81 if (state.equals(State.xopInclude)) { 82 state = State.others; 83 } else { 84 super.writeEndElement(); 85 } 86 } 87 88 @Override 89 public void writeAttribute(String prefix, String ns, String ln, String value) throws XMLStreamException { 90 if (binaryText != null && href.equals(ln)) { 91 return; 92 } else { 93 super.writeAttribute(prefix, ns, ln, value); 94 } 95 } 96 97 // @Override 98 // public void writeComment(String data) throws XMLStreamException { 99 // ((ElementImpl)currentElement).addCommentNode(data); 100 // } 101 // 102 // @Override 103 // public void writeCData(String data) throws XMLStreamException { 104 // CDataTextImpl cdt = new CDataTextImpl(soap.getSOAPPart(), data); 105 // currentElement.appendChild(cdt); 106 // } 107 108 @Override 109 public NamespaceContextEx getNamespaceContext() { 110 return new NamespaceContextEx() { 111 public String getNamespaceURI(String prefix) { 112 return currentElement.getNamespaceURI(prefix); 113 } 114 public String getPrefix(String namespaceURI) { 115 return currentElement.lookupPrefix(namespaceURI); 116 } 117 public Iterator getPrefixes(final String namespaceURI) { 118 return new Iterator() { 119 String prefix = getPrefix(namespaceURI); 120 public boolean hasNext() { 121 return (prefix != null); 122 } 123 public Object next() { 124 if (prefix == null) throw new java.util.NoSuchElementException(); 125 String next = prefix; 126 prefix = null; 127 return next; 128 } 129 public void remove() {} 130 }; 131 } 132 public Iterator<Binding> iterator() { 133 return new Iterator<Binding>() { 134 public boolean hasNext() { return false; } 135 public Binding next() { return null; } 136 public void remove() {} 137 }; 138 } 139 }; 140 } 141 142 @Override 143 public void writeBinary(DataHandler data) throws XMLStreamException { 144 // binaryText = BinaryTextImpl.createBinaryTextFromDataHandler((MessageImpl)soap, null, currentElement.getOwnerDocument(), data); 145 // currentElement.appendChild(binaryText); 146 addBinaryText(data); 147 } 148 149 @Override 150 public OutputStream writeBinary(String arg0) throws XMLStreamException { 151 return null; 152 } 153 154 @Override 155 public void writeBinary(byte[] data, int offset, int length, String contentType) throws XMLStreamException { 156 // if (mtomThreshold == -1 || mtomThreshold > length) return null; 157 byte[] bytes = (offset == 0 && length == data.length) ? data : Arrays.copyOfRange(data, offset, offset + length); 158 if (currentElement instanceof MtomEnabled) { 159 binaryText = ((MtomEnabled) currentElement).addBinaryText(bytes); 160 } else { 161 throw new IllegalStateException("The currentElement is not MtomEnabled " + currentElement); 162 } 163 } 164 165 @Override 166 public void writePCDATA(CharSequence arg0) throws XMLStreamException { 167 if (arg0 instanceof Base64Data) { 168 // The fix of StreamReaderBufferCreator preserves this dataHandler 169 addBinaryText(((Base64Data) arg0).getDataHandler()); 170 } else { 171 // We should not normally get here as we expect a DataHandler, 172 // but this is the most general solution. If we do get 173 // something other than a Data Handler, create a Text node with 174 // the data. Another alternative would be to throw an exception, 175 // but in the most general case, we don't know whether this input 176 // is expected. 177 try { 178 currentElement.addTextNode(arg0.toString()); 179 } catch (SOAPException e) { 180 throw new XMLStreamException("Cannot add Text node", e); 181 } 182 } 183 } 184 185 static private String encodeCid() { 186 String cid = "example.jaxws.sun.com"; 187 String name = UUID.randomUUID() + "@"; 188 return name + cid; 189 } 190 191 private String addBinaryText(DataHandler data) { 192 String hrefOrCid = null; 193 if (data instanceof StreamingDataHandler) { 194 hrefOrCid = ((StreamingDataHandler) data).getHrefCid(); 195 } 196 if (hrefOrCid == null) hrefOrCid = encodeCid(); 197 198 String prefixedCid = (hrefOrCid.startsWith("cid:")) ? hrefOrCid : "cid:" + hrefOrCid; 199 // Should we do the threshold processing on DataHandler ? But that would be 200 // expensive as DataHolder need to read the data again from its source 201 //binaryText = BinaryTextImpl.createBinaryTextFromDataHandler((MessageImpl) soap, prefixedCid, currentElement.getOwnerDocument(), data); 202 //currentElement.appendChild(binaryText); 203 if (currentElement instanceof MtomEnabled) { 204 binaryText = ((MtomEnabled) currentElement).addBinaryText(prefixedCid, data); 205 } else { 206 throw new IllegalStateException("The currentElement is not MtomEnabled " + currentElement); 207 } 208 return hrefOrCid; 209 } 210 211 public AttachmentMarshaller getAttachmentMarshaller() { 212 return new AttachmentMarshaller() { 213 @Override 214 public String addMtomAttachment(DataHandler data, String ns, String ln) { 215 // if (mtomThreshold == -1) return null; 216 String hrefOrCid = addBinaryText(data); 217 // return binaryText.getHref(); 218 return hrefOrCid; 219 } 220 221 @Override 222 public String addMtomAttachment(byte[] data, int offset, int length, String mimeType, String ns, String ln) { 223 // if (mtomThreshold == -1 || mtomThreshold > length) return null; 224 byte[] bytes = (offset == 0 && length == data.length) ? data : Arrays.copyOfRange(data, offset, offset + length); 225 // binaryText = (BinaryTextImpl) ((ElementImpl) currentElement).addAsBase64TextNode(bytes); 226 if (currentElement instanceof MtomEnabled) { 227 binaryText = ((MtomEnabled) currentElement).addBinaryText(bytes); 228 } else { 229 throw new IllegalStateException("The currentElement is not MtomEnabled " + currentElement); 230 } 231 return binaryText.getHref(); 232 } 233 234 @Override 235 public String addSwaRefAttachment(DataHandler data) { 236 return "cid:"+encodeCid(); 237 } 238 239 @Override 240 public boolean isXOPPackage() { 241 return true; 242 } 243 }; 244 } 245 }