1 /*
   2  * Copyright (c) 1997, 2013, 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.ws.message.jaxb;
  27 
  28 import com.sun.xml.internal.ws.api.SOAPVersion;
  29 import com.sun.xml.internal.ws.api.message.Message;
  30 import com.sun.xml.internal.ws.api.message.MessageHeaders;
  31 import com.sun.xml.internal.ws.encoding.SOAPBindingCodec;
  32 import com.sun.xml.internal.ws.message.AbstractMessageImpl;
  33 import com.sun.xml.internal.ws.message.PayloadElementSniffer;
  34 import com.sun.xml.internal.ws.spi.db.BindingContext;
  35 import com.sun.xml.internal.ws.spi.db.XMLBridge;
  36 import com.sun.xml.internal.ws.streaming.MtomStreamWriter;
  37 import com.sun.xml.internal.ws.streaming.XMLStreamWriterUtil;
  38 import org.xml.sax.ContentHandler;
  39 import org.xml.sax.ErrorHandler;
  40 import org.xml.sax.SAXException;
  41 
  42 import javax.xml.bind.JAXBContext;
  43 import javax.xml.bind.JAXBException;
  44 import javax.xml.bind.Marshaller;
  45 import javax.xml.bind.attachment.AttachmentMarshaller;
  46 import javax.xml.namespace.QName;
  47 import javax.xml.stream.XMLStreamException;
  48 import javax.xml.stream.XMLStreamReader;
  49 import javax.xml.stream.XMLStreamWriter;
  50 import javax.xml.transform.Source;
  51 import javax.xml.ws.WebServiceException;
  52 import java.io.OutputStream;
  53 
  54 /**
  55  * {@link Message} backed by a JAXB bean; this implementation is used when client uses
  56  * Dispatch mechanism in JAXB/MESSAGE mode; difference from {@link JAXBMessage} is
  57  * that {@code jaxbObject} holds whole SOAP message including SOAP envelope;
  58  * it's the client who is responsible for preparing message content.
  59  *
  60  * @author Miroslav Kos (miroslav.kos at oracle.com)
  61  */
  62 public class JAXBDispatchMessage extends AbstractMessageImpl {
  63 
  64     private final Object jaxbObject;
  65 
  66     private final XMLBridge bridge;
  67 
  68     /**
  69      * For the use case of a user-supplied JAXB context that is not
  70      * a known JAXB type, as when creating a Disaptch object with a
  71      * JAXB object parameter, we will marshal and unmarshal directly with
  72      * the context object, as there is no Bond available.  In this case,
  73      * swaRef is not supported.
  74      */
  75     private final JAXBContext rawContext;
  76 
  77     /**
  78      * Lazily sniffed payload element name
  79      */
  80     private QName payloadQName;
  81 
  82     /**
  83      * Copy constructor.
  84      */
  85     private JAXBDispatchMessage(JAXBDispatchMessage that) {
  86         super(that);
  87         jaxbObject = that.jaxbObject;
  88         rawContext = that.rawContext;
  89         bridge = that.bridge;
  90     }
  91 
  92     public JAXBDispatchMessage(JAXBContext rawContext, Object jaxbObject, SOAPVersion soapVersion) {
  93         super(soapVersion);
  94         this.bridge = null;
  95         this.rawContext = rawContext;
  96         this.jaxbObject = jaxbObject;
  97     }
  98 
  99     public JAXBDispatchMessage(BindingContext context, Object jaxbObject, SOAPVersion soapVersion) {
 100         super(soapVersion);
 101         this.bridge = context.createFragmentBridge();
 102         this.rawContext = null;
 103         this.jaxbObject = jaxbObject;
 104     }
 105 
 106     @Override
 107     protected void writePayloadTo(ContentHandler contentHandler, ErrorHandler errorHandler, boolean fragment) throws SAXException {
 108         throw new UnsupportedOperationException();
 109     }
 110 
 111     @Override
 112     public boolean hasHeaders() {
 113         return false;
 114     }
 115 
 116     @Override
 117     public MessageHeaders getHeaders() {
 118         return null;
 119     }
 120 
 121     @Override
 122     public String getPayloadLocalPart() {
 123         if (payloadQName == null) {
 124             readPayloadElement();
 125         }
 126         return payloadQName.getLocalPart();
 127     }
 128 
 129     @Override
 130     public String getPayloadNamespaceURI() {
 131         if (payloadQName == null) {
 132             readPayloadElement();
 133         }
 134         return payloadQName.getNamespaceURI();
 135     }
 136 
 137     private void readPayloadElement() {
 138         PayloadElementSniffer sniffer = new PayloadElementSniffer();
 139         try {
 140             if (rawContext != null) {
 141                 Marshaller m = rawContext.createMarshaller();
 142                 m.setProperty("jaxb.fragment", Boolean.FALSE);
 143                 m.marshal(jaxbObject, sniffer);
 144             } else {
 145                 bridge.marshal(jaxbObject, sniffer, null);
 146             }
 147 
 148         } catch (JAXBException e) {
 149             // if it's due to us aborting the processing after the first element,
 150             // we can safely ignore this exception.
 151             //
 152             // if it's due to error in the object, the same error will be reported
 153             // when the readHeader() method is used, so we don't have to report
 154             // an error right now.
 155             payloadQName = sniffer.getPayloadQName();
 156         }
 157     }
 158 
 159     @Override
 160     public boolean hasPayload() {
 161         return true;
 162     }
 163 
 164     @Override
 165     public Source readPayloadAsSource() {
 166         throw new UnsupportedOperationException();
 167     }
 168 
 169     @Override
 170     public XMLStreamReader readPayload() throws XMLStreamException {
 171         throw new UnsupportedOperationException();
 172     }
 173 
 174     @Override
 175     public void writePayloadTo(XMLStreamWriter sw) throws XMLStreamException {
 176         throw new UnsupportedOperationException();
 177     }
 178 
 179     @Override
 180     public Message copy() {
 181         return new JAXBDispatchMessage(this);
 182     }
 183 
 184     @Override
 185     @SuppressWarnings("unchecked")
 186     public void writeTo(XMLStreamWriter sw) throws XMLStreamException {
 187         try {
 188             // MtomCodec sets its own AttachmentMarshaller
 189             AttachmentMarshaller am = (sw instanceof MtomStreamWriter)
 190                     ? ((MtomStreamWriter) sw).getAttachmentMarshaller()
 191                     : new AttachmentMarshallerImpl(attachmentSet);
 192 
 193             // Get the encoding of the writer
 194             String encoding = XMLStreamWriterUtil.getEncoding(sw);
 195 
 196             // Get output stream and use JAXB UTF-8 writer
 197             OutputStream os = bridge.supportOutputStream() ? XMLStreamWriterUtil.getOutputStream(sw) : null;
 198             if (rawContext != null) {
 199                 Marshaller m = rawContext.createMarshaller();
 200                 m.setProperty("jaxb.fragment", Boolean.FALSE);
 201                 m.setAttachmentMarshaller(am);
 202                 if (os != null) {
 203                     m.marshal(jaxbObject, os);
 204                 } else {
 205                     m.marshal(jaxbObject, sw);
 206                 }
 207 
 208             } else {
 209 
 210                 if (os != null && encoding != null && encoding.equalsIgnoreCase(SOAPBindingCodec.UTF8_ENCODING)) {
 211                     bridge.marshal(jaxbObject, os, sw.getNamespaceContext(), am);
 212                 } else {
 213                     bridge.marshal(jaxbObject, sw, am);
 214                 }
 215             }
 216             //cleanup() is not needed since JAXB doesn't keep ref to AttachmentMarshaller
 217         } catch (JAXBException e) {
 218             // bug 6449684, spec 4.3.4
 219             throw new WebServiceException(e);
 220         }
 221     }
 222 }