Author: veithen Date: Fri Feb 27 01:28:42 2009 New Revision: 748368 URL: http://svn.apache.org/viewvc?rev=748368&view=rev Log: Optimized processing of JMS BytesMessages with content type application/octet-stream by allowing the transport to pass a DataSource object instead of an InputStream to the message builder (BinaryBuilder in this case). The corresponding method is defined by a new optional interface DataSourceMessageBuilder. When this method is used, the message builder can process the message without creating a copy of the data.
Added: webservices/commons/trunk/modules/transport/modules/base/src/main/java/org/apache/axis2/format/DataSourceMessageBuilder.java (with props) webservices/commons/trunk/modules/transport/modules/jms/src/main/java/org/apache/axis2/transport/jms/BytesMessageDataSource.java (with props) Modified: webservices/commons/trunk/modules/transport/modules/base/src/main/java/org/apache/axis2/format/BinaryBuilder.java webservices/commons/trunk/modules/transport/modules/jms/src/main/java/org/apache/axis2/transport/jms/BytesMessageInputStream.java webservices/commons/trunk/modules/transport/modules/jms/src/main/java/org/apache/axis2/transport/jms/JMSUtils.java Modified: webservices/commons/trunk/modules/transport/modules/base/src/main/java/org/apache/axis2/format/BinaryBuilder.java URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/transport/modules/base/src/main/java/org/apache/axis2/format/BinaryBuilder.java?rev=748368&r1=748367&r2=748368&view=diff ============================================================================== --- webservices/commons/trunk/modules/transport/modules/base/src/main/java/org/apache/axis2/format/BinaryBuilder.java (original) +++ webservices/commons/trunk/modules/transport/modules/base/src/main/java/org/apache/axis2/format/BinaryBuilder.java Fri Feb 27 01:28:42 2009 @@ -22,6 +22,7 @@ import java.io.InputStream; import javax.activation.DataHandler; +import javax.activation.DataSource; import javax.xml.namespace.QName; import org.apache.axiom.attachments.ByteArrayDataSource; @@ -29,7 +30,6 @@ import org.apache.axiom.om.OMElement; import org.apache.axiom.om.OMFactory; import org.apache.axis2.AxisFault; -import org.apache.axis2.builder.Builder; import org.apache.axis2.context.MessageContext; import org.apache.axis2.description.Parameter; import org.apache.commons.io.IOUtils; @@ -44,8 +44,8 @@ * be configured as a service parameter (see {...@link BaseConstants#WRAPPER_PARAM}). * It defaults to {...@link BaseConstants#DEFAULT_BINARY_WRAPPER}. */ -public class BinaryBuilder implements Builder { - public OMElement processDocument(InputStream inputStream, +public class BinaryBuilder implements DataSourceMessageBuilder { + public OMElement processDocument(DataSource dataSource, String contentType, MessageContext msgContext) throws AxisFault { QName wrapperQName = BaseConstants.DEFAULT_BINARY_WRAPPER; @@ -57,15 +57,22 @@ } OMFactory factory = OMAbstractFactory.getOMFactory(); OMElement wrapper = factory.createOMElement(wrapperQName, null); + DataHandler dataHandler = new DataHandler(dataSource); + wrapper.addChild(factory.createOMText(dataHandler, true)); + msgContext.setDoingMTOM(true); + return wrapper; + } + + public OMElement processDocument(InputStream inputStream, + String contentType, + MessageContext msgContext) throws AxisFault { + // TODO: this could be further optimized by deferring the read operation byte[] msgBytes; try { msgBytes = IOUtils.toByteArray(inputStream); } catch (IOException ex) { throw new AxisFault("Unable to read message payload", ex); } - DataHandler dataHandler = new DataHandler(new ByteArrayDataSource(msgBytes)); - wrapper.addChild(factory.createOMText(dataHandler, true)); - msgContext.setDoingMTOM(true); - return wrapper; + return processDocument(new ByteArrayDataSource(msgBytes), contentType, msgContext); } } Added: webservices/commons/trunk/modules/transport/modules/base/src/main/java/org/apache/axis2/format/DataSourceMessageBuilder.java URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/transport/modules/base/src/main/java/org/apache/axis2/format/DataSourceMessageBuilder.java?rev=748368&view=auto ============================================================================== --- webservices/commons/trunk/modules/transport/modules/base/src/main/java/org/apache/axis2/format/DataSourceMessageBuilder.java (added) +++ webservices/commons/trunk/modules/transport/modules/base/src/main/java/org/apache/axis2/format/DataSourceMessageBuilder.java Fri Feb 27 01:28:42 2009 @@ -0,0 +1,57 @@ +/* + * 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.format; + +import javax.activation.DataSource; + +import org.apache.axiom.om.OMElement; +import org.apache.axis2.AxisFault; +import org.apache.axis2.builder.Builder; +import org.apache.axis2.context.MessageContext; + +/** + * Message builder able to build messages from {...@link DataSource} objects. + * This interface can be optionally implemented by {...@link Builder} + * implementations that support building messages from {...@link DataSource} objects. + * Since by definition the data from a {...@link DataSource} can be read multiple + * times, this interface can be used by message builders to avoid storing the + * message content in memory. + * <p> + * If a message builder implements this interface and the transport is able to + * provide the message payload as a data source, then the method defined by this + * interface should be preferred over the method defined by {...@link Builder}. + * <p> + * When a message builder is invoked through the basic {...@link Builder} interface, + * it is the responsibility of the transport to close the input stream once the + * message has been processed, and the builder is not required to consume the input + * stream immediately. On the other hand, when the builder is invoked through this extension + * interface, the transport is only responsible for ensuring that the {...@link DataSource} + * remains valid for the whole lifecycle of the message. It is the responsibility of the + * builder to acquire the input stream and to make sure that it is closed when no longer + * needed. This important difference is the reason why there is no + * DataSourceMessageBuilderAdapter class. + * <p> + * Implementing this interface helps optimizing message processing with transports + * that use messaging providers that store messages in memory or on the file system. + * Examples are JMS and VFS. + */ +public interface DataSourceMessageBuilder extends Builder { + public OMElement processDocument(DataSource dataSource, String contentType, + MessageContext messageContext) throws AxisFault; +} Propchange: webservices/commons/trunk/modules/transport/modules/base/src/main/java/org/apache/axis2/format/DataSourceMessageBuilder.java ------------------------------------------------------------------------------ svn:eol-style = native Added: webservices/commons/trunk/modules/transport/modules/jms/src/main/java/org/apache/axis2/transport/jms/BytesMessageDataSource.java URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/transport/modules/jms/src/main/java/org/apache/axis2/transport/jms/BytesMessageDataSource.java?rev=748368&view=auto ============================================================================== --- webservices/commons/trunk/modules/transport/modules/jms/src/main/java/org/apache/axis2/transport/jms/BytesMessageDataSource.java (added) +++ webservices/commons/trunk/modules/transport/modules/jms/src/main/java/org/apache/axis2/transport/jms/BytesMessageDataSource.java Fri Feb 27 01:28:42 2009 @@ -0,0 +1,74 @@ +/* +* Copyright 2004,2005 The Apache Software Foundation. +* +* Licensed 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.transport.jms; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import javax.jms.BytesMessage; +import javax.jms.JMSException; + +import org.apache.axiom.attachments.SizeAwareDataSource; + +/** + * Data source implementation wrapping a JMS {...@link BytesMessage}. + * <p> + * Note that two input streams created by the same instance of this + * class can not be used at the same time. + */ +public class BytesMessageDataSource implements SizeAwareDataSource { + private final BytesMessage message; + private final String contentType; + + public BytesMessageDataSource(BytesMessage message, String contentType) { + this.message = message; + this.contentType = contentType; + } + + public BytesMessageDataSource(BytesMessage message) { + this(message, "application/octet-stream"); + } + + public long getSize() { + try { + return message.getBodyLength(); + } catch (JMSException ex) { + throw new RuntimeException(ex); + } + } + + public String getContentType() { + return contentType; + } + + public InputStream getInputStream() throws IOException { + try { + message.reset(); + } catch (JMSException ex) { + throw new JMSExceptionWrapper(ex); + } + return new BytesMessageInputStream(message); + } + + public String getName() { + return null; + } + + public OutputStream getOutputStream() throws IOException { + throw new UnsupportedOperationException(); + } +} Propchange: webservices/commons/trunk/modules/transport/modules/jms/src/main/java/org/apache/axis2/transport/jms/BytesMessageDataSource.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: webservices/commons/trunk/modules/transport/modules/jms/src/main/java/org/apache/axis2/transport/jms/BytesMessageInputStream.java URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/transport/modules/jms/src/main/java/org/apache/axis2/transport/jms/BytesMessageInputStream.java?rev=748368&r1=748367&r2=748368&view=diff ============================================================================== --- webservices/commons/trunk/modules/transport/modules/jms/src/main/java/org/apache/axis2/transport/jms/BytesMessageInputStream.java (original) +++ webservices/commons/trunk/modules/transport/modules/jms/src/main/java/org/apache/axis2/transport/jms/BytesMessageInputStream.java Fri Feb 27 01:28:42 2009 @@ -23,6 +23,10 @@ /** * Input stream that reads data from a JMS {...@link BytesMessage}. + * Note that since the current position in the message is managed by + * the underlying {...@link BytesMessage} object, it is not possible to + * use several instances of this class operating on a single + * {...@link BytesMessage} at the same time. */ public class BytesMessageInputStream extends InputStream { private final BytesMessage message; Modified: webservices/commons/trunk/modules/transport/modules/jms/src/main/java/org/apache/axis2/transport/jms/JMSUtils.java URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/transport/modules/jms/src/main/java/org/apache/axis2/transport/jms/JMSUtils.java?rev=748368&r1=748367&r2=748368&view=diff ============================================================================== --- webservices/commons/trunk/modules/transport/modules/jms/src/main/java/org/apache/axis2/transport/jms/JMSUtils.java (original) +++ webservices/commons/trunk/modules/transport/modules/jms/src/main/java/org/apache/axis2/transport/jms/JMSUtils.java Fri Feb 27 01:28:42 2009 @@ -25,6 +25,7 @@ import org.apache.axis2.context.MessageContext; import org.apache.axis2.description.AxisService; import org.apache.axis2.description.Parameter; +import org.apache.axis2.format.DataSourceMessageBuilder; import org.apache.axis2.format.TextMessageBuilder; import org.apache.axis2.format.TextMessageBuilderAdapter; import org.apache.commons.logging.Log; @@ -164,55 +165,51 @@ public static void setSOAPEnvelope(Message message, MessageContext msgContext, String contentType) throws AxisFault, JMSException { - if (message instanceof BytesMessage) { - if (contentType == null) { - log.debug("No content type specified; assuming application/octet-stream."); - contentType = "application/octet-stream"; + if (contentType == null) { + if (message instanceof TextMessage) { + contentType = "text/plain"; } else { - // Extract the charset encoding from the content type and - // set the CHARACTER_SET_ENCODING property as e.g. SOAPBuilder relies on this. - String charSetEnc = null; - try { - if (contentType != null) { - charSetEnc = new ContentType(contentType).getParameter("charset"); - } - } catch (ParseException ex) { - // ignore - } - msgContext.setProperty(Constants.Configuration.CHARACTER_SET_ENCODING, charSetEnc); + contentType = "application/octet-stream"; } - - SOAPEnvelope envelope; - try { - envelope = TransportUtils.createSOAPMessage(msgContext, - new BytesMessageInputStream((BytesMessage)message), contentType); - } catch (XMLStreamException ex) { - handleException("Error parsing XML", ex); - return; // Make compiler happy + if (log.isDebugEnabled()) { + log.debug("No content type specified; assuming " + contentType); } - msgContext.setEnvelope(envelope); - - } else if (message instanceof TextMessage) { - String type; - if (contentType == null) { - log.debug("No content type specified; assuming text/plain."); - type = contentType = "text/plain"; - } else { - int index = contentType.indexOf(';'); - if (index > 0) { - type = contentType.substring(0, index); - } else { - type = contentType; - } + } + + int index = contentType.indexOf(';'); + String type = index > 0 ? contentType.substring(0, index) : contentType; + Builder builder = BuilderUtil.getBuilderFromSelector(type, msgContext); + if (builder == null) { + if (log.isDebugEnabled()) { + log.debug("No message builder found for type '" + type + "'. Falling back to SOAP."); } - Builder builder = BuilderUtil.getBuilderFromSelector(type, msgContext); - if (builder == null) { - if (log.isDebugEnabled()) { - log.debug("No message builder found for type '" + type + "'. Falling back to SOAP."); + builder = new SOAPBuilder(); + } + + OMElement documentElement; + if (message instanceof BytesMessage) { + // Extract the charset encoding from the content type and + // set the CHARACTER_SET_ENCODING property as e.g. SOAPBuilder relies on this. + String charSetEnc = null; + try { + if (contentType != null) { + charSetEnc = new ContentType(contentType).getParameter("charset"); } - builder = new SOAPBuilder(); + } catch (ParseException ex) { + // ignore } - + msgContext.setProperty(Constants.Configuration.CHARACTER_SET_ENCODING, charSetEnc); + + if (builder instanceof DataSourceMessageBuilder) { + documentElement = ((DataSourceMessageBuilder)builder).processDocument( + new BytesMessageDataSource((BytesMessage)message), contentType, + msgContext); + } else { + documentElement = builder.processDocument( + new BytesMessageInputStream((BytesMessage)message), contentType, + msgContext); + } + } else if (message instanceof TextMessage) { TextMessageBuilder textMessageBuilder; if (builder instanceof TextMessageBuilder) { textMessageBuilder = (TextMessageBuilder)builder; @@ -220,10 +217,12 @@ textMessageBuilder = new TextMessageBuilderAdapter(builder); } String content = ((TextMessage)message).getText(); - OMElement documentElement - = textMessageBuilder.processDocument(content, contentType, msgContext); - msgContext.setEnvelope(TransportUtils.createSOAPEnvelope(documentElement)); + documentElement = textMessageBuilder.processDocument(content, contentType, msgContext); + } else { + handleException("Unsupported JMS message type " + message.getClass().getName()); + return; // Make compiler happy } + msgContext.setEnvelope(TransportUtils.createSOAPEnvelope(documentElement)); } /**