001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036: package com.sun.xml.ws.message.saaj;
037:
038: import com.sun.istack.NotNull;
039: import com.sun.istack.XMLStreamException2;
040: import com.sun.xml.bind.api.Bridge;
041: import com.sun.xml.bind.unmarshaller.DOMScanner;
042: import com.sun.xml.ws.api.SOAPVersion;
043: import com.sun.xml.ws.api.message.Attachment;
044: import com.sun.xml.ws.api.message.AttachmentSet;
045: import com.sun.xml.ws.api.message.HeaderList;
046: import com.sun.xml.ws.api.message.Message;
047: import com.sun.xml.ws.api.message.Packet;
048: import com.sun.xml.ws.message.AttachmentUnmarshallerImpl;
049: import com.sun.xml.ws.streaming.DOMStreamReader;
050: import com.sun.xml.ws.util.DOMUtil;
051: import org.w3c.dom.Element;
052: import org.w3c.dom.Node;
053: import org.xml.sax.ContentHandler;
054: import org.xml.sax.ErrorHandler;
055: import org.xml.sax.SAXException;
056:
057: import javax.activation.DataHandler;
058: import javax.xml.bind.JAXBException;
059: import javax.xml.bind.Unmarshaller;
060: import javax.xml.soap.AttachmentPart;
061: import javax.xml.soap.SOAPBody;
062: import javax.xml.soap.SOAPEnvelope;
063: import javax.xml.soap.SOAPException;
064: import javax.xml.soap.SOAPHeader;
065: import javax.xml.soap.SOAPHeaderElement;
066: import javax.xml.soap.SOAPMessage;
067: import javax.xml.stream.XMLStreamException;
068: import javax.xml.stream.XMLStreamReader;
069: import javax.xml.stream.XMLStreamWriter;
070: import javax.xml.transform.Source;
071: import javax.xml.transform.dom.DOMSource;
072: import javax.xml.transform.stream.StreamSource;
073: import javax.xml.ws.WebServiceException;
074: import java.io.IOException;
075: import java.io.InputStream;
076: import java.io.OutputStream;
077: import java.util.HashMap;
078: import java.util.Iterator;
079: import java.util.List;
080: import java.util.Map;
081:
082: /**
083: * {@link Message} implementation backed by {@link SOAPMessage}.
084: *
085: * @author Vivek Pandey
086: */
087: public class SAAJMessage extends Message {
088: private final SOAPMessage sm;
089: private HeaderList headers;
090: private String payloadLocalName;
091: private String payloadNamspace;
092: private List<Element> bodyParts;
093: private Element payload;
094:
095: private boolean parsedHeader;
096:
097: public SAAJMessage(SOAPMessage sm) {
098: this .sm = sm;
099:
100: try {
101: Node body = sm.getSOAPBody();
102: //cature all the body elements
103: bodyParts = DOMUtil.getChildElements(body);
104:
105: //we treat payload as the first body part
106: payload = bodyParts.size() > 0 ? bodyParts.get(0) : null;
107: // hope this is correct. Caching the localname and namespace of the payload should be fine
108: // but what about if a Handler replaces the payload with something else? Weel, may be it
109: // will be error condition anyway
110: if (payload != null) {
111: payloadLocalName = payload.getLocalName();
112: payloadNamspace = payload.getNamespaceURI();
113: }
114: } catch (SOAPException e) {
115: throw new WebServiceException(e);
116: }
117: }
118:
119: /**
120: * This constructor is a convenience and called by the {@link #copy}
121: * @param headers
122: * @param sm
123: */
124: private SAAJMessage(HeaderList headers, AttachmentSet as,
125: SOAPMessage sm) {
126: this (sm);
127: this .headers = headers;
128: this .attachmentSet = as;
129: }
130:
131: public boolean hasHeaders() {
132: return getHeaders().size() > 0;
133: }
134:
135: /**
136: * Gets all the headers of this message.
137: *
138: * @return always return the same non-null object.
139: */
140: public HeaderList getHeaders() {
141: if (parsedHeader)
142: return headers;
143:
144: if (headers == null)
145: headers = new HeaderList();
146:
147: try {
148: SOAPHeader header = sm.getSOAPHeader();
149: if (header != null) {
150: Iterator iter = header.examineAllHeaderElements();
151: while (iter.hasNext()) {
152: headers.add(new SAAJHeader((SOAPHeaderElement) iter
153: .next()));
154: }
155: }
156: parsedHeader = true;
157: } catch (SOAPException e) {
158: e.printStackTrace();
159: }
160: return headers;
161: }
162:
163: /**
164: * Gets the attachments of this message
165: * (attachments live outside a message.)
166: */
167: @Override
168: @NotNull
169: public AttachmentSet getAttachments() {
170: if (attachmentSet == null)
171: attachmentSet = new SAAJAttachmentSet(sm);
172: return attachmentSet;
173: }
174:
175: protected boolean hasAttachments() {
176: return !getAttachments().isEmpty();
177: }
178:
179: /**
180: * Gets the local name of the payload element.
181: */
182: public String getPayloadLocalPart() {
183: return payloadLocalName;
184: }
185:
186: /**
187: * Gets the namespace URI of the payload element.
188: */
189: public String getPayloadNamespaceURI() {
190: return payloadNamspace;
191: }
192:
193: public boolean hasPayload() {
194: return payloadNamspace != null;
195: }
196:
197: /**
198: * Consumes this message including the envelope.
199: * returns it as a {@link javax.xml.transform.Source} object.
200: */
201: public Source readEnvelopeAsSource() {
202: try {
203: SOAPEnvelope se = sm.getSOAPPart().getEnvelope();
204: return new DOMSource(se);
205: } catch (SOAPException e) {
206: throw new WebServiceException(e);
207: }
208: }
209:
210: /**
211: * Returns the payload as a {@link javax.xml.transform.Source} object.
212: *
213: * Can't really give all the body parts inside soapenv:Body as Source
214: * cant take only one part.
215: *
216: * <p/>
217: * This consumes the message.
218: */
219: public Source readPayloadAsSource() {
220: return (payload != null) ? new DOMSource(payload) : null;
221: }
222:
223: /**
224: * Creates the equivalent {@link javax.xml.soap.SOAPMessage} from this message.
225: * <p/>
226: * This consumes the message.
227: */
228: public SOAPMessage readAsSOAPMessage() {
229: return sm;
230: }
231:
232: public SOAPMessage readAsSOAPMessage(Packet packet, boolean inbound)
233: throws SOAPException {
234: return sm;
235: }
236:
237: /**
238: * Reads the payload as a JAXB object by using the given unmarshaller.
239: * <p/>
240: * This consumes the message.
241: */
242: public <T> T readPayloadAsJAXB(Unmarshaller unmarshaller)
243: throws JAXBException {
244: try {
245: Node pn = sm.getSOAPBody().getFirstChild();
246: if (pn != null)
247: return (T) unmarshaller.unmarshal(pn);
248: return null;
249: } catch (SOAPException e) {
250: throw new WebServiceException(e);
251: }
252: }
253:
254: public <T> T readPayloadAsJAXB(Bridge<T> bridge)
255: throws JAXBException {
256: try {
257: Node pn = DOMUtil.getFirstElementChild(sm.getSOAPBody());
258: if (pn != null)
259: return bridge
260: .unmarshal(pn, new AttachmentUnmarshallerImpl(
261: getAttachments()));
262: return null;
263: } catch (SOAPException e) {
264: throw new WebServiceException(e);
265: }
266: }
267:
268: /**
269: * Reads the payload as a {@link javax.xml.stream.XMLStreamReader}
270: * <p/>
271: * This consumes the message.
272: */
273: public XMLStreamReader readPayload() throws XMLStreamException {
274: if (payload == null)
275: return null;
276:
277: DOMStreamReader dss = new DOMStreamReader();
278: dss.setCurrentNode(payload);
279: dss.nextTag();
280: assert dss.getEventType() == XMLStreamReader.START_ELEMENT;
281: return dss;
282: }
283:
284: /**
285: * Writes the payload to StAX.
286: * <p/>
287: * This method writes just the payload of the message to the writer.
288: * This consumes the message.
289: */
290: public void writePayloadTo(XMLStreamWriter sw) {
291: try {
292: for (Element part : bodyParts)
293: DOMUtil.serializeNode(part, sw);
294: } catch (XMLStreamException e) {
295: throw new WebServiceException(e);
296: }
297: }
298:
299: public void writeTo(ContentHandler contentHandler,
300: ErrorHandler errorHandler) throws SAXException {
301: DOMScanner ds = new DOMScanner();
302: ds.setContentHandler(contentHandler);
303: ds.scan(sm.getSOAPPart());
304: }
305:
306: /**
307: * Creates a copy of a {@link com.sun.xml.ws.api.message.Message}.
308: * <p/>
309: * <p/>
310: * This method creates a new {@link com.sun.xml.ws.api.message.Message} whose header/payload/attachments/properties
311: * are identical to this {@link com.sun.xml.ws.api.message.Message}. Once created, the created {@link com.sun.xml.ws.api.message.Message}
312: * and the original {@link com.sun.xml.ws.api.message.Message} behaves independently --- adding header/
313: * attachment to one {@link com.sun.xml.ws.api.message.Message} doesn't affect another {@link com.sun.xml.ws.api.message.Message}
314: * at all.
315: * <p/>
316: * <h3>Design Rationale</h3>
317: * <p/>
318: * Since a {@link com.sun.xml.ws.api.message.Message} body is read-once, sometimes
319: * (such as when you do fail-over, or WS-RM) you need to
320: * create an idential copy of a {@link com.sun.xml.ws.api.message.Message}.
321: * <p/>
322: * <p/>
323: * The actual copy operation depends on the layout
324: * of the data in memory, hence it's best to be done by
325: * the {@link com.sun.xml.ws.api.message.Message} implementation itself.
326: */
327: public Message copy() {
328: try {
329: SOAPBody sb = sm.getSOAPPart().getEnvelope().getBody();
330: SOAPMessage msg = SOAPVersion.fromNsUri(sb
331: .getNamespaceURI()).saajMessageFactory
332: .createMessage();
333: SOAPBody newBody = msg.getSOAPPart().getEnvelope()
334: .getBody();
335: for (Element part : bodyParts) {
336: Node n = newBody.getOwnerDocument().importNode(part,
337: true);
338: newBody.appendChild(n);
339: }
340: return new SAAJMessage(getHeaders(), getAttachments(), msg);
341: } catch (SOAPException e) {
342: throw new WebServiceException(e);
343: }
344: }
345:
346: private class SAAJAttachment implements Attachment {
347:
348: AttachmentPart ap;
349:
350: public SAAJAttachment(AttachmentPart part) {
351: this .ap = part;
352: }
353:
354: /**
355: * Content ID of the attachment. Uniquely identifies an attachment.
356: */
357: public String getContentId() {
358: return ap.getContentId();
359: }
360:
361: /**
362: * Gets the MIME content-type of this attachment.
363: */
364: public String getContentType() {
365: return ap.getContentType();
366: }
367:
368: /**
369: * Gets the attachment as an exact-length byte array.
370: */
371: public byte[] asByteArray() {
372: try {
373: return ap.getRawContentBytes();
374: } catch (SOAPException e) {
375: throw new WebServiceException(e);
376: }
377: }
378:
379: /**
380: * Gets the attachment as a {@link javax.activation.DataHandler}.
381: */
382: public DataHandler asDataHandler() {
383: try {
384: return ap.getDataHandler();
385: } catch (SOAPException e) {
386: throw new WebServiceException(e);
387: }
388: }
389:
390: /**
391: * Gets the attachment as a {@link javax.xml.transform.Source}.
392: * Note that there's no guarantee that the attachment is actually an XML.
393: */
394: public Source asSource() {
395: try {
396: return new StreamSource(ap.getRawContent());
397: } catch (SOAPException e) {
398: throw new WebServiceException(e);
399: }
400: }
401:
402: /**
403: * Obtains this attachment as an {@link java.io.InputStream}.
404: */
405: public InputStream asInputStream() {
406: try {
407: return ap.getRawContent();
408: } catch (SOAPException e) {
409: throw new WebServiceException(e);
410: }
411: }
412:
413: /**
414: * Writes the contents of the attachment into the given stream.
415: */
416: public void writeTo(OutputStream os) throws IOException {
417: os.write(asByteArray());
418: }
419:
420: /**
421: * Writes this attachment to the given {@link javax.xml.soap.SOAPMessage}.
422: */
423: public void writeTo(SOAPMessage saaj) {
424: saaj.addAttachmentPart(ap);
425: }
426:
427: AttachmentPart asAttachmentPart() {
428: return ap;
429: }
430: }
431:
432: /**
433: * {@link AttachmentSet} for SAAJ.
434: *
435: * SAAJ wants '<' and '>' for the content ID, but {@link AttachmentSet}
436: * doesn't. S this class also does the conversion between them.
437: */
438: private class SAAJAttachmentSet implements AttachmentSet {
439:
440: private Map<String, Attachment> attMap;
441: private Iterator attIter;
442:
443: public SAAJAttachmentSet(SOAPMessage sm) {
444: attIter = sm.getAttachments();
445: }
446:
447: /**
448: * Gets the attachment by the content ID.
449: *
450: * @return null
451: * if no such attachment exist.
452: */
453: public Attachment get(String contentId) {
454: // if this is the first time then create the attachment Map
455: if (attMap == null) {
456: if (!attIter.hasNext())
457: return null;
458: attMap = createAttachmentMap();
459: }
460: if (contentId.charAt(0) != '<') {
461: return attMap.get('<' + contentId + '>');
462: }
463: return attMap.get(contentId);
464: }
465:
466: public boolean isEmpty() {
467: if (attMap != null)
468: return attMap.isEmpty();
469: else
470: return !attIter.hasNext();
471: }
472:
473: /**
474: * Returns an iterator over a set of elements of type T.
475: *
476: * @return an Iterator.
477: */
478: public Iterator<Attachment> iterator() {
479: if (attMap == null) {
480: attMap = createAttachmentMap();
481: }
482: return attMap.values().iterator();
483: }
484:
485: private Map<String, Attachment> createAttachmentMap() {
486: HashMap<String, Attachment> map = new HashMap<String, Attachment>();
487: while (attIter.hasNext()) {
488: AttachmentPart ap = (AttachmentPart) attIter.next();
489: map.put(ap.getContentId(), new SAAJAttachment(ap));
490: }
491: return map;
492: }
493:
494: public void add(Attachment att) {
495: attMap.put('<' + att.getContentId() + '>', att);
496: }
497: }
498:
499: public void writeTo(XMLStreamWriter writer)
500: throws XMLStreamException {
501: try {
502: writer.writeStartDocument();
503: SOAPEnvelope env = sm.getSOAPPart().getEnvelope();
504: DOMUtil.writeTagWithAttributes(env, writer);
505: if (hasHeaders()) {
506: writer.writeStartElement(env.getPrefix(), "Header", env
507: .getNamespaceURI());
508: int len = headers.size();
509: for (int i = 0; i < len; i++) {
510: headers.get(i).writeTo(writer);
511: }
512: writer.writeEndElement();
513: }
514:
515: DOMUtil.serializeNode(sm.getSOAPBody(), writer);
516: writer.writeEndElement();
517: writer.writeEndDocument();
518: writer.flush();
519: } catch (SOAPException ex) {
520: throw new XMLStreamException2(ex);
521: //for now. ask jaxws team what to do.
522: }
523:
524: }
525:
526: }
|