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.jaxb;
037:
038: import com.sun.istack.FragmentContentHandler;
039: import com.sun.istack.NotNull;
040: import com.sun.xml.bind.api.Bridge;
041: import com.sun.xml.bind.api.JAXBRIContext;
042: import com.sun.xml.stream.buffer.MutableXMLStreamBuffer;
043: import com.sun.xml.stream.buffer.XMLStreamBuffer;
044: import com.sun.xml.stream.buffer.XMLStreamBufferResult;
045: import com.sun.xml.ws.api.SOAPVersion;
046: import com.sun.xml.ws.api.message.AttachmentSet;
047: import com.sun.xml.ws.api.message.HeaderList;
048: import com.sun.xml.ws.api.message.Message;
049: import com.sun.xml.ws.message.AbstractMessageImpl;
050: import com.sun.xml.ws.message.AttachmentSetImpl;
051: import com.sun.xml.ws.message.RootElementSniffer;
052: import com.sun.xml.ws.message.stream.StreamMessage;
053: import com.sun.xml.ws.streaming.XMLStreamWriterUtil;
054: import com.sun.xml.ws.streaming.XMLStreamReaderUtil;
055: import org.xml.sax.ContentHandler;
056: import org.xml.sax.ErrorHandler;
057: import org.xml.sax.SAXException;
058:
059: import javax.xml.bind.JAXBElement;
060: import javax.xml.bind.JAXBException;
061: import javax.xml.bind.Marshaller;
062: import javax.xml.bind.Unmarshaller;
063: import javax.xml.bind.annotation.XmlRootElement;
064: import javax.xml.bind.util.JAXBResult;
065: import javax.xml.namespace.QName;
066: import javax.xml.stream.XMLStreamException;
067: import javax.xml.stream.XMLStreamReader;
068: import javax.xml.stream.XMLStreamWriter;
069: import static javax.xml.stream.XMLStreamConstants.START_DOCUMENT;
070: import javax.xml.transform.Source;
071: import javax.xml.ws.WebServiceException;
072: import java.io.OutputStream;
073: import java.util.Map;
074:
075: /**
076: * {@link Message} backed by a JAXB bean.
077: *
078: * @author Kohsuke Kawaguchi
079: */
080: public final class JAXBMessage extends AbstractMessageImpl {
081: private HeaderList headers;
082:
083: /**
084: * The JAXB object that represents the header.
085: */
086: private final Object jaxbObject;
087:
088: private final AttachmentSetImpl attachmentSet;
089:
090: private final Bridge bridge;
091:
092: /**
093: * Lazily sniffed payload element name
094: */
095: private String nsUri, localName;
096:
097: /**
098: * If we have the infoset representation for the payload, this field is non-null.
099: */
100: private XMLStreamBuffer infoset;
101:
102: /**
103: * Creates a {@link Message} backed by a JAXB bean.
104: *
105: * @param context
106: * The JAXBContext to be used for marshalling.
107: * @param jaxbObject
108: * The JAXB object that represents the payload. must not be null. This object
109: * must be bound to an element (which means it either is a {@link JAXBElement} or
110: * an instanceof a class with {@link XmlRootElement}).
111: * @param soapVersion
112: * The SOAP version of the message. Must not be null.
113: */
114: public static Message create(JAXBRIContext context,
115: Object jaxbObject, SOAPVersion soapVersion) {
116: if (!context.hasSwaRef()) {
117: return new JAXBMessage(context, jaxbObject, soapVersion);
118: }
119:
120: // If we have swaRef, then that means we might have attachments.
121: // to comply with the packet API, we need to eagerly turn the JAXB object into infoset
122: // to correctly find out about attachments.
123:
124: try {
125: MutableXMLStreamBuffer xsb = new MutableXMLStreamBuffer();
126:
127: Marshaller m = context.createMarshaller();
128: AttachmentSetImpl attachments = new AttachmentSetImpl();
129: AttachmentMarshallerImpl am = new AttachmentMarshallerImpl(
130: attachments);
131: m.setAttachmentMarshaller(am);
132: am.cleanup();
133: m.marshal(jaxbObject, xsb.createFromXMLStreamWriter());
134:
135: // any way to reuse this XMLStreamBuffer in StreamMessage?
136: return new StreamMessage(null, attachments, xsb
137: .readAsXMLStreamReader(), soapVersion);
138: } catch (JAXBException e) {
139: throw new WebServiceException(e);
140: } catch (XMLStreamException e) {
141: throw new WebServiceException(e);
142: }
143: }
144:
145: private JAXBMessage(JAXBRIContext context, Object jaxbObject,
146: SOAPVersion soapVer) {
147: super (soapVer);
148: this .bridge = new MarshallerBridge(context);
149: this .jaxbObject = jaxbObject;
150: this .attachmentSet = new AttachmentSetImpl();
151: }
152:
153: /**
154: * Creates a {@link Message} backed by a JAXB bean.
155: *
156: * @param bridge
157: * Specify the payload tag name and how <tt>jaxbObject</tt> is bound.
158: * @param jaxbObject
159: */
160: public static Message create(Bridge bridge, Object jaxbObject,
161: SOAPVersion soapVer) {
162: if (!bridge.getContext().hasSwaRef()) {
163: return new JAXBMessage(bridge, jaxbObject, soapVer);
164: }
165:
166: // If we have swaRef, then that means we might have attachments.
167: // to comply with the packet API, we need to eagerly turn the JAXB object into infoset
168: // to correctly find out about attachments.
169:
170: try {
171: MutableXMLStreamBuffer xsb = new MutableXMLStreamBuffer();
172:
173: AttachmentSetImpl attachments = new AttachmentSetImpl();
174: AttachmentMarshallerImpl am = new AttachmentMarshallerImpl(
175: attachments);
176: bridge.marshal(jaxbObject, xsb.createFromXMLStreamWriter(),
177: am);
178: am.cleanup();
179:
180: // any way to reuse this XMLStreamBuffer in StreamMessage?
181: return new StreamMessage(null, attachments, xsb
182: .readAsXMLStreamReader(), soapVer);
183: } catch (JAXBException e) {
184: throw new WebServiceException(e);
185: } catch (XMLStreamException e) {
186: throw new WebServiceException(e);
187: }
188: }
189:
190: private JAXBMessage(Bridge bridge, Object jaxbObject,
191: SOAPVersion soapVer) {
192: super (soapVer);
193: // TODO: think about a better way to handle BridgeContext
194: this .bridge = bridge;
195: this .jaxbObject = jaxbObject;
196: QName tagName = bridge.getTypeReference().tagName;
197: this .nsUri = tagName.getNamespaceURI();
198: this .localName = tagName.getLocalPart();
199: this .attachmentSet = new AttachmentSetImpl();
200: }
201:
202: /**
203: * Copy constructor.
204: */
205: public JAXBMessage(JAXBMessage that) {
206: super (that);
207: this .headers = that.headers;
208: if (this .headers != null)
209: this .headers = new HeaderList(this .headers);
210: this .attachmentSet = that.attachmentSet;
211:
212: this .jaxbObject = that.jaxbObject;
213: this .bridge = that.bridge;
214: }
215:
216: @Override
217: public @NotNull
218: AttachmentSet getAttachments() {
219: return attachmentSet;
220: }
221:
222: public boolean hasHeaders() {
223: return headers != null && !headers.isEmpty();
224: }
225:
226: public HeaderList getHeaders() {
227: if (headers == null)
228: headers = new HeaderList();
229: return headers;
230: }
231:
232: public String getPayloadLocalPart() {
233: if (localName == null)
234: sniff();
235: return localName;
236: }
237:
238: public String getPayloadNamespaceURI() {
239: if (nsUri == null)
240: sniff();
241: return nsUri;
242: }
243:
244: public boolean hasPayload() {
245: return true;
246: }
247:
248: /**
249: * Obtains the tag name of the root element.
250: */
251: private void sniff() {
252: RootElementSniffer sniffer = new RootElementSniffer(false);
253: try {
254: bridge.marshal(jaxbObject, sniffer);
255: } catch (JAXBException e) {
256: // if it's due to us aborting the processing after the first element,
257: // we can safely ignore this exception.
258: //
259: // if it's due to error in the object, the same error will be reported
260: // when the readHeader() method is used, so we don't have to report
261: // an error right now.
262: nsUri = sniffer.getNsUri();
263: localName = sniffer.getLocalName();
264: }
265: }
266:
267: public Source readPayloadAsSource() {
268: return new JAXBBridgeSource(bridge, jaxbObject);
269: }
270:
271: public <T> T readPayloadAsJAXB(Unmarshaller unmarshaller)
272: throws JAXBException {
273: JAXBResult out = new JAXBResult(unmarshaller);
274: // since the bridge only produces fragments, we need to fire start/end document.
275: try {
276: out.getHandler().startDocument();
277: bridge.marshal(jaxbObject, out);
278: out.getHandler().endDocument();
279: } catch (SAXException e) {
280: throw new JAXBException(e);
281: }
282: return (T) out.getResult();
283: }
284:
285: public XMLStreamReader readPayload() throws XMLStreamException {
286: try {
287: if (infoset == null) {
288: XMLStreamBufferResult sbr = new XMLStreamBufferResult();
289: bridge.marshal(jaxbObject, sbr);
290: infoset = sbr.getXMLStreamBuffer();
291: }
292: XMLStreamReader reader = infoset.readAsXMLStreamReader();
293: if (reader.getEventType() == START_DOCUMENT)
294: XMLStreamReaderUtil.nextElementContent(reader);
295: return reader;
296: } catch (JAXBException e) {
297: // bug 6449684, spec 4.3.4
298: throw new WebServiceException(e);
299: }
300: }
301:
302: /**
303: * Writes the payload as SAX events.
304: */
305: protected void writePayloadTo(ContentHandler contentHandler,
306: ErrorHandler errorHandler, boolean fragment)
307: throws SAXException {
308: try {
309: if (fragment)
310: contentHandler = new FragmentContentHandler(
311: contentHandler);
312: AttachmentMarshallerImpl am = new AttachmentMarshallerImpl(
313: attachmentSet);
314: bridge.marshal(jaxbObject, contentHandler, am);
315: am.cleanup();
316: } catch (JAXBException e) {
317: // this is really more helpful but spec compliance
318: // errorHandler.fatalError(new SAXParseException(e.getMessage(),NULL_LOCATOR,e));
319: // bug 6449684, spec 4.3.4
320: throw new WebServiceException(e.getMessage(), e);
321: }
322: }
323:
324: public void writePayloadTo(XMLStreamWriter sw)
325: throws XMLStreamException {
326: try {
327: AttachmentMarshallerImpl am = new AttachmentMarshallerImpl(
328: attachmentSet);
329: // Get output stream and use JAXB UTF-8 writer
330: OutputStream os = XMLStreamWriterUtil.getOutputStream(sw);
331: if (os != null) {
332: bridge.marshal(jaxbObject, os,
333: sw.getNamespaceContext(), am);
334: } else {
335: bridge.marshal(jaxbObject, sw, am);
336: }
337: am.cleanup();
338: } catch (JAXBException e) {
339: // bug 6449684, spec 4.3.4
340: throw new WebServiceException(e);
341: }
342: }
343:
344: public Message copy() {
345: return new JAXBMessage(this);
346: }
347: }
|