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.OMElement;
022: import org.apache.axiom.om.OMOutputFormat;
023: import org.apache.axiom.om.impl.MTOMXMLStreamWriter;
024: import org.apache.axiom.om.impl.builder.StAXOMBuilder;
025: import org.apache.axiom.om.util.StAXUtils;
026: import org.apache.axis2.jaxws.ExceptionFactory;
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.factory.BlockFactory;
031: import org.apache.axis2.jaxws.message.util.Reader2Writer;
032: import org.apache.axis2.jaxws.spi.Constants;
033: import org.apache.axis2.jaxws.utility.JavaUtils;
034: import org.apache.commons.logging.Log;
035: import org.apache.commons.logging.LogFactory;
036:
037: import javax.xml.namespace.QName;
038: import javax.xml.stream.XMLStreamException;
039: import javax.xml.stream.XMLStreamReader;
040: import javax.xml.stream.XMLStreamWriter;
041: import javax.xml.ws.WebServiceException;
042: import java.io.OutputStream;
043: import java.io.StringReader;
044: import java.io.Writer;
045:
046: /**
047: * BlockImpl Abstract Base class for various Block Implementations.
048: * <p/>
049: * The base class takes care of controlling the transformations between BusinessObject,
050: * XMLStreamReader and SOAPElement A derived class must minimally define the following:
051: * _getBOFromReader _getReaderFromBO _outputFromBO
052: * <p/>
053: * In addtion, the derived class may want to override the following: _getBOFromBO ...if the
054: * BusinessObject is consumed when read (i.e. it is an InputSource)
055: * <p/>
056: * The derived classes don't have direct access to the instance data. This ensures that BlockImpl
057: * controls the transformations.
058: */
059: public abstract class BlockImpl implements Block {
060:
061: private static Log log = LogFactory.getLog(BlockImpl.class);
062:
063: protected Object busObject;
064: protected Object busContext;
065:
066: protected OMElement omElement = null;
067:
068: protected QName qName;
069: protected BlockFactory factory;
070: protected boolean consumed = false;
071: protected Message parent;
072:
073: /**
074: * A Block has the following components
075: *
076: * @param busObject
077: * @param busContext or null
078: * @param qName or null if unknown
079: * @param factory that creates the Block
080: */
081: protected BlockImpl(Object busObject, Object busContext,
082: QName qName, BlockFactory factory) {
083: this .busObject = busObject;
084: this .busContext = busContext;
085: this .qName = qName;
086: this .factory = factory;
087: }
088:
089: /**
090: * A Block has the following components
091: *
092: * @param reader
093: * @param busContext or null
094: * @param qName or null if unknown
095: * @param factory that creates the Block
096: */
097: protected BlockImpl(OMElement omElement, Object busContext,
098: QName qName, BlockFactory factory) {
099: this .omElement = omElement;
100: this .busContext = busContext;
101: this .qName = qName;
102: this .factory = factory;
103: }
104:
105: /* (non-Javadoc)
106: * @see org.apache.axis2.jaxws.message.Block#getBlockFactory()
107: */
108: public BlockFactory getBlockFactory() {
109: return factory;
110: }
111:
112: /* (non-Javadoc)
113: * @see org.apache.axis2.jaxws.message.Block#getBusinessContext()
114: */
115: public Object getBusinessContext() {
116: return busContext;
117: }
118:
119: public Message getParent() {
120: return parent;
121: }
122:
123: public void setParent(Message p) {
124: parent = p;
125: }
126:
127: /* (non-Javadoc)
128: * @see org.apache.axis2.jaxws.message.Block#getBusinessObject(boolean)
129: */
130: public Object getBusinessObject(boolean consume)
131: throws XMLStreamException, WebServiceException {
132: if (consumed) {
133: throw ExceptionFactory.makeWebServiceException(Messages
134: .getMessage("BlockImplErr1", this .getClass()
135: .getName()));
136: }
137: if (busObject != null) {
138: busObject = _getBOFromBO(busObject, busContext, consume);
139: } else {
140: // Transform reader into business object
141: XMLStreamReader reader;
142: if (omElement.getBuilder() != null
143: && !omElement.getBuilder().isCompleted()) {
144: reader = omElement.getXMLStreamReaderWithoutCaching();
145: } else {
146: reader = omElement.getXMLStreamReader();
147: }
148: busObject = _getBOFromReader(reader, busContext);
149: omElement = null;
150: }
151:
152: // Save the businessObject in a local variable
153: // so that we can reset the Block if consume was indicated
154: Object newBusObject = busObject;
155: setConsumed(consume);
156: return newBusObject;
157: }
158:
159: /* (non-Javadoc)
160: * @see org.apache.axis2.jaxws.message.Block#getQName()
161: */
162: public QName getQName() throws WebServiceException {
163: // If the QName is not known, find it
164: try {
165: if (qName == null) {
166: if (omElement == null) {
167: XMLStreamReader newReader = _getReaderFromBO(
168: busObject, busContext);
169: busObject = null;
170: StAXOMBuilder builder = new StAXOMBuilder(newReader);
171: omElement = builder.getDocumentElement();
172: }
173: qName = omElement.getQName();
174: }
175: return qName;
176: } catch (Exception xse) {
177: setConsumed(true);
178: throw ExceptionFactory.makeWebServiceException(xse);
179: }
180: }
181:
182: /**
183: * This method is intended for derived objects to set the qName
184: *
185: * @param qName
186: */
187: protected void setQName(QName qName) {
188: this .qName = qName;
189: }
190:
191: /* (non-Javadoc)
192: * @see org.apache.axis2.jaxws.message.Block#getXMLStreamReader(boolean)
193: */
194: public XMLStreamReader getXMLStreamReader(boolean consume)
195: throws XMLStreamException, WebServiceException {
196: XMLStreamReader newReader = null;
197: if (consumed) {
198: // In some scenarios, the message is written out after the service instance is invoked.
199: // In these situations, it is preferable to simply ignore this block.
200: if (this .getParent() != null && getParent().isPostPivot()) {
201: return _postPivot_getXMLStreamReader();
202: }
203: throw ExceptionFactory.makeWebServiceException(Messages
204: .getMessage("BlockImplErr1", this .getClass()
205: .getName()));
206: }
207: if (omElement != null) {
208: if (consume) {
209: if (omElement.getBuilder() != null
210: && !omElement.getBuilder().isCompleted()) {
211: newReader = omElement
212: .getXMLStreamReaderWithoutCaching();
213: } else {
214: newReader = omElement.getXMLStreamReader();
215: }
216: omElement = null;
217: } else {
218: newReader = omElement.getXMLStreamReader();
219: }
220: } else if (busObject != null) {
221: // Getting the reader does not destroy the BusinessObject
222: busObject = _getBOFromBO(busObject, busContext, consume);
223: newReader = _getReaderFromBO(busObject, busContext);
224: }
225: setConsumed(consume);
226: return newReader;
227: }
228:
229: /* (non-Javadoc)
230: * @see org.apache.axiom.om.OMDataSource#getReader()
231: */
232: public XMLStreamReader getReader() throws XMLStreamException {
233: return getXMLStreamReader(true);
234: }
235:
236: /* (non-Javadoc)
237: * @see org.apache.axiom.om.OMDataSource#serialize(java.io.OutputStream, org.apache.axiom.om.OMOutputFormat)
238: */
239: public void serialize(OutputStream output, OMOutputFormat format)
240: throws XMLStreamException {
241: MTOMXMLStreamWriter writer = new MTOMXMLStreamWriter(output,
242: format);
243: serialize(writer);
244: writer.flush();
245: }
246:
247: /* (non-Javadoc)
248: * @see org.apache.axiom.om.OMDataSource#serialize(java.io.Writer, org.apache.axiom.om.OMOutputFormat)
249: */
250: public void serialize(Writer writerTarget, OMOutputFormat format)
251: throws XMLStreamException {
252: MTOMXMLStreamWriter writer = new MTOMXMLStreamWriter(StAXUtils
253: .createXMLStreamWriter(writerTarget));
254: writer.setOutputFormat(format);
255: serialize(writer);
256: writer.flush();
257: }
258:
259: /* (non-Javadoc)
260: * @see org.apache.axiom.om.OMDataSource#serialize(javax.xml.stream.XMLStreamWriter)
261: */
262: public void serialize(XMLStreamWriter writer)
263: throws XMLStreamException {
264: outputTo(writer, true);
265: }
266:
267: public OMElement getOMElement() throws XMLStreamException,
268: WebServiceException {
269: OMElement newOMElement = null;
270: boolean consume = true; // get the OM consumes the message
271: if (consumed) {
272: throw ExceptionFactory.makeWebServiceException(Messages
273: .getMessage("BlockImplErr1", this .getClass()
274: .getName()));
275: }
276: if (omElement != null) {
277: newOMElement = omElement;
278: } else if (busObject != null) {
279: // Getting the reader does not destroy the BusinessObject
280: busObject = _getBOFromBO(busObject, busContext, consume);
281: XMLStreamReader newReader = _getReaderFromBO(busObject,
282: busContext);
283: StAXOMBuilder builder = new StAXOMBuilder(newReader);
284: newOMElement = builder.getDocumentElement();
285: }
286: setConsumed(consume);
287: return newOMElement;
288: }
289:
290: /* (non-Javadoc)
291: * @see org.apache.axis2.jaxws.message.Block#isConsumed()
292: */
293: public boolean isConsumed() {
294: return consumed;
295: }
296:
297: /**
298: * Once consumed, all instance data objects are nullified to prevent subsequent access
299: *
300: * @param consume
301: * @return
302: */
303: public void setConsumed(boolean consume) {
304: if (consume) {
305: this .consumed = true;
306: busObject = null;
307: busContext = null;
308: omElement = null;
309: if (log.isDebugEnabled()) {
310: // The following stack trace consumes indicates where the message is consumed
311: log.debug("Message Block Monitor: Action=Consumed");
312: log.debug(JavaUtils.stackToString());
313: }
314: } else {
315: consumed = false;
316: }
317: }
318:
319: public boolean isQNameAvailable() {
320: return (qName != null);
321: }
322:
323: public void outputTo(XMLStreamWriter writer, boolean consume)
324: throws XMLStreamException, WebServiceException {
325: if (consumed) {
326: // In some scenarios, the message is written out after the service instance is invoked.
327: // In these situations, it is preferable to simply ignore this block.
328: if (this .getParent() != null && getParent().isPostPivot()) {
329: _postPivot_outputTo(writer);
330: }
331: throw ExceptionFactory.makeWebServiceException(Messages
332: .getMessage("BlockImplErr1", this .getClass()
333: .getName()));
334: }
335: if (omElement != null) {
336: if (consume) {
337: omElement.serializeAndConsume(writer);
338: } else {
339: omElement.serialize(writer);
340: }
341: } else if (busObject != null) {
342: busObject = _getBOFromBO(busObject, busContext, consume);
343: _outputFromBO(busObject, busContext, writer);
344: }
345: setConsumed(consume);
346: return;
347: }
348:
349: /**
350: * Called if we have passed the pivot point but someone wants to output the block. The actual
351: * block implementation may choose to override this setting
352: */
353: protected void _postPivot_outputTo(XMLStreamWriter writer)
354: throws XMLStreamException, WebServiceException {
355: if (log.isDebugEnabled()) {
356: QName theQName = isQNameAvailable() ? getQName()
357: : new QName("unknown");
358: log
359: .debug("The Block for "
360: + theQName
361: + " is already consumed and therefore it is not written.");
362: log
363: .debug("If you need this block preserved, please set the "
364: + Constants.SAVE_REQUEST_MSG
365: + " property on the MessageContext.");
366: }
367: return;
368: }
369:
370: /**
371: * Called if we have passed the pivot point but someone wants to output the block. The actual
372: * block implementation may choose to override this setting.
373: */
374: protected XMLStreamReader _postPivot_getXMLStreamReader()
375: throws XMLStreamException, WebServiceException {
376: if (log.isDebugEnabled()) {
377: QName theQName = isQNameAvailable() ? getQName()
378: : new QName("unknown");
379: log
380: .debug("The Block for "
381: + theQName
382: + " is already consumed and therefore it is only partially read.");
383: log
384: .debug("If you need this block preserved, please set the "
385: + Constants.SAVE_REQUEST_MSG
386: + " property on the MessageContext.");
387: }
388: QName qName = getQName();
389: String text = "";
390: if (qName.getNamespaceURI().length() > 0) {
391: text = "<prefix:" + qName.getLocalPart()
392: + " xmlns:prefix='" + qName.getNamespaceURI()
393: + "'/>";
394: } else {
395: text = "<" + qName.getLocalPart() + "/>";
396: }
397: StringReader sr = new StringReader(text);
398: return StAXUtils.createXMLStreamReader(sr);
399: }
400:
401: /**
402: * @return true if the representation of the block is currently a business object. Derived classes
403: * may use this information to get information in a performant way.
404: */
405: protected boolean isBusinessObject() {
406: return busObject != null;
407: }
408:
409: public String traceString(String indent) {
410: // TODO add trace string
411: return null;
412: }
413:
414: /**
415: * The default implementation is to return the business object. A derived block may want to
416: * override this class if the business object is consumed when read (thus the dervived block may
417: * want to make a buffered copy) (An example use case for overriding this method is the
418: * businessObject is an InputSource)
419: *
420: * @param busObject
421: * @param busContext
422: * @param consume
423: * @return
424: */
425: protected Object _getBOFromBO(Object busObject, Object busContext,
426: boolean consume) {
427: return busObject;
428: }
429:
430: /**
431: * The derived class must provide an implementation that builds the business object from the
432: * reader
433: *
434: * @param reader XMLStreamReader, which is consumed
435: * @param busContext
436: * @return
437: */
438: protected abstract Object _getBOFromReader(XMLStreamReader reader,
439: Object busContext) throws XMLStreamException,
440: WebServiceException;
441:
442: /**
443: * Get an XMLStreamReader for the BusinessObject The derived Block must implement this method
444: *
445: * @param busObj
446: * @param busContext
447: * @return
448: */
449: protected abstract XMLStreamReader _getReaderFromBO(Object busObj,
450: Object busContext) throws XMLStreamException,
451: WebServiceException;
452:
453: /**
454: * Output Reader contents to a Writer. The default implementation is probably sufficient for most
455: * derived classes.
456: *
457: * @param reader
458: * @param writer
459: * @throws XMLStreamException
460: */
461: protected void _outputFromReader(XMLStreamReader reader,
462: XMLStreamWriter writer) throws XMLStreamException {
463: Reader2Writer r2w = new Reader2Writer(reader);
464: r2w.outputTo(writer);
465: }
466:
467: /**
468: * Output BusinessObject contents to a Writer.
469: * Derived classes must provide this implementation
470: * @param busObject
471: * @param busContext
472: * @param writer
473: * @throws XMLStreamException
474: * @throws WebServiceException
475: */
476: protected abstract void _outputFromBO(Object busObject,
477: Object busContext, XMLStreamWriter writer)
478: throws XMLStreamException, WebServiceException;
479:
480: }
|