325N/A/*
325N/A * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
325N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
325N/A *
325N/A * This code is free software; you can redistribute it and/or modify it
325N/A * under the terms of the GNU General Public License version 2 only, as
325N/A * published by the Free Software Foundation. Oracle designates this
325N/A * particular file as subject to the "Classpath" exception as provided
325N/A * by Oracle in the LICENSE file that accompanied this code.
325N/A *
325N/A * This code is distributed in the hope that it will be useful, but WITHOUT
325N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
325N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
325N/A * version 2 for more details (a copy is included in the LICENSE file that
325N/A * accompanied this code).
325N/A *
325N/A * You should have received a copy of the GNU General Public License version
325N/A * 2 along with this work; if not, write to the Free Software Foundation,
325N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
325N/A *
325N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
325N/A * or visit www.oracle.com if you need additional information or have any
325N/A * questions.
325N/A */
325N/A
325N/Apackage com.sun.xml.internal.ws.message.jaxb;
325N/A
325N/Aimport com.sun.istack.internal.FragmentContentHandler;
325N/Aimport com.sun.istack.internal.NotNull;
325N/Aimport com.sun.xml.internal.bind.api.Bridge;
325N/Aimport com.sun.xml.internal.bind.api.JAXBRIContext;
325N/Aimport com.sun.xml.internal.stream.buffer.MutableXMLStreamBuffer;
325N/Aimport com.sun.xml.internal.stream.buffer.XMLStreamBuffer;
325N/Aimport com.sun.xml.internal.stream.buffer.XMLStreamBufferResult;
325N/Aimport com.sun.xml.internal.ws.api.SOAPVersion;
325N/Aimport com.sun.xml.internal.ws.api.message.AttachmentSet;
325N/Aimport com.sun.xml.internal.ws.api.message.HeaderList;
325N/Aimport com.sun.xml.internal.ws.api.message.Message;
325N/Aimport com.sun.xml.internal.ws.message.AbstractMessageImpl;
325N/Aimport com.sun.xml.internal.ws.message.AttachmentSetImpl;
325N/Aimport com.sun.xml.internal.ws.message.RootElementSniffer;
325N/Aimport com.sun.xml.internal.ws.message.stream.StreamMessage;
325N/Aimport com.sun.xml.internal.ws.streaming.XMLStreamWriterUtil;
325N/Aimport com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil;
325N/Aimport com.sun.xml.internal.ws.streaming.MtomStreamWriter;
325N/Aimport org.xml.sax.ContentHandler;
325N/Aimport org.xml.sax.ErrorHandler;
325N/Aimport org.xml.sax.SAXException;
325N/A
325N/Aimport javax.xml.bind.JAXBElement;
325N/Aimport javax.xml.bind.JAXBException;
325N/Aimport javax.xml.bind.Marshaller;
325N/Aimport javax.xml.bind.Unmarshaller;
325N/Aimport javax.xml.bind.attachment.AttachmentMarshaller;
325N/Aimport javax.xml.bind.annotation.XmlRootElement;
325N/Aimport javax.xml.bind.util.JAXBResult;
325N/Aimport javax.xml.namespace.QName;
325N/Aimport javax.xml.stream.XMLStreamException;
325N/Aimport javax.xml.stream.XMLStreamReader;
325N/Aimport javax.xml.stream.XMLStreamWriter;
325N/Aimport static javax.xml.stream.XMLStreamConstants.START_DOCUMENT;
325N/Aimport javax.xml.transform.Source;
325N/Aimport javax.xml.ws.WebServiceException;
325N/Aimport java.io.OutputStream;
325N/Aimport java.util.Map;
325N/A
325N/A/**
325N/A * {@link Message} backed by a JAXB bean.
325N/A *
325N/A * @author Kohsuke Kawaguchi
325N/A */
325N/Apublic final class JAXBMessage extends AbstractMessageImpl {
325N/A private HeaderList headers;
325N/A
325N/A /**
325N/A * The JAXB object that represents the payload.
325N/A */
325N/A private final Object jaxbObject;
325N/A
325N/A private final Bridge bridge;
325N/A
325N/A /**
325N/A * Lazily sniffed payload element name
325N/A */
325N/A private String nsUri,localName;
325N/A
325N/A /**
325N/A * If we have the infoset representation for the payload, this field is non-null.
325N/A */
325N/A private XMLStreamBuffer infoset;
325N/A
325N/A public static Message create(JAXBRIContext context, Object jaxbObject, SOAPVersion soapVersion, HeaderList headers, AttachmentSet attachments) {
325N/A if(!context.hasSwaRef()) {
325N/A return new JAXBMessage(context,jaxbObject,soapVersion,headers,attachments);
325N/A }
325N/A
325N/A // If we have swaRef, then that means we might have attachments.
325N/A // to comply with the packet API, we need to eagerly turn the JAXB object into infoset
325N/A // to correctly find out about attachments.
325N/A
325N/A try {
325N/A MutableXMLStreamBuffer xsb = new MutableXMLStreamBuffer();
325N/A
325N/A Marshaller m = context.createMarshaller();
325N/A AttachmentMarshallerImpl am = new AttachmentMarshallerImpl(attachments);
325N/A m.setAttachmentMarshaller(am);
325N/A am.cleanup();
325N/A m.marshal(jaxbObject,xsb.createFromXMLStreamWriter());
325N/A
325N/A // any way to reuse this XMLStreamBuffer in StreamMessage?
325N/A return new StreamMessage(headers,attachments,xsb.readAsXMLStreamReader(),soapVersion);
325N/A } catch (JAXBException e) {
325N/A throw new WebServiceException(e);
325N/A } catch (XMLStreamException e) {
325N/A throw new WebServiceException(e);
325N/A }
325N/A }
325N/A /**
325N/A * Creates a {@link Message} backed by a JAXB bean.
325N/A *
325N/A * @param context
325N/A * The JAXBContext to be used for marshalling.
325N/A * @param jaxbObject
325N/A * The JAXB object that represents the payload. must not be null. This object
325N/A * must be bound to an element (which means it either is a {@link JAXBElement} or
325N/A * an instanceof a class with {@link XmlRootElement}).
325N/A * @param soapVersion
325N/A * The SOAP version of the message. Must not be null.
325N/A */
325N/A public static Message create(JAXBRIContext context, Object jaxbObject, SOAPVersion soapVersion) {
325N/A return create(context,jaxbObject,soapVersion,null,null);
325N/A }
325N/A
325N/A private JAXBMessage( JAXBRIContext context, Object jaxbObject, SOAPVersion soapVer, HeaderList headers, AttachmentSet attachments ) {
325N/A super(soapVer);
325N/A this.bridge = new MarshallerBridge(context);
325N/A this.jaxbObject = jaxbObject;
325N/A this.headers = headers;
325N/A this.attachmentSet = attachments;
325N/A }
325N/A
325N/A /**
325N/A * Creates a {@link Message} backed by a JAXB bean.
325N/A *
325N/A * @param bridge
325N/A * Specify the payload tag name and how <tt>jaxbObject</tt> is bound.
325N/A * @param jaxbObject
325N/A */
325N/A public static Message create(Bridge bridge, Object jaxbObject, SOAPVersion soapVer) {
325N/A if(!bridge.getContext().hasSwaRef()) {
325N/A return new JAXBMessage(bridge,jaxbObject,soapVer);
325N/A }
325N/A
325N/A // If we have swaRef, then that means we might have attachments.
325N/A // to comply with the packet API, we need to eagerly turn the JAXB object into infoset
325N/A // to correctly find out about attachments.
325N/A
325N/A try {
325N/A MutableXMLStreamBuffer xsb = new MutableXMLStreamBuffer();
325N/A
325N/A AttachmentSetImpl attachments = new AttachmentSetImpl();
325N/A AttachmentMarshallerImpl am = new AttachmentMarshallerImpl(attachments);
325N/A bridge.marshal(jaxbObject,xsb.createFromXMLStreamWriter(), am);
325N/A am.cleanup();
325N/A
325N/A // any way to reuse this XMLStreamBuffer in StreamMessage?
325N/A return new StreamMessage(null,attachments,xsb.readAsXMLStreamReader(),soapVer);
325N/A } catch (JAXBException e) {
325N/A throw new WebServiceException(e);
325N/A } catch (XMLStreamException e) {
325N/A throw new WebServiceException(e);
325N/A }
325N/A }
325N/A
325N/A private JAXBMessage(Bridge bridge, Object jaxbObject, SOAPVersion soapVer) {
325N/A super(soapVer);
325N/A // TODO: think about a better way to handle BridgeContext
325N/A this.bridge = bridge;
325N/A this.jaxbObject = jaxbObject;
325N/A QName tagName = bridge.getTypeReference().tagName;
325N/A this.nsUri = tagName.getNamespaceURI();
325N/A this.localName = tagName.getLocalPart();
325N/A this.attachmentSet = new AttachmentSetImpl();
325N/A }
325N/A
325N/A /**
325N/A * Copy constructor.
325N/A */
325N/A public JAXBMessage(JAXBMessage that) {
325N/A super(that);
325N/A this.headers = that.headers;
325N/A if(this.headers!=null)
325N/A this.headers = new HeaderList(this.headers);
325N/A this.attachmentSet = that.attachmentSet;
325N/A
325N/A this.jaxbObject = that.jaxbObject;
325N/A this.bridge = that.bridge;
325N/A }
325N/A
325N/A public boolean hasHeaders() {
325N/A return headers!=null && !headers.isEmpty();
325N/A }
325N/A
325N/A public HeaderList getHeaders() {
325N/A if(headers==null)
325N/A headers = new HeaderList();
325N/A return headers;
325N/A }
325N/A
325N/A public String getPayloadLocalPart() {
325N/A if(localName==null)
325N/A sniff();
325N/A return localName;
325N/A }
325N/A
325N/A public String getPayloadNamespaceURI() {
325N/A if(nsUri==null)
325N/A sniff();
325N/A return nsUri;
325N/A }
325N/A
325N/A public boolean hasPayload() {
325N/A return true;
325N/A }
325N/A
325N/A /**
325N/A * Obtains the tag name of the root element.
325N/A */
325N/A private void sniff() {
325N/A RootElementSniffer sniffer = new RootElementSniffer(false);
325N/A try {
325N/A bridge.marshal(jaxbObject,sniffer);
325N/A } catch (JAXBException e) {
325N/A // if it's due to us aborting the processing after the first element,
325N/A // we can safely ignore this exception.
325N/A //
325N/A // if it's due to error in the object, the same error will be reported
325N/A // when the readHeader() method is used, so we don't have to report
325N/A // an error right now.
325N/A nsUri = sniffer.getNsUri();
325N/A localName = sniffer.getLocalName();
325N/A }
325N/A }
325N/A
325N/A public Source readPayloadAsSource() {
325N/A return new JAXBBridgeSource(bridge,jaxbObject);
325N/A }
325N/A
325N/A public <T> T readPayloadAsJAXB(Unmarshaller unmarshaller) throws JAXBException {
325N/A JAXBResult out = new JAXBResult(unmarshaller);
325N/A // since the bridge only produces fragments, we need to fire start/end document.
325N/A try {
325N/A out.getHandler().startDocument();
325N/A bridge.marshal(jaxbObject,out);
325N/A out.getHandler().endDocument();
325N/A } catch (SAXException e) {
325N/A throw new JAXBException(e);
325N/A }
325N/A return (T)out.getResult();
325N/A }
325N/A
325N/A public XMLStreamReader readPayload() throws XMLStreamException {
325N/A try {
325N/A if(infoset==null) {
325N/A XMLStreamBufferResult sbr = new XMLStreamBufferResult();
325N/A bridge.marshal(jaxbObject,sbr);
325N/A infoset = sbr.getXMLStreamBuffer();
325N/A }
325N/A XMLStreamReader reader = infoset.readAsXMLStreamReader();
325N/A if(reader.getEventType()== START_DOCUMENT)
325N/A XMLStreamReaderUtil.nextElementContent(reader);
325N/A return reader;
325N/A } catch (JAXBException e) {
325N/A // bug 6449684, spec 4.3.4
325N/A throw new WebServiceException(e);
325N/A }
325N/A }
325N/A
325N/A /**
325N/A * Writes the payload as SAX events.
325N/A */
325N/A protected void writePayloadTo(ContentHandler contentHandler, ErrorHandler errorHandler, boolean fragment) throws SAXException {
325N/A try {
325N/A if(fragment)
325N/A contentHandler = new FragmentContentHandler(contentHandler);
325N/A AttachmentMarshallerImpl am = new AttachmentMarshallerImpl(attachmentSet);
325N/A bridge.marshal(jaxbObject,contentHandler, am);
325N/A am.cleanup();
325N/A } catch (JAXBException e) {
325N/A // this is really more helpful but spec compliance
325N/A // errorHandler.fatalError(new SAXParseException(e.getMessage(),NULL_LOCATOR,e));
325N/A // bug 6449684, spec 4.3.4
325N/A throw new WebServiceException(e.getMessage(),e);
325N/A }
325N/A }
325N/A
325N/A public void writePayloadTo(XMLStreamWriter sw) throws XMLStreamException {
325N/A try {
325N/A // MtomCodec sets its own AttachmentMarshaller
325N/A AttachmentMarshaller am = (sw instanceof MtomStreamWriter)
325N/A ? ((MtomStreamWriter)sw).getAttachmentMarshaller()
325N/A : new AttachmentMarshallerImpl(attachmentSet);
325N/A
325N/A // Get output stream and use JAXB UTF-8 writer
325N/A OutputStream os = XMLStreamWriterUtil.getOutputStream(sw);
325N/A if (os != null) {
325N/A bridge.marshal(jaxbObject, os, sw.getNamespaceContext(),am);
325N/A } else {
325N/A bridge.marshal(jaxbObject,sw,am);
325N/A }
325N/A //cleanup() is not needed since JAXB doesn't keep ref to AttachmentMarshaller
325N/A //am.cleanup();
325N/A } catch (JAXBException e) {
325N/A // bug 6449684, spec 4.3.4
325N/A throw new WebServiceException(e);
325N/A }
325N/A }
325N/A
325N/A public Message copy() {
325N/A return new JAXBMessage(this);
325N/A }
325N/A}