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.om.OMContainer;
022: import org.apache.axiom.om.OMElement;
023: import org.apache.axiom.om.OMNamespace;
024: import org.apache.axiom.om.OMNode;
025: import org.apache.axiom.om.impl.OMContainerEx;
026: import org.apache.axiom.om.impl.llom.OMElementImpl;
027: import org.apache.axiom.om.impl.llom.OMSourcedElementImpl;
028: import org.apache.axiom.soap.SOAP11Constants;
029: import org.apache.axiom.soap.SOAP12Constants;
030: import org.apache.axiom.soap.SOAPBody;
031: import org.apache.axiom.soap.SOAPEnvelope;
032: import org.apache.axiom.soap.SOAPFactory;
033: import org.apache.axiom.soap.SOAPFault;
034: import org.apache.axiom.soap.SOAPFaultDetail;
035: import org.apache.axiom.soap.SOAPHeader;
036: import org.apache.axiom.soap.impl.llom.soap11.SOAP11Factory;
037: import org.apache.axiom.soap.impl.llom.soap12.SOAP12Factory;
038: import org.apache.axis2.jaxws.ExceptionFactory;
039: import org.apache.axis2.jaxws.i18n.Messages;
040: import org.apache.axis2.jaxws.message.Block;
041: import org.apache.axis2.jaxws.message.Message;
042: import org.apache.axis2.jaxws.message.Protocol;
043: import org.apache.axis2.jaxws.message.XMLFault;
044: import org.apache.axis2.jaxws.message.factory.BlockFactory;
045: import org.apache.axis2.jaxws.message.factory.OMBlockFactory;
046: import org.apache.axis2.jaxws.message.util.MessageUtils;
047: import org.apache.axis2.jaxws.message.util.Reader2Writer;
048: import org.apache.axis2.jaxws.message.util.XMLFaultUtils;
049: import org.apache.axis2.jaxws.registry.FactoryRegistry;
050: import org.apache.axis2.jaxws.utility.JavaUtils;
051: import org.apache.commons.logging.Log;
052: import org.apache.commons.logging.LogFactory;
053:
054: import javax.jws.soap.SOAPBinding.Style;
055: import javax.xml.namespace.QName;
056: import javax.xml.stream.XMLStreamConstants;
057: import javax.xml.stream.XMLStreamException;
058: import javax.xml.stream.XMLStreamReader;
059: import javax.xml.stream.XMLStreamWriter;
060: import javax.xml.ws.WebServiceException;
061: import java.util.Iterator;
062:
063: /**
064: * XMLSpineImpl
065: * <p/>
066: * An XMLSpine consists is an OMEnvelope (either a default one or one create from an incoming
067: * message). As Blocks are added or requested, they are placed in the tree as OMSourcedElements.
068: * <p/>
069: * NOTE: For XML/HTTP (REST) messages, a SOAP 1.1 envelope is built and the xml payload is
070: * placed in the body. This purposely mimics the implementation used by Axis2.
071: */
072: class XMLSpineImpl implements XMLSpine {
073:
074: private static Log log = LogFactory.getLog(XMLSpine.class);
075: private static OMBlockFactory obf = (OMBlockFactory) FactoryRegistry
076: .getFactory(OMBlockFactory.class);
077:
078: private Protocol protocol = Protocol.unknown;
079: private Style style = Style.DOCUMENT;
080: private int indirection = 0;
081: private SOAPEnvelope root = null;
082: private SOAPFactory soapFactory = null;
083:
084: private boolean consumed = false;
085: private Message parent = null;
086:
087: /**
088: * Create a lightweight representation of this protocol (i.e. the Envelope, Header and Body)
089: *
090: * @param protocol Protocol
091: * @param style Style
092: * @param indirection (0 or 1) indicates location of body blocks
093: * @param initialPayload (OMElement or null...used to add rest payload)
094: */
095: public XMLSpineImpl(Protocol protocol, Style style,
096: int indirection, OMElement payload) {
097: super ();
098: this .protocol = protocol;
099: this .style = style;
100: this .indirection = indirection;
101: soapFactory = _getFactory(protocol);
102: root = _createEmptyEnvelope(style, soapFactory);
103: if (payload != null) {
104: ((SOAPEnvelope) root).getBody().addChild(payload);
105: }
106: }
107:
108: /**
109: * Create spine from an existing OM tree
110: *
111: * @param envelope
112: * @param style Style
113: * @param indirection (0 or 1) indicates location of body blocks
114: * @throws WebServiceException
115: */
116: public XMLSpineImpl(SOAPEnvelope envelope, Style style,
117: int indirection, Protocol protocol)
118: throws WebServiceException {
119: super ();
120: this .style = style;
121: this .indirection = indirection;
122: this .protocol = protocol;
123: init(envelope);
124: // If null, detect protocol from soap namespace
125: if (protocol == null) {
126: if (root.getNamespace().getNamespaceURI().equals(
127: SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI)) {
128: this .protocol = Protocol.soap11;
129: } else if (root.getNamespace().getNamespaceURI().equals(
130: SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI)) {
131: this .protocol = Protocol.soap12;
132: }
133: }
134: }
135:
136: /**
137: * @param envelope
138: * @throws WebServiceException
139: */
140: private void init(SOAPEnvelope envelope) throws WebServiceException {
141: root = envelope;
142: soapFactory = MessageUtils.getSOAPFactory(root);
143:
144: // Advance past the header
145: SOAPHeader header = root.getHeader();
146: if (header == null) {
147: header = soapFactory.createSOAPHeader(root);
148: }
149:
150: // Now advance the parser to the body element
151: SOAPBody body = root.getBody();
152: if (body == null) {
153: // Create the body if one does not exist
154: body = soapFactory.createSOAPBody(root);
155: }
156: }
157:
158: /* (non-Javadoc)
159: * @see org.apache.axis2.jaxws.message.XMLPart#getProtocol()
160: */
161: public Protocol getProtocol() {
162: return protocol;
163: }
164:
165: /*
166: * (non-Javadoc)
167: * @see org.apache.axis2.jaxws.message.XMLPart#getParent()
168: */
169: public Message getParent() {
170: return parent;
171: }
172:
173: /*
174: * Set the backpointer to this XMLPart's parent Message
175: */
176: public void setParent(Message p) {
177: parent = p;
178: }
179:
180: /* (non-Javadoc)
181: * @see org.apache.axis2.jaxws.message.XMLPart#outputTo(javax.xml.stream.XMLStreamWriter,
182: * boolean)
183: */
184: public void outputTo(XMLStreamWriter writer, boolean consume)
185: throws XMLStreamException, WebServiceException {
186: Reader2Writer r2w = new Reader2Writer(
187: getXMLStreamReader(consume));
188: r2w.outputTo(writer);
189: }
190:
191: public XMLStreamReader getXMLStreamReader(boolean consume)
192: throws WebServiceException {
193: if (consume) {
194: if (root.getBuilder() != null
195: && !root.getBuilder().isCompleted()) {
196: return root.getXMLStreamReaderWithoutCaching();
197: } else {
198: return root.getXMLStreamReader();
199: }
200: } else {
201: return root.getXMLStreamReader();
202: }
203: }
204:
205: /* (non-Javadoc)
206: * @see org.apache.axis2.jaxws.message.impl.XMLSpine#getXMLFault()
207: */
208: public XMLFault getXMLFault() throws WebServiceException {
209: if (!isFault()) {
210: return null;
211: }
212:
213: // Advance through all of the detail blocks
214: int numDetailBlocks = getNumDetailBlocks();
215:
216: Block[] blocks = null;
217: if (numDetailBlocks > 0) {
218: blocks = new Block[numDetailBlocks];
219: SOAPFaultDetail detail = root.getBody().getFault()
220: .getDetail();
221: for (int i = 0; i < numDetailBlocks; i++) {
222: OMElement om = this ._getChildOMElement(detail, i);
223: blocks[i] = this ._getBlockFromOMElement(om, null, obf,
224: false);
225:
226: }
227: }
228:
229: XMLFault xmlFault = XMLFaultUtils.createXMLFault(root.getBody()
230: .getFault(), blocks);
231: return xmlFault;
232: }
233:
234: private int getNumDetailBlocks() throws WebServiceException {
235: if (isFault()) {
236: SOAPFault fault = root.getBody().getFault();
237: return _getNumChildElements(fault.getDetail());
238: }
239: return 0;
240: }
241:
242: public void setXMLFault(XMLFault xmlFault)
243: throws WebServiceException {
244:
245: // Clear out the existing body and detail blocks
246: SOAPBody body = root.getBody();
247: getNumDetailBlocks(); // Forces parse of existing detail blocks
248: getNumBodyBlocks(); // Forces parse over body
249: OMNode child = body.getFirstOMChild();
250: while (child != null) {
251: child.detach();
252: child = body.getFirstOMChild();
253: }
254:
255: // Add a SOAPFault to the body.
256: SOAPFault soapFault = XMLFaultUtils.createSOAPFault(xmlFault,
257: body, false);
258: }
259:
260: public boolean isConsumed() {
261: return consumed;
262: }
263:
264: public OMElement getAsOMElement() throws WebServiceException {
265: return root;
266: }
267:
268: /* (non-Javadoc)
269: * @see org.apache.axis2.jaxws.message.XMLPart#getNumBodyBlocks()
270: */
271: public int getNumBodyBlocks() throws WebServiceException {
272: return _getNumChildElements(_getBodyBlockParent());
273: }
274:
275: /* (non-Javadoc)
276: * @see org.apache.axis2.jaxws.message.impl.XMLSpine#getBodyBlock(int, java.lang.Object,
277: * org.apache.axis2.jaxws.message.factory.BlockFactory)
278: */
279: public Block getBodyBlock(int index, Object context,
280: BlockFactory blockFactory) throws WebServiceException {
281:
282: if (log.isDebugEnabled()) {
283: log.debug("getBodyBlock: Get the " + index
284: + "block using the block factory, " + blockFactory);
285: }
286:
287: // Forces the parser to read all of the blocks
288: getNumBodyBlocks();
289:
290: // Get the indicated block
291: OMElement omElement = _getChildOMElement(_getBodyBlockParent(),
292: index);
293: if (omElement == null) {
294: // Null indicates that no block is available
295: if (log.isDebugEnabled()) {
296: log.debug("getBodyBlock: The block was not found ");
297: }
298: return null;
299: }
300: if (log.isDebugEnabled()) {
301: log.debug("getBodyBlock: Found omElement "
302: + omElement.getQName());
303: }
304: return this ._getBlockFromOMElement(omElement, context,
305: blockFactory, false);
306: }
307:
308: /* (non-Javadoc)
309: * @see org.apache.axis2.jaxws.message.impl.XMLSpine#getBodyBlock(int, java.lang.Object,
310: * org.apache.axis2.jaxws.message.factory.BlockFactory)
311: */
312: public Block getBodyBlock(Object context, BlockFactory blockFactory)
313: throws WebServiceException {
314:
315: if (log.isDebugEnabled()) {
316: log
317: .debug("getBodyBlock PERFORMANT: Get the block using the block factory, "
318: + blockFactory);
319: }
320:
321: // TODO Need to upgrade the code to get Blocks that represent text and elements.
322:
323: // Calling getBodyBlock assumes that there is only one or zero body blocks in the message.
324: // Subsequent Blocks are lost. If the caller needs access to multiple body blocks,
325: // then getBodyBlocks(index,...) should be used
326:
327: // Get the indicated block
328: OMElement omElement = _getChildOMElement(_getBodyBlockParent(),
329: 0);
330: if (omElement == null) {
331: // Null indicates that no block is available
332: if (log.isDebugEnabled()) {
333: log.debug("getBodyBlock: The block was not found ");
334: }
335: return null;
336: }
337: if (log.isDebugEnabled()) {
338: log.debug("getBodyBlock: Found omElement "
339: + omElement.getQName());
340: }
341: return this ._getBlockFromOMElement(omElement, context,
342: blockFactory, true);
343: }
344:
345: public void setBodyBlock(int index, Block block)
346: throws WebServiceException {
347:
348: // Forces the parser to read all of the blocks
349: getNumBodyBlocks();
350:
351: block.setParent(getParent());
352: OMElement bElement = _getBodyBlockParent();
353: OMElement om = this ._getChildOMElement(bElement, index);
354:
355: // The block is supposed to represent a single element.
356: // But if it does not represent an element , the following will fail.
357: QName qName = block.getQName();
358:
359: OMElement newOM = _createOMElementFromBlock(qName, block,
360: soapFactory);
361: if (om == null) {
362: bElement.addChild(newOM);
363: } else {
364: om.insertSiblingBefore(newOM);
365: om.detach();
366: }
367: }
368:
369: public void setBodyBlock(Block block) throws WebServiceException {
370:
371: // Forces the parser to read all of the blocks
372: getNumBodyBlocks();
373:
374: block.setParent(getParent());
375:
376: // Remove all of the children
377: OMElement bElement = _getBodyBlockParent();
378: Iterator it = bElement.getChildren();
379: while (it.hasNext()) {
380: it.next();
381: it.remove();
382: }
383:
384: if (block.isElementData()) {
385: // If the block is element data then it is safe to create
386: // an OMElement representing the block
387:
388: // The block is supposed to represent a single element.
389: // But if it does not represent an element , the following will fail.
390: QName qName = block.getQName();
391:
392: OMElement newOM = _createOMElementFromBlock(qName, block,
393: soapFactory);
394: bElement.addChild(newOM);
395: } else {
396: // This needs to be fixed, but for now we will require that there must be an
397: // element...otherwise no block is added
398: try {
399: QName qName = block.getQName();
400:
401: OMElement newOM = _createOMElementFromBlock(qName,
402: block, soapFactory);
403: bElement.addChild(newOM);
404: } catch (Throwable t) {
405: if (log.isDebugEnabled()) {
406: log
407: .debug("An attempt was made to pass a Source or String that does not "
408: + "have an xml element. Processing continues with an empty payload.");
409: }
410: }
411: }
412: }
413:
414: public void removeBodyBlock(int index) throws WebServiceException {
415: // Forces the parser to read all of the blocks
416: getNumBodyBlocks();
417:
418: OMElement om = this ._getChildOMElement(_getBodyBlockParent(),
419: index);
420: if (om != null) {
421: om.detach();
422: }
423: }
424:
425: public int getNumHeaderBlocks() throws WebServiceException {
426: return _getNumChildElements(root.getHeader());
427: }
428:
429: public Block getHeaderBlock(String namespace, String localPart,
430: Object context, BlockFactory blockFactory)
431: throws WebServiceException {
432: OMElement om = _getChildOMElement(root.getHeader(), namespace,
433: localPart);
434: if (om == null) {
435: return null;
436: }
437: return this ._getBlockFromOMElement(om, context, blockFactory,
438: false);
439: }
440:
441: public void setHeaderBlock(String namespace, String localPart,
442: Block block) throws WebServiceException {
443: block.setParent(getParent());
444: OMElement newOM = _createOMElementFromBlock(new QName(
445: namespace, localPart), block, soapFactory);
446: OMElement om = this ._getChildOMElement(root.getHeader(),
447: namespace, localPart);
448: if (om == null) {
449: if (root.getHeader() == null) {
450: soapFactory.createSOAPHeader(root);
451: }
452: root.getHeader().addChild(newOM);
453: } else {
454: om.insertSiblingBefore(newOM);
455: om.detach();
456: }
457: }
458:
459: public void removeHeaderBlock(String namespace, String localPart)
460: throws WebServiceException {
461: OMElement om = this ._getChildOMElement(root.getHeader(),
462: namespace, localPart);
463: if (om != null) {
464: om.detach();
465: }
466: }
467:
468: public String traceString(String indent) {
469: // TODO Trace String Support
470: return null;
471: }
472:
473: public String getXMLPartContentType() {
474: return "SPINE";
475: }
476:
477: public boolean isFault() throws WebServiceException {
478: return XMLFaultUtils.isFault(root);
479: }
480:
481: public Style getStyle() {
482: return style;
483: }
484:
485: public QName getOperationElement() {
486: OMElement omElement = this ._getBodyBlockParent();
487: if (omElement instanceof SOAPBody) {
488: return null;
489: } else {
490: return omElement.getQName();
491: }
492: }
493:
494: public void setOperationElement(QName operationQName) {
495: OMElement opElement = this ._getBodyBlockParent();
496: if (!(opElement instanceof SOAPBody)) {
497: OMNamespace ns = soapFactory.createOMNamespace(
498: operationQName.getNamespaceURI(), operationQName
499: .getPrefix());
500: opElement.setLocalName(operationQName.getLocalPart());
501: opElement.setNamespace(ns);
502: }
503: }
504:
505: private Block _getBlockFromOMElement(OMElement om, Object context,
506: BlockFactory blockFactory, boolean setComplete)
507: throws WebServiceException {
508: try {
509: QName qName = om.getQName();
510: /* TODO We could gain performance if OMSourcedElement exposed a getDataSource method
511: if (om instanceof OMSourcedElementImpl &&
512: ((OMSourcedElementImpl) om).getDataSource() instanceof Block) {
513: Block oldBlock = (Block) ((OMSourcedElementImpl) om).getDataSource();
514: Block newBlock = blockFactory.createFrom(oldBlock, context);
515: newBlock.setParent(getParent());
516: if (newBlock != oldBlock) {
517: // Replace the OMElement with the OMSourcedElement that delegates to the block
518: OMSourcedElementImpl newOM = new OMSourcedElementImpl(qName, soapFactory, newBlock);
519: om.insertSiblingBefore(newOM);
520: om.detach();
521: }
522: return newBlock;
523: }
524: */
525:
526: // Create the block
527: Block block = blockFactory.createFrom(om, context, qName);
528: block.setParent(getParent());
529:
530: // Get the business object to force a parse
531: block.getBusinessObject(false);
532:
533: // Replace the OMElement with the OMSourcedElement that delegates to the block
534: OMElement newOM = _createOMElementFromBlock(qName, block,
535: soapFactory);
536: om.insertSiblingBefore(newOM);
537:
538: // We want to set the om element and its parents to complete to
539: // shutdown the parsing.
540: if (setComplete) {
541:
542: // Get the root of the document
543: OMElementImpl root = (OMElementImpl) om;
544: while (root.getParent() instanceof OMElementImpl) {
545: root = (OMElementImpl) root.getParent();
546: }
547:
548: try {
549: if (!root.isComplete() && root.getBuilder() != null
550: && !root.getBuilder().isCompleted()) {
551: // Forward the parser to the end so it will close
552: while (root.getBuilder().next() != XMLStreamConstants.END_DOCUMENT) {
553: //do nothing
554: }
555: }
556: } catch (Exception e) {
557: // Log and continue
558: if (log.isDebugEnabled()) {
559: log.debug("Builder next error:"
560: + e.getMessage());
561: log.debug(JavaUtils.stackToString(e));
562: }
563:
564: }
565:
566: OMContainer o = om;
567: while (o != null && o instanceof OMContainerEx) {
568: ((OMContainerEx) o).setComplete(true);
569: if ((o instanceof OMNode)
570: && (((OMNode) o).getParent()) instanceof OMContainer) {
571: o = ((OMNode) o).getParent();
572: } else {
573: o = null;
574: }
575: }
576: }
577:
578: om.detach();
579: return block;
580: } catch (XMLStreamException xse) {
581: throw ExceptionFactory.makeWebServiceException(xse);
582: }
583: }
584:
585: private static OMElement _createOMElementFromBlock(QName qName,
586: Block b, SOAPFactory soapFactory) {
587: return new OMSourcedElementImpl(qName, soapFactory, b);
588: }
589:
590: /**
591: * Gets the OMElement that is the parent of where the the body blocks are located
592: *
593: * @return
594: */
595: private OMElement _getBodyBlockParent() {
596: SOAPBody body = root.getBody();
597: if (!body.hasFault() && indirection == 1) {
598: // For RPC the blocks are within the operation element
599: OMElement op = body.getFirstElement();
600: if (op == null) {
601: // Create one
602: OMNamespace ns = soapFactory.createOMNamespace("", "");
603: op = soapFactory.createOMElement(
604: "PLACEHOLDER_OPERATION", ns, body);
605: }
606: return op;
607: }
608: return body;
609: }
610:
611: /**
612: * Create a factory for the specified protocol
613: *
614: * @param protocol
615: * @return
616: */
617: private static SOAPFactory _getFactory(Protocol protocol) {
618: SOAPFactory soapFactory;
619: if (protocol == Protocol.soap11) {
620: soapFactory = new SOAP11Factory();
621: } else if (protocol == Protocol.soap12) {
622: soapFactory = new SOAP12Factory();
623: } else if (protocol == Protocol.rest) {
624: // For REST, create a SOAP 1.1 Envelope to contain the message
625: // This is consistent with Axis2.
626: soapFactory = new SOAP11Factory();
627: } else {
628: throw ExceptionFactory.makeWebServiceException(Messages
629: .getMessage("RESTIsNotSupported"), null);
630: }
631: return soapFactory;
632: }
633:
634: /**
635: * Create an emtpy envelope
636: *
637: * @param protocol
638: * @param style
639: * @param factory
640: * @return
641: */
642: private static SOAPEnvelope _createEmptyEnvelope(Style style,
643: SOAPFactory factory) {
644: SOAPEnvelope env = factory.createSOAPEnvelope();
645: // Add an empty body and header
646: factory.createSOAPBody(env);
647: factory.createSOAPHeader(env);
648:
649: // Create a dummy operation element if this is an rpc message
650: if (style == Style.RPC) {
651: OMNamespace ns = factory.createOMNamespace("", "");
652: factory.createOMElement("PLACEHOLDER_OPERATION", ns, env
653: .getBody());
654: }
655:
656: return env;
657: }
658:
659: /* (non-Javadoc)
660: * @see org.apache.axis2.jaxws.message.XMLPart#getNumBodyBlocks()
661: */
662: private static int _getNumChildElements(OMElement om)
663: throws WebServiceException {
664: // Avoid calling this method. It advances the parser.
665: if (om == null) {
666: return 0;
667: }
668: int num = 0;
669: Iterator iterator = om.getChildElements();
670: while (iterator.hasNext()) {
671: num++;
672: iterator.next();
673: }
674: return num;
675: }
676:
677: /**
678: * Get the child om at the indicated index
679: *
680: * @param om
681: * @param index
682: * @return child om or null
683: */
684: private static OMElement _getChildOMElement(OMElement om, int index) {
685: if (om == null) {
686: return null;
687: }
688: int i = 0;
689: for (OMNode child = om.getFirstOMChild(); child != null; child = child
690: .getNextOMSibling()) {
691: if (child instanceof OMElement) {
692: if (i == index) {
693: return (OMElement) child;
694: }
695: i++;
696: }
697: }
698: return null;
699: }
700:
701: /**
702: * Get the child om at the indicated index
703: *
704: * @param om
705: * @param index
706: * @return child om or null
707: */
708: private static OMElement _getChildOMElement(OMElement om,
709: String namespace, String localPart) {
710: if (om == null) {
711: return null;
712: }
713: QName qName = new QName(namespace, localPart);
714: Iterator it = om.getChildrenWithName(qName);
715: if (it != null && it.hasNext()) {
716: return (OMElement) it.next();
717: }
718: return null;
719: }
720:
721: }
|