| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.apache.axis2.jaxws.message.impl; |
| |
| import org.apache.axiom.om.OMDataSourceExt; |
| import org.apache.axiom.om.OMElement; |
| import org.apache.axiom.om.OMException; |
| import org.apache.axiom.om.OMOutputFormat; |
| import org.apache.axiom.om.ds.ByteArrayDataSource; |
| import org.apache.axiom.om.impl.MTOMXMLStreamWriter; |
| import org.apache.axiom.om.impl.builder.StAXOMBuilder; |
| import org.apache.axiom.om.util.StAXUtils; |
| import org.apache.axis2.jaxws.ExceptionFactory; |
| import org.apache.axis2.jaxws.i18n.Messages; |
| import org.apache.axis2.jaxws.message.Block; |
| import org.apache.axis2.jaxws.message.Message; |
| import org.apache.axis2.jaxws.message.factory.BlockFactory; |
| import org.apache.axis2.jaxws.message.util.Reader2Writer; |
| import org.apache.axis2.jaxws.spi.Constants; |
| import org.apache.axis2.jaxws.utility.JavaUtils; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| |
| import javax.xml.namespace.QName; |
| import javax.xml.stream.XMLStreamException; |
| import javax.xml.stream.XMLStreamReader; |
| import javax.xml.stream.XMLStreamWriter; |
| import javax.xml.ws.WebServiceException; |
| import java.io.OutputStream; |
| import java.io.StringReader; |
| import java.io.UnsupportedEncodingException; |
| import java.io.Writer; |
| import java.util.HashMap; |
| |
| /** |
| * BlockImpl Abstract Base class for various Block Implementations. |
| * <p/> |
| * The base class takes care of controlling the transformations between BusinessObject, |
| * XMLStreamReader and SOAPElement A derived class must minimally define the following: |
| * _getBOFromReader _getReaderFromBO _outputFromBO |
| * <p/> |
| * In addtion, the derived class may want to override the following: _getBOFromBO ...if the |
| * BusinessObject is consumed when read (i.e. it is an InputSource) |
| * <p/> |
| * The derived classes don't have direct access to the instance data. This ensures that BlockImpl |
| * controls the transformations. |
| */ |
| public abstract class BlockImpl implements Block { |
| |
| private static Log log = LogFactory.getLog(BlockImpl.class); |
| |
| protected Object busObject; |
| protected Object busContext; |
| |
| protected OMElement omElement = null; |
| |
| protected QName qName; |
| private boolean noQNameAvailable = false; |
| |
| protected BlockFactory factory; |
| protected boolean consumed = false; |
| protected Message parent; |
| |
| private HashMap map = null; // OMDataSourceExt properties |
| |
| /** |
| * A Block has the following components |
| * |
| * @param busObject |
| * @param busContext or null |
| * @param qName or null if unknown |
| * @param factory that creates the Block |
| */ |
| protected BlockImpl(Object busObject, Object busContext, QName qName, BlockFactory factory) { |
| this.busObject = busObject; |
| this.busContext = busContext; |
| this.qName = qName; |
| this.factory = factory; |
| } |
| |
| /** |
| * A Block has the following components |
| * |
| * @param reader |
| * @param busContext or null |
| * @param qName or null if unknown |
| * @param factory that creates the Block |
| */ |
| protected BlockImpl(OMElement omElement, Object busContext, QName qName, BlockFactory factory) { |
| this.omElement = omElement; |
| this.busContext = busContext; |
| this.qName = qName; |
| this.factory = factory; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.axis2.jaxws.message.Block#getBlockFactory() |
| */ |
| public BlockFactory getBlockFactory() { |
| return factory; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.axis2.jaxws.message.Block#getBusinessContext() |
| */ |
| public Object getBusinessContext() { |
| return busContext; |
| } |
| |
| public Message getParent() { |
| return parent; |
| } |
| |
| public void setParent(Message p) { |
| parent = p; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.axis2.jaxws.message.Block#getBusinessObject(boolean) |
| */ |
| public Object getBusinessObject(boolean consume) |
| throws XMLStreamException, WebServiceException { |
| if (consumed) { |
| throw ExceptionFactory.makeWebServiceException( |
| Messages.getMessage("BlockImplErr1", this.getClass().getName())); |
| } |
| if (busObject != null) { |
| busObject = _getBOFromBO(busObject, busContext, consume); |
| } else { |
| // Transform reader into business object |
| busObject = _getBOFromOM(omElement, busContext); |
| omElement = null; |
| } |
| |
| // Save the businessObject in a local variable |
| // so that we can reset the Block if consume was indicated |
| Object newBusObject = busObject; |
| setConsumed(consume); |
| return newBusObject; |
| } |
| |
| |
| /* (non-Javadoc) |
| * @see org.apache.axis2.jaxws.message.Block#getQName() |
| */ |
| public QName getQName() throws WebServiceException { |
| // If the QName is not known, find it |
| try { |
| if (qName == null) { |
| // If a prior call discovered that this content has no QName, then return null |
| if (noQNameAvailable) { |
| return null; |
| } |
| if (omElement == null) { |
| try { |
| XMLStreamReader newReader = _getReaderFromBO(busObject, busContext); |
| busObject = null; |
| StAXOMBuilder builder = new StAXOMBuilder(newReader); |
| omElement = builder.getDocumentElement(); |
| } catch (Exception e) { |
| // Some blocks may represent non-element data |
| if (log.isDebugEnabled()) { |
| log.debug("Exception occurred while obtaining QName:" + e); |
| } |
| if (!isElementData()) { |
| // If this block can hold non-element data, then accept |
| // the fact that there is no qname and continue |
| if (log.isDebugEnabled()) { |
| log.debug("The block does not contain an xml element. Processing continues."); |
| } |
| // Indicate that the content has no QName |
| // The exception is swallowed. |
| noQNameAvailable = true; |
| return null; |
| } else { |
| // The content should contain xml. |
| // Rethrowing the exception. |
| throw ExceptionFactory.makeWebServiceException(e); |
| } |
| } |
| } |
| qName = omElement.getQName(); |
| } |
| return qName; |
| } catch (Exception xse) { |
| setConsumed(true); |
| throw ExceptionFactory.makeWebServiceException(xse); |
| } |
| } |
| |
| /** |
| * This method is intended for derived objects to set the qName |
| * |
| * @param qName |
| */ |
| protected void setQName(QName qName) { |
| this.qName = qName; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.axis2.jaxws.message.Block#getXMLStreamReader(boolean) |
| */ |
| public XMLStreamReader getXMLStreamReader(boolean consume) |
| throws XMLStreamException, WebServiceException { |
| XMLStreamReader newReader = null; |
| if (consumed) { |
| // In some scenarios, the message is written out after the service instance is invoked. |
| // In these situations, it is preferable to simply ignore this block. |
| if (this.getParent() != null && getParent().isPostPivot()) { |
| return _postPivot_getXMLStreamReader(); |
| } |
| throw ExceptionFactory.makeWebServiceException( |
| Messages.getMessage("BlockImplErr1", this.getClass().getName())); |
| } |
| if (omElement != null) { |
| if (consume) { |
| if (omElement.getBuilder() != null && !omElement.getBuilder().isCompleted()) { |
| newReader = omElement.getXMLStreamReaderWithoutCaching(); |
| } else { |
| newReader = omElement.getXMLStreamReader(); |
| } |
| omElement = null; |
| } else { |
| newReader = omElement.getXMLStreamReader(); |
| } |
| } else if (busObject != null) { |
| // Getting the reader does not destroy the BusinessObject |
| busObject = _getBOFromBO(busObject, busContext, consume); |
| newReader = _getReaderFromBO(busObject, busContext); |
| } |
| setConsumed(consume); |
| return newReader; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.axiom.om.OMDataSource#getReader() |
| */ |
| public XMLStreamReader getReader() throws XMLStreamException { |
| return getXMLStreamReader(true); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.axiom.om.OMDataSource#serialize(java.io.OutputStream, org.apache.axiom.om.OMOutputFormat) |
| */ |
| public void serialize(OutputStream output, OMOutputFormat format) throws XMLStreamException { |
| MTOMXMLStreamWriter writer = new MTOMXMLStreamWriter(output, format); |
| serialize(writer); |
| writer.flush(); |
| try { |
| writer.close(); |
| } catch (XMLStreamException e) { |
| // An exception can occur if nothing is written to the |
| // writer. This is possible if the underlying data source |
| // writers to the output stream directly. |
| if (log.isDebugEnabled()) { |
| log.debug("Catching and swallowing exception " + e); |
| } |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.axiom.om.OMDataSource#serialize(java.io.Writer, org.apache.axiom.om.OMOutputFormat) |
| */ |
| public void serialize(Writer writerTarget, OMOutputFormat format) throws XMLStreamException { |
| MTOMXMLStreamWriter writer = |
| new MTOMXMLStreamWriter(StAXUtils.createXMLStreamWriter(writerTarget)); |
| writer.setOutputFormat(format); |
| serialize(writer); |
| writer.flush(); |
| writer.close(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.axiom.om.OMDataSource#serialize(javax.xml.stream.XMLStreamWriter) |
| */ |
| public void serialize(XMLStreamWriter writer) throws XMLStreamException { |
| outputTo(writer, isDestructiveWrite()); |
| } |
| |
| public OMElement getOMElement() throws XMLStreamException, WebServiceException { |
| OMElement newOMElement = null; |
| boolean consume = true; // get the OM consumes the message |
| if (consumed) { |
| throw ExceptionFactory.makeWebServiceException( |
| Messages.getMessage("BlockImplErr1", this.getClass().getName())); |
| } |
| if (omElement != null) { |
| newOMElement = omElement; |
| } else if (busObject != null) { |
| // Getting the reader does not destroy the BusinessObject |
| busObject = _getBOFromBO(busObject, busContext, consume); |
| newOMElement = _getOMFromBO(busObject, busContext); |
| } |
| setConsumed(consume); |
| return newOMElement; |
| } |
| |
| |
| /* (non-Javadoc) |
| * @see org.apache.axis2.jaxws.message.Block#isConsumed() |
| */ |
| public boolean isConsumed() { |
| return consumed; |
| } |
| |
| /** |
| * Once consumed, all instance data objects are nullified to prevent subsequent access |
| * |
| * @param consume |
| * @return |
| */ |
| public void setConsumed(boolean consume) { |
| if (consume) { |
| this.consumed = true; |
| busObject = null; |
| busContext = null; |
| omElement = null; |
| if (log.isDebugEnabled()) { |
| // The following stack trace consumes indicates where the message is consumed |
| log.debug("Message Block Monitor: Action=Consumed"); |
| log.trace(JavaUtils.stackToString()); |
| } |
| } else { |
| consumed = false; |
| } |
| } |
| |
| public boolean isQNameAvailable() { |
| return (qName != null); |
| } |
| |
| public void outputTo(XMLStreamWriter writer, boolean consume) |
| throws XMLStreamException, WebServiceException { |
| if (log.isDebugEnabled()) { |
| log.debug("Start outputTo"); |
| } |
| if (consumed) { |
| // In some scenarios, the message is written out after the service instance is invoked. |
| // In these situations, it is preferable to simply ignore this block. |
| if (this.getParent() != null && getParent().isPostPivot()) { |
| _postPivot_outputTo(writer); |
| } |
| throw ExceptionFactory.makeWebServiceException( |
| Messages.getMessage("BlockImplErr1", this.getClass().getName())); |
| } |
| if (omElement != null) { |
| _outputFromOM(omElement, writer, consume); |
| } else if (busObject != null) { |
| if (log.isDebugEnabled()) { |
| log.debug("Write business object"); |
| } |
| busObject = _getBOFromBO(busObject, busContext, consume); |
| _outputFromBO(busObject, busContext, writer); |
| } |
| setConsumed(consume); |
| if (log.isDebugEnabled()) { |
| log.debug("End outputTo"); |
| } |
| return; |
| } |
| |
| /** |
| * Called if we have passed the pivot point but someone wants to output the block. The actual |
| * block implementation may choose to override this setting |
| */ |
| protected void _postPivot_outputTo(XMLStreamWriter writer) |
| throws XMLStreamException, WebServiceException { |
| if (log.isDebugEnabled()) { |
| QName theQName = isQNameAvailable() ? getQName() : new QName("unknown"); |
| log.debug("The Block for " + theQName + |
| " is already consumed and therefore it is not written."); |
| log.debug("If you need this block preserved, please set the " + Constants |
| .SAVE_REQUEST_MSG + " property on the MessageContext."); |
| } |
| return; |
| } |
| |
| /** |
| * Called if we have passed the pivot point but someone wants to output the block. The actual |
| * block implementation may choose to override this setting. |
| */ |
| protected XMLStreamReader _postPivot_getXMLStreamReader() |
| throws XMLStreamException, WebServiceException { |
| if (log.isDebugEnabled()) { |
| QName theQName = isQNameAvailable() ? getQName() : new QName("unknown"); |
| log.debug("The Block for " + theQName + |
| " is already consumed and therefore it is only partially read."); |
| log.debug("If you need this block preserved, please set the " + Constants |
| .SAVE_REQUEST_MSG + " property on the MessageContext."); |
| } |
| QName qName = getQName(); |
| String text = ""; |
| if (qName.getNamespaceURI().length() > 0) { |
| text = "<prefix:" + qName.getLocalPart() + " xmlns:prefix='" + qName.getNamespaceURI() + |
| "'/>"; |
| } else { |
| text = "<" + qName.getLocalPart() + "/>"; |
| } |
| StringReader sr = new StringReader(text); |
| return StAXUtils.createXMLStreamReader(sr); |
| } |
| |
| /** |
| * @return true if the representation of the block is currently a business object. Derived classes |
| * may use this information to get information in a performant way. |
| */ |
| protected boolean isBusinessObject() { |
| return busObject != null; |
| } |
| |
| public String traceString(String indent) { |
| // TODO add trace string |
| return null; |
| } |
| |
| /** |
| * The default implementation is to return the business object. A derived block may want to |
| * override this class if the business object is consumed when read (thus the dervived block may |
| * want to make a buffered copy) (An example use case for overriding this method is the |
| * businessObject is an InputSource) |
| * |
| * @param busObject |
| * @param busContext |
| * @param consume |
| * @return |
| */ |
| protected Object _getBOFromBO(Object busObject, Object busContext, boolean consume) { |
| return busObject; |
| } |
| |
| |
| /** |
| * The derived class must provide an implementation that builds the business object from the |
| * reader |
| * |
| * @param reader XMLStreamReader, which is consumed |
| * @param busContext |
| * @return |
| */ |
| protected abstract Object _getBOFromReader(XMLStreamReader reader, Object busContext) |
| throws XMLStreamException, WebServiceException; |
| |
| |
| /** |
| * Default method for getting business object from OM. |
| * Derived classes may override this method to get the business object from a |
| * data source. |
| * |
| * @param om |
| * @param busContext |
| * @return Business Object |
| * @throws XMLStreamException |
| * @throws WebServiceException |
| */ |
| protected Object _getBOFromOM(OMElement omElement, Object busContext) |
| throws XMLStreamException, WebServiceException { |
| XMLStreamReader reader = _getReaderFromOM(omElement); |
| return _getBOFromReader(reader, busContext); |
| } |
| |
| /** |
| * Get an XMLStreamReader for the BusinessObject The derived Block must implement this method |
| * |
| * @param busObj |
| * @param busContext |
| * @return |
| */ |
| protected abstract XMLStreamReader _getReaderFromBO(Object busObj, Object busContext) |
| throws XMLStreamException, WebServiceException; |
| |
| /** |
| * @param omElement |
| * @return XMLStreamReader |
| */ |
| protected XMLStreamReader _getReaderFromOM(OMElement omElement) { |
| XMLStreamReader reader; |
| if (omElement.getBuilder() != null && !omElement.getBuilder().isCompleted()) { |
| reader = omElement.getXMLStreamReaderWithoutCaching(); |
| } else { |
| reader = omElement.getXMLStreamReader(); |
| } |
| return reader; |
| } |
| |
| /** |
| * @param busObject |
| * @param busContext |
| * @return OMElement |
| * @throws XMLStreamException |
| * @throws WebServiceException |
| */ |
| protected OMElement _getOMFromBO(Object busObject, Object busContext) |
| throws XMLStreamException, WebServiceException { |
| // Getting the reader does not destroy the BusinessObject |
| XMLStreamReader newReader = _getReaderFromBO(busObject, busContext); |
| StAXOMBuilder builder = new StAXOMBuilder(newReader); |
| return builder.getDocumentElement(); |
| } |
| |
| /** |
| * Output Reader contents to a Writer. The default implementation is probably sufficient for most |
| * derived classes. |
| * |
| * @param reader |
| * @param writer |
| * @throws XMLStreamException |
| */ |
| protected void _outputFromReader(XMLStreamReader reader, XMLStreamWriter writer) |
| throws XMLStreamException { |
| Reader2Writer r2w = new Reader2Writer(reader); |
| r2w.outputTo(writer); |
| } |
| |
| /** |
| * Output OMElement contents to a Writer. The default implementation is probably sufficient for most |
| * derived classes. |
| * |
| * @param om |
| * @param writer |
| * @throws XMLStreamException |
| */ |
| protected void _outputFromOM(OMElement omElement, XMLStreamWriter writer, boolean consume) |
| throws XMLStreamException { |
| if (consume) { |
| if (log.isDebugEnabled()) { |
| log.debug("Write using OMElement.serializeAndConsume"); |
| } |
| omElement.serializeAndConsume(writer); |
| } else { |
| if (log.isDebugEnabled()) { |
| log.debug("Write Using OMElement.serialize"); |
| } |
| omElement.serialize(writer); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.axiom.om.OMDataSourceExt#copy() |
| */ |
| public OMDataSourceExt copy() throws OMException { |
| // TODO: This is a default implementation. Much |
| // more refactoring needs to occur to account for attachments. |
| try { |
| String encoding = "utf-8"; // Choose a common encoding |
| byte[] bytes = this.getXMLBytes(encoding); |
| return new ByteArrayDataSource(bytes, encoding); |
| } catch (UnsupportedEncodingException e) { |
| throw new OMException(e); |
| } |
| } |
| |
| /** |
| * Output BusinessObject contents to a Writer. |
| * Derived classes must provide this implementation |
| * @param busObject |
| * @param busContext |
| * @param writer |
| * @throws XMLStreamException |
| * @throws WebServiceException |
| */ |
| protected abstract void _outputFromBO(Object busObject, Object busContext, |
| XMLStreamWriter writer) |
| throws XMLStreamException, WebServiceException; |
| |
| public Object getProperty(String key) { |
| if (map == null) { |
| return null; |
| } |
| return map.get(key); |
| } |
| |
| public Object setProperty(String key, Object value) { |
| if (map == null) { |
| map = new HashMap(); |
| } |
| return map.put(key, value); |
| } |
| |
| public boolean hasProperty(String key) { |
| if (map == null) { |
| return false; |
| } |
| return map.containsKey(key); |
| } |
| } |