001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.axis2.jaxws.message.impl;
020:
021: import org.apache.axiom.attachments.Attachments;
022: import org.apache.axiom.om.OMElement;
023: import org.apache.axiom.om.OMNamespace;
024: import org.apache.axis2.Constants.Configuration;
025: import org.apache.axis2.jaxws.ExceptionFactory;
026: import org.apache.axis2.jaxws.core.MessageContext;
027: import org.apache.axis2.jaxws.i18n.Messages;
028: import org.apache.axis2.jaxws.message.Block;
029: import org.apache.axis2.jaxws.message.Message;
030: import org.apache.axis2.jaxws.message.Protocol;
031: import org.apache.axis2.jaxws.message.XMLFault;
032: import org.apache.axis2.jaxws.message.XMLPart;
033: import org.apache.axis2.jaxws.message.attachments.AttachmentUtils;
034: import org.apache.axis2.jaxws.message.factory.BlockFactory;
035: import org.apache.axis2.jaxws.message.factory.SAAJConverterFactory;
036: import org.apache.axis2.jaxws.message.factory.SOAPEnvelopeBlockFactory;
037: import org.apache.axis2.jaxws.message.factory.XMLPartFactory;
038: import org.apache.axis2.jaxws.message.factory.XMLStringBlockFactory;
039: import org.apache.axis2.jaxws.message.util.MessageUtils;
040: import org.apache.axis2.jaxws.message.util.SAAJConverter;
041: import org.apache.axis2.jaxws.registry.FactoryRegistry;
042: import org.apache.commons.logging.Log;
043: import org.apache.commons.logging.LogFactory;
044:
045: import javax.activation.DataHandler;
046: import javax.jws.soap.SOAPBinding.Style;
047: import javax.xml.namespace.QName;
048: import javax.xml.soap.AttachmentPart;
049: import javax.xml.soap.MessageFactory;
050: import javax.xml.soap.MimeHeaders;
051: import javax.xml.soap.SOAPConstants;
052: import javax.xml.soap.SOAPEnvelope;
053: import javax.xml.soap.SOAPMessage;
054: import javax.xml.stream.XMLStreamException;
055: import javax.xml.stream.XMLStreamReader;
056: import javax.xml.stream.XMLStreamWriter;
057: import javax.xml.ws.WebServiceException;
058:
059: import java.io.ByteArrayInputStream;
060: import java.io.ByteArrayOutputStream;
061: import java.util.HashMap;
062: import java.util.Iterator;
063: import java.util.List;
064: import java.util.Map;
065:
066: /**
067: * MessageImpl
068: * A Message is an XML part + Attachments.
069: * Most of the implementation delegates to the XMLPart implementation.
070: *
071: * NOTE: For XML/HTTP (REST), a SOAP 1.1. Envelope is built and the rest payload is placed
072: * in the body. This purposely mimics the Axis2 implementation.
073: */
074: public class MessageImpl implements Message {
075: private static final Log log = LogFactory.getLog(MessageImpl.class);
076:
077: Protocol protocol = Protocol.unknown; // the protocol, defaults to unknown
078: XMLPart xmlPart = null; // the representation of the xmlpart
079:
080: boolean mtomEnabled;
081:
082: // The transport headers are stored in a Map, which is the
083: // same data representation used by the Axis2 MessageContext (TRANSPORT_HEADERS).
084: private Map transportHeaders = null;
085:
086: // The Message is connected to a MessageContext.
087: // Prior to that connection, attachments are stored locally
088: // After the connection, attachments are obtained from the MessageContext
089: Attachments attachments = new Attachments(); // Local Attachments
090: private MessageContext messageContext;
091:
092: // Set after we have past the pivot point when the message is consumed
093: private boolean postPivot = false;
094: private boolean doingSWA = false;
095:
096: /**
097: * MessageImpl should be constructed via the MessageFactory.
098: * This constructor constructs an empty message with the specified protocol
099: * @param protocol
100: */
101: MessageImpl(Protocol protocol) throws WebServiceException,
102: XMLStreamException {
103: createXMLPart(protocol);
104: }
105:
106: /**
107: * Message is constructed by the MessageFactory.
108: * This constructor creates a message from the specified root.
109: * @param root
110: * @param protocol or null
111: */
112: MessageImpl(OMElement root, Protocol protocol)
113: throws WebServiceException, XMLStreamException {
114: createXMLPart(root, protocol);
115: }
116:
117: /**
118: * Message is constructed by the MessageFactory.
119: * This constructor creates a message from the specified root.
120: * @param root
121: * @param protocol or null
122: */
123: MessageImpl(SOAPEnvelope root) throws WebServiceException,
124: XMLStreamException {
125: createXMLPart(root);
126: }
127:
128: /**
129: * Create a new XMLPart and Protocol from the root
130: * @param root SOAPEnvelope
131: * @throws WebServiceException
132: * @throws XMLStreamException
133: */
134: private void createXMLPart(SOAPEnvelope root)
135: throws WebServiceException, XMLStreamException {
136: XMLPartFactory factory = (XMLPartFactory) FactoryRegistry
137: .getFactory(XMLPartFactory.class);
138: xmlPart = factory.createFrom(root);
139: this .protocol = xmlPart.getProtocol();
140: xmlPart.setParent(this );
141: }
142:
143: /**
144: * Create a new XMLPart and Protocol from the root
145: * @param root OMElement
146: * @throws WebServiceException
147: * @throws XMLStreamException
148: */
149: private void createXMLPart(OMElement root, Protocol protocol)
150: throws WebServiceException, XMLStreamException {
151: XMLPartFactory factory = (XMLPartFactory) FactoryRegistry
152: .getFactory(XMLPartFactory.class);
153: xmlPart = factory.createFrom(root, protocol);
154: this .protocol = xmlPart.getProtocol();
155: xmlPart.setParent(this );
156: }
157:
158: /**
159: * Create a new empty XMLPart from the Protocol
160: * @param protocol
161: * @throws WebServiceException
162: * @throws XMLStreamException
163: */
164: private void createXMLPart(Protocol protocol)
165: throws WebServiceException, XMLStreamException {
166: this .protocol = protocol;
167: if (protocol.equals(Protocol.unknown)) {
168: throw ExceptionFactory.makeWebServiceException(Messages
169: .getMessage("ProtocolIsNotKnown"));
170: }
171: XMLPartFactory factory = (XMLPartFactory) FactoryRegistry
172: .getFactory(XMLPartFactory.class);
173: xmlPart = factory.create(protocol);
174: xmlPart.setParent(this );
175: }
176:
177: /* (non-Javadoc)
178: * @see org.apache.axis2.jaxws.message.Message#getAsSOAPMessage()
179: */
180: public SOAPMessage getAsSOAPMessage() throws WebServiceException {
181:
182: // TODO:
183: // This is a non performant way to create SOAPMessage. I will serialize
184: // the xmlpart content and then create an InputStream of byte.
185: // Finally create SOAPMessage using this InputStream.
186: // The real solution may involve using non-spec, implementation
187: // constructors to create a Message from an Envelope
188: try {
189: // Get OMElement from XMLPart.
190: OMElement element = xmlPart.getAsOMElement();
191:
192: // Get the namespace so that we can determine SOAP11 or SOAP12
193: OMNamespace ns = element.getNamespace();
194:
195: ByteArrayOutputStream outStream = new ByteArrayOutputStream();
196: element.serialize(outStream);
197:
198: // Create InputStream
199: ByteArrayInputStream inStream = new ByteArrayInputStream(
200: outStream.toByteArray());
201:
202: // Create MessageFactory that supports the version of SOAP in the om element
203: MessageFactory mf = getSAAJConverter()
204: .createMessageFactory(ns.getNamespaceURI());
205:
206: // Create soapMessage object from Message Factory using the input
207: // stream created from OM.
208:
209: // Get the MimeHeaders from the transportHeaders map
210: MimeHeaders defaultHeaders = new MimeHeaders();
211: if (transportHeaders != null) {
212: Iterator it = transportHeaders.entrySet().iterator();
213: while (it.hasNext()) {
214: Map.Entry entry = (Map.Entry) it.next();
215: String key = (String) entry.getKey();
216: if (entry.getValue() instanceof String) {
217: // Normally there is one value per key
218: defaultHeaders.addHeader(key, (String) entry
219: .getValue());
220: } else {
221: // There may be multiple values for each key. This code
222: // assumes the value is an array of String.
223: String values[] = (String[]) entry.getValue();
224: for (int i = 0; i < values.length; i++) {
225: defaultHeaders.addHeader(key, values[i]);
226: }
227: }
228: }
229: }
230:
231: // Toggle based on SOAP 1.1 or SOAP 1.2
232: String contentType = null;
233: if (ns.getNamespaceURI().equals(
234: SOAPConstants.URI_NS_SOAP_1_1_ENVELOPE)) {
235: contentType = SOAPConstants.SOAP_1_1_CONTENT_TYPE;
236: } else {
237: contentType = SOAPConstants.SOAP_1_2_CONTENT_TYPE;
238: }
239:
240: // Override the content-type
241: defaultHeaders.setHeader("Content-type", contentType
242: + "; charset=UTF-8");
243: SOAPMessage soapMessage = mf.createMessage(defaultHeaders,
244: inStream);
245:
246: // At this point the XMLPart is still an OMElement.
247: // We need to change it to the new SOAPEnvelope.
248: createXMLPart(soapMessage.getSOAPPart().getEnvelope());
249:
250: // If axiom read the message from the input stream,
251: // then one of the attachments is a SOAPPart. Ignore this attachment
252: String soapPartContentID = getSOAPPartContentID(); // This may be null
253:
254: // Add the attachments
255: for (String cid : getAttachmentIDs()) {
256: DataHandler dh = attachments.getDataHandler(cid);
257: boolean isSOAPPart = cid.equals(soapPartContentID);
258: if (!isSOAPPart) {
259: AttachmentPart ap = MessageUtils
260: .createAttachmentPart(cid, dh, soapMessage);
261: soapMessage.addAttachmentPart(ap);
262: }
263: }
264:
265: return soapMessage;
266: } catch (Exception e) {
267: throw ExceptionFactory.makeWebServiceException(e);
268: }
269:
270: }
271:
272: /**
273: * Get the indicated (non-soap part) attachment id
274: * @param index
275: * @return CID or null if not present
276: */
277: public String getAttachmentID(int index) {
278: List<String> cids = getAttachmentIDs();
279: String spCID = getSOAPPartContentID();
280: if (log.isDebugEnabled()) {
281: log.debug("getAttachmentID for index =" + index);
282: for (int i = 0; i < cids.size(); i++) {
283: log.debug("Attachment CID (" + i + ") = "
284: + cids.indexOf(i));
285: }
286: log.debug("The SOAP Part CID is ignored. It's CID is ("
287: + spCID + ")");
288: }
289: int spIndex = (spCID == null) ? -1 : cids.indexOf(spCID);
290:
291: // Bump index so we don't consider the soap part
292: index = (spIndex != -1 && spIndex <= index) ? index + 1 : index;
293:
294: // Return the content id at the calculated index
295: String resultCID = null;
296: if (index < cids.size()) {
297: resultCID = cids.get(index);
298: }
299: if (log.isDebugEnabled()) {
300: log.debug("Returning CID=" + resultCID);
301: }
302: return resultCID;
303: }
304:
305: private String getSOAPPartContentID() {
306: String contentID = null;
307: if (messageContext == null) {
308: return null; // Attachments set up programmatically...so there is no SOAPPart
309: }
310: try {
311: contentID = attachments.getSOAPPartContentID();
312: } catch (RuntimeException e) {
313: // OM will kindly throw an OMException or NPE if the attachments is set up
314: // programmatically.
315: return null;
316: }
317: return contentID;
318: }
319:
320: /* (non-Javadoc)
321: * @see org.apache.axis2.jaxws.message.Message#getValue(java.lang.Object,
322: * org.apache.axis2.jaxws.message.factory.BlockFactory)
323: */
324: public Object getValue(Object context, BlockFactory blockFactory)
325: throws WebServiceException {
326: try {
327: Object value = null;
328: if (protocol == Protocol.rest) {
329: // The implementation of rest stores the rest xml inside a dummy soap 1.1 envelope.
330: // So use the get body block logic.
331: Block block = xmlPart.getBodyBlock(context,
332: blockFactory);
333: if (block != null) {
334: value = block.getBusinessObject(true);
335: }
336:
337: } else {
338: // Must be SOAP
339: if (blockFactory instanceof SOAPEnvelopeBlockFactory) {
340: value = getAsSOAPMessage();
341: } else {
342: // TODO: This doesn't seem right to me.
343: // We should not have an intermediate StringBlock.
344: // This is not performant. Scheu
345: OMElement messageOM = getAsOMElement();
346: String stringValue = messageOM.toString();
347: String soapNS = (protocol == Protocol.soap11) ? SOAPConstants.URI_NS_SOAP_1_1_ENVELOPE
348: : SOAPConstants.URI_NS_SOAP_1_2_ENVELOPE;
349: QName soapEnvQname = new QName(soapNS, "Envelope");
350:
351: XMLStringBlockFactory stringFactory = (XMLStringBlockFactory) FactoryRegistry
352: .getFactory(XMLStringBlockFactory.class);
353: Block stringBlock = stringFactory.createFrom(
354: stringValue, null, soapEnvQname);
355: Block block = blockFactory.createFrom(stringBlock,
356: context);
357: value = block.getBusinessObject(true);
358: }
359: }
360: return value;
361: } catch (Throwable e) {
362: throw ExceptionFactory.makeWebServiceException(e);
363: }
364: }
365:
366: /* (non-Javadoc)
367: * @see org.apache.axis2.jaxws.message.Message#getAttachmentIDs()
368: */
369: public List<String> getAttachmentIDs() {
370: return attachments.getContentIDList();
371: }
372:
373: /* (non-Javadoc)
374: * @see org.apache.axis2.jaxws.message.Message#getDataHandler(java.lang.String)
375: */
376: public DataHandler getDataHandler(String cid) {
377: String bcid = getBlobCID(cid);
378: return attachments.getDataHandler(bcid);
379: }
380:
381: /* (non-Javadoc)
382: * @see org.apache.axis2.jaxws.message.Message#removeDataHandler(java.lang.String)
383: */
384: public DataHandler removeDataHandler(String cid) {
385: String bcid = getBlobCID(cid);
386: DataHandler dh = attachments.getDataHandler(bcid);
387: attachments.removeDataHandler(bcid);
388: return dh;
389: }
390:
391: private String getBlobCID(String cid) {
392: String blobCID = cid;
393: if (cid.startsWith("cid:")) {
394: blobCID = cid.substring(4); // Skip over cid:
395: }
396: return blobCID;
397: }
398:
399: /* (non-Javadoc)
400: * @see org.apache.axis2.jaxws.message.XMLPart#getProtocol()
401: */
402: public Protocol getProtocol() {
403: return protocol;
404: }
405:
406: public OMElement getAsOMElement() throws WebServiceException {
407: return xmlPart.getAsOMElement();
408: }
409:
410: public javax.xml.soap.SOAPEnvelope getAsSOAPEnvelope()
411: throws WebServiceException {
412: return xmlPart.getAsSOAPEnvelope();
413: }
414:
415: public Block getBodyBlock(int index, Object context,
416: BlockFactory blockFactory) throws WebServiceException {
417: return xmlPart.getBodyBlock(index, context, blockFactory);
418: }
419:
420: public Block getHeaderBlock(String namespace, String localPart,
421: Object context, BlockFactory blockFactory)
422: throws WebServiceException {
423: return xmlPart.getHeaderBlock(namespace, localPart, context,
424: blockFactory);
425: }
426:
427: public int getNumBodyBlocks() throws WebServiceException {
428: return xmlPart.getNumBodyBlocks();
429: }
430:
431: public int getNumHeaderBlocks() throws WebServiceException {
432: return xmlPart.getNumHeaderBlocks();
433: }
434:
435: public XMLStreamReader getXMLStreamReader(boolean consume)
436: throws WebServiceException {
437: return xmlPart.getXMLStreamReader(consume);
438: }
439:
440: public boolean isConsumed() {
441: return xmlPart.isConsumed();
442: }
443:
444: public void outputTo(XMLStreamWriter writer, boolean consume)
445: throws XMLStreamException, WebServiceException {
446: xmlPart.outputTo(writer, consume);
447: }
448:
449: public void removeBodyBlock(int index) throws WebServiceException {
450: xmlPart.removeBodyBlock(index);
451: }
452:
453: public void removeHeaderBlock(String namespace, String localPart)
454: throws WebServiceException {
455: xmlPart.removeHeaderBlock(namespace, localPart);
456: }
457:
458: public void setBodyBlock(int index, Block block)
459: throws WebServiceException {
460: xmlPart.setBodyBlock(index, block);
461: }
462:
463: public void setHeaderBlock(String namespace, String localPart,
464: Block block) throws WebServiceException {
465: xmlPart.setHeaderBlock(namespace, localPart, block);
466: }
467:
468: public String traceString(String indent) {
469: return xmlPart.traceString(indent);
470: }
471:
472: /**
473: * Load the SAAJConverter
474: * @return SAAJConverter
475: */
476: SAAJConverter converter = null;
477:
478: private SAAJConverter getSAAJConverter() {
479: if (converter == null) {
480: SAAJConverterFactory factory = (SAAJConverterFactory) FactoryRegistry
481: .getFactory(SAAJConverterFactory.class);
482: converter = factory.getSAAJConverter();
483: }
484: return converter;
485: }
486:
487: public void addDataHandler(DataHandler dh, String id) {
488: if (id.startsWith("<") && id.endsWith(">")) {
489: id = id.substring(1, id.length() - 1);
490: }
491: attachments.addDataHandler(id, dh);
492: }
493:
494: public Message getParent() {
495: return null;
496: }
497:
498: public void setParent(Message msg) {
499: // A Message does not have a parent
500: throw new UnsupportedOperationException();
501: }
502:
503: /**
504: * @return true if the binding for this message indicates mtom
505: */
506: public boolean isMTOMEnabled() {
507: return mtomEnabled;
508: }
509:
510: /**
511: * @param true if the binding for this message indicates mtom
512: */
513: public void setMTOMEnabled(boolean b) {
514: mtomEnabled = b;
515: }
516:
517: public XMLFault getXMLFault() throws WebServiceException {
518: return xmlPart.getXMLFault();
519: }
520:
521: public void setXMLFault(XMLFault xmlFault)
522: throws WebServiceException {
523: xmlPart.setXMLFault(xmlFault);
524: }
525:
526: public boolean isFault() throws WebServiceException {
527: return xmlPart.isFault();
528: }
529:
530: public String getXMLPartContentType() {
531: return xmlPart.getXMLPartContentType();
532: }
533:
534: public Style getStyle() {
535: return xmlPart.getStyle();
536: }
537:
538: public void setStyle(Style style) throws WebServiceException {
539: xmlPart.setStyle(style);
540: }
541:
542: public QName getOperationElement() throws WebServiceException {
543: return xmlPart.getOperationElement();
544: }
545:
546: public void setOperationElement(QName operationQName)
547: throws WebServiceException {
548: xmlPart.setOperationElement(operationQName);
549: }
550:
551: /* (non-Javadoc)
552: * @see org.apache.axis2.jaxws.message.Attachment#getMimeHeaders()
553: */
554: public Map getMimeHeaders() {
555: // Lazily create transport headers.
556: if (transportHeaders == null) {
557: transportHeaders = new HashMap();
558: }
559: return transportHeaders;
560: }
561:
562: /* (non-Javadoc)
563: * @see org.apache.axis2.jaxws.message.Attachment#setMimeHeaders(java.util.Map)
564: */
565: public void setMimeHeaders(Map map) {
566: transportHeaders = map;
567: if (transportHeaders == null) {
568: transportHeaders = new HashMap();
569: }
570: }
571:
572: public Block getBodyBlock(Object context, BlockFactory blockFactory)
573: throws WebServiceException {
574: return xmlPart.getBodyBlock(context, blockFactory);
575: }
576:
577: public void setBodyBlock(Block block) throws WebServiceException {
578: xmlPart.setBodyBlock(block);
579: }
580:
581: public void setPostPivot() {
582: this .postPivot = true;
583: }
584:
585: public boolean isPostPivot() {
586: return postPivot;
587: }
588:
589: public int getIndirection() {
590: return xmlPart.getIndirection();
591: }
592:
593: public void setIndirection(int indirection) {
594: xmlPart.setIndirection(indirection);
595: }
596:
597: public MessageContext getMessageContext() {
598: return messageContext;
599: }
600:
601: public void setMessageContext(MessageContext messageContext) {
602: if (this .messageContext != messageContext) {
603: // Copy attachments to the new map
604: Attachments newMap = messageContext.getAxisMessageContext()
605: .getAttachmentMap();
606: Attachments oldMap = attachments;
607: for (String cid : oldMap.getAllContentIDs()) {
608: DataHandler dh = oldMap.getDataHandler(cid);
609: if (dh != null) {
610: newMap.addDataHandler(cid, dh);
611: }
612: }
613: // If not MTOM and there are attachments, set SWA style
614: if (!isMTOMEnabled()) {
615: String[] cids = newMap.getAllContentIDs();
616: if (cids.length > 0) {
617: messageContext.setProperty(
618: Configuration.ENABLE_SWA, "true");
619: }
620: }
621: if (log.isDebugEnabled()) {
622: for (String cid : newMap.getAllContentIDs()) {
623: log
624: .debug("Message has an attachment with content id= "
625: + cid);
626: }
627: }
628: attachments = newMap;
629: }
630:
631: // Check for cached attachment file(s) if attachments exist.
632: if (attachments != null
633: && !messageContext.getAxisMessageContext()
634: .isServerSide()) {
635: AttachmentUtils.findCachedAttachment(attachments);
636: }
637:
638: this .messageContext = messageContext;
639: }
640:
641: public void setDoingSWA(boolean value) {
642: doingSWA = value;
643: }
644:
645: public boolean isDoingSWA() {
646: return doingSWA;
647: }
648: }
|