summaryrefslogtreecommitdiffstats
path: root/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format
diff options
context:
space:
mode:
Diffstat (limited to 'branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format')
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/BinaryBuilder.java78
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/BinaryFormatter.java96
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/DataSourceMessageBuilder.java77
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/ElementHelper.java111
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/ManagedDataSource.java36
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/ManagedDataSourceFactory.java131
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/MessageFormatterEx.java44
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/MessageFormatterExAdapter.java83
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/PlainTextBuilder.java114
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/PlainTextFormatter.java98
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextFromElementDataSource.java65
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextFromElementReader.java160
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextMessageBuilder.java48
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextMessageBuilderAdapter.java78
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/WrappedTextNodeOMDataSourceFromDataSource.java114
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/WrappedTextNodeOMDataSourceFromReader.java99
-rw-r--r--branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/WrappedTextNodeStreamReader.java437
17 files changed, 1869 insertions, 0 deletions
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/BinaryBuilder.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/BinaryBuilder.java
new file mode 100644
index 0000000000..3f47fd855b
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/BinaryBuilder.java
@@ -0,0 +1,78 @@
+/*
+ * 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.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.activation.DataHandler;
+import javax.activation.DataSource;
+import javax.xml.namespace.QName;
+
+import org.apache.axiom.attachments.ByteArrayDataSource;
+import org.apache.axiom.om.OMAbstractFactory;
+import org.apache.axiom.om.OMElement;
+import org.apache.axiom.om.OMFactory;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.context.MessageContext;
+import org.apache.axis2.description.Parameter;
+import org.apache.commons.io.IOUtils;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.BaseConstants;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.BaseUtils;
+
+/**
+ * Message builder for binary payloads.
+ * <p>
+ * This builder processes the input message as binary and wraps
+ * the data in a wrapper element. The name of the wrapper element can
+ * be configured as a service parameter (see {@link BaseConstants#WRAPPER_PARAM}).
+ * It defaults to {@link BaseConstants#DEFAULT_BINARY_WRAPPER}.
+ */
+public class BinaryBuilder implements DataSourceMessageBuilder {
+ public OMElement processDocument(DataSource dataSource,
+ String contentType,
+ MessageContext msgContext) throws AxisFault {
+ QName wrapperQName = BaseConstants.DEFAULT_BINARY_WRAPPER;
+ if (msgContext.getAxisService() != null) {
+ Parameter wrapperParam = msgContext.getAxisService().getParameter(BaseConstants.WRAPPER_PARAM);
+ if (wrapperParam != null) {
+ wrapperQName = BaseUtils.getQNameFromString(wrapperParam.getValue());
+ }
+ }
+ 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);
+ }
+ return processDocument(new ByteArrayDataSource(msgBytes), contentType, msgContext);
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/BinaryFormatter.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/BinaryFormatter.java
new file mode 100644
index 0000000000..1a20d61d4b
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/BinaryFormatter.java
@@ -0,0 +1,96 @@
+/*
+ * 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.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URL;
+
+import javax.activation.DataHandler;
+import javax.activation.DataSource;
+
+import org.apache.axiom.om.OMElement;
+import org.apache.axiom.om.OMNode;
+import org.apache.axiom.om.OMOutputFormat;
+import org.apache.axiom.om.OMText;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.context.MessageContext;
+import org.apache.axis2.transport.http.util.URLTemplatingUtil;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.BaseConstants;
+
+public class BinaryFormatter implements MessageFormatterEx {
+ public byte[] getBytes(MessageContext messageContext, OMOutputFormat format) throws AxisFault {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ writeTo(messageContext, format, baos, true);
+ return baos.toByteArray();
+ }
+
+ private DataHandler getDataHandler(MessageContext messageContext) {
+ OMElement firstChild = messageContext.getEnvelope().getBody().getFirstElement();
+ if (BaseConstants.DEFAULT_BINARY_WRAPPER.equals(firstChild.getQName())) {
+ OMNode omNode = firstChild.getFirstOMChild();
+ if (omNode != null && omNode instanceof OMText) {
+ Object dh = ((OMText)omNode).getDataHandler();
+ if (dh != null && dh instanceof DataHandler) {
+ return (DataHandler)dh;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void writeTo(MessageContext messageContext, OMOutputFormat format,
+ OutputStream outputStream, boolean preserve) throws AxisFault {
+ DataHandler dh = getDataHandler(messageContext);
+ if (dh != null) {
+ try {
+ ((DataHandler)dh).writeTo(outputStream);
+ } catch (IOException e) {
+ throw new AxisFault("Error serializing binary content of element : " +
+ BaseConstants.DEFAULT_BINARY_WRAPPER, e);
+ }
+ }
+ }
+
+ public String getContentType(MessageContext messageContext,
+ OMOutputFormat format, String soapAction) {
+ DataHandler dh = getDataHandler(messageContext);
+ if (dh != null) {
+ return dh.getContentType();
+ } else {
+ return null;
+ }
+ }
+
+ public URL getTargetAddress(MessageContext messageContext,
+ OMOutputFormat format, URL targetURL) throws AxisFault {
+ return URLTemplatingUtil.getTemplatedURL(targetURL, messageContext, false);
+ }
+
+ public String formatSOAPAction(MessageContext messageContext,
+ OMOutputFormat format, String soapAction) {
+ return null;
+ }
+
+ public DataSource getDataSource(MessageContext messageContext,
+ OMOutputFormat format, String soapAction) throws AxisFault {
+ return getDataHandler(messageContext).getDataSource();
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/DataSourceMessageBuilder.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/DataSourceMessageBuilder.java
new file mode 100644
index 0000000000..4a299df50d
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/DataSourceMessageBuilder.java
@@ -0,0 +1,77 @@
+/*
+ * 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.tuscany.sca.binding.ws.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>
+ * 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.
+ * <p>
+ * The builder will typically expose the data source directly or indirectly through
+ * the returned {@link OMElement}, e.g. by adding to the tree an {@link org.apache.axiom.om.OMText}
+ * or {@link org.apache.axiom.om.OMDataSource} node referencing the data source.
+ * This means that the builder will not be able to guarantee that all streams requested
+ * from the data source are properly closed. Note that code accessing the returned
+ * {@link OMElement} can't be expected to take care of this since in many cases the fact
+ * that a data source is being used is completely transparent to that code.
+ * It is therefore the responsibility of the transport to make sure that all resources linked to
+ * the data source itself as well as any open stream requested from that data source are properly
+ * released after the message has been processed. Depending on the type of transport, there are
+ * three possible cases:
+ * <ol>
+ * <li>All resources allocated to the data source or streams requested from it are
+ * memory based. In that case the garbage collector will take care of freeing
+ * these resources and the transport should simply pass the data source object
+ * to the builder.</li>
+ * <li>There are operation system resources linked to the data source and open
+ * streams will become invalid when these resources are freed, i.e.
+ * it is not required that all streams be closed explicitly.
+ * In this case the transport only needs to take care to properly dispose of
+ * the data source after the message has been processed by the Axis2 engine.</li>
+ * <li>Requesting a stream from the data source allocates operation system resources
+ * (e.g. a network connection) that remain linked to the stream, i.e. all streams requested
+ * from the data source must be closed properly. In that case the transport should use
+ * {@link ManagedDataSourceFactory#create(DataSource)} to wrap the original data source
+ * before passing it to the builder. After the message has been processed it should
+ * then call {@link ManagedDataSource#destroy()} on the wrapper to close all remaining
+ * open streams.</li>
+ * </ol>
+ */
+public interface DataSourceMessageBuilder extends Builder {
+ public OMElement processDocument(DataSource dataSource, String contentType,
+ MessageContext messageContext) throws AxisFault;
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/ElementHelper.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/ElementHelper.java
new file mode 100644
index 0000000000..c3cd09b67a
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/ElementHelper.java
@@ -0,0 +1,111 @@
+/*
+ * 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.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.Writer;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import org.apache.axiom.om.OMElement;
+import org.apache.axiom.om.OMNode;
+import org.apache.axiom.om.OMSourcedElement;
+import org.apache.axiom.om.OMText;
+
+/**
+ * Utility class with methods to work on {@link OMElement} objects.
+ * <p>
+ * NOTICE: The code in this class will be moved to Axiom (or somewhere else). Use with care!
+ */
+public class ElementHelper {
+ private ElementHelper() {}
+
+ /**
+ * Returns a stream representing the concatenation of the text nodes that are children of a
+ * given element.
+ * The stream returned by this method produces exactly the same character sequence as the
+ * the stream created by the following expression:
+ * <pre>new StringReader(element.getText())</pre>
+ * The difference is that the stream implementation returned by this method is guaranteed
+ * to have constant memory usage and is optimized for performance.
+ *
+ * @param element the element to read the text nodes from
+ * @param cache whether to enable caching when accessing the element
+ * @return a stream representing the concatenation of the text nodes
+ *
+ * @see OMElement#getText()
+ */
+ public static Reader getTextAsStream(OMElement element, boolean cache) {
+ // If the element is not an OMSourcedElement and has not more than one child, then the most
+ // efficient way to get the Reader is to build a StringReader
+ if (!(element instanceof OMSourcedElement) && (!cache || element.isComplete())) {
+ OMNode child = element.getFirstOMChild();
+ if (child == null) {
+ return new StringReader("");
+ } else if (child.getNextOMSibling() == null) {
+ return new StringReader(child instanceof OMText ? ((OMText)child).getText() : "");
+ }
+ }
+ // In all other cases, extract the data from the XMLStreamReader
+ return new TextFromElementReader(cache ? element.getXMLStreamReader()
+ : element.getXMLStreamReaderWithoutCaching());
+ }
+
+ /**
+ * Write the content of the text nodes that are children of a given element to a
+ * {@link Writer}.
+ * If <code>cache</code> is true, this method has the same effect as the following instruction:
+ * <pre>out.write(element.getText())</pre>
+ * The difference is that this method is guaranteed to have constant memory usage and is
+ * optimized for performance.
+ *
+ * @param element the element to read the text nodes from
+ * @param out the stream to write the content to
+ * @param cache whether to enable caching when accessing the element
+ * @throws XMLStreamException if an error occurs when reading from the element
+ * @throws IOException if an error occurs when writing to the stream
+ *
+ * @see OMElement#getText()
+ */
+ public static void writeTextTo(OMElement element, Writer out, boolean cache)
+ throws XMLStreamException, IOException {
+
+ XMLStreamReader reader = cache ? element.getXMLStreamReader()
+ : element.getXMLStreamReaderWithoutCaching();
+ int depth = 0;
+ while (reader.hasNext()) {
+ switch (reader.next()) {
+ case XMLStreamReader.CHARACTERS:
+ case XMLStreamReader.CDATA:
+ if (depth == 1) {
+ out.write(reader.getText());
+ }
+ break;
+ case XMLStreamReader.START_ELEMENT:
+ depth++;
+ break;
+ case XMLStreamReader.END_ELEMENT:
+ depth--;
+ }
+ }
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/ManagedDataSource.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/ManagedDataSource.java
new file mode 100644
index 0000000000..d45b741b0a
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/ManagedDataSource.java
@@ -0,0 +1,36 @@
+/*
+ * 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.tuscany.sca.binding.ws.axis2.format;
+
+import javax.activation.DataSource;
+
+/**
+ * Managed data source.
+ * This type of data source keeps track of the streams that have been
+ * requested using {@link DataSource#getInputStream()} and allows to
+ * forcibly close these streams. Any existing data source can be converted
+ * to a managed data source using {@link ManagedDataSourceFactory#create(DataSource)}.
+ */
+public interface ManagedDataSource extends DataSource {
+ /**
+ * Close all streams that have been requested from this data source
+ * and that are not yet closed.
+ */
+ void destroy();
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/ManagedDataSourceFactory.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/ManagedDataSourceFactory.java
new file mode 100644
index 0000000000..0c44721ac7
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/ManagedDataSourceFactory.java
@@ -0,0 +1,131 @@
+/*
+ * 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.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.activation.DataSource;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Utility class to create {@link ManagedDataSource} objects.
+ */
+public class ManagedDataSourceFactory {
+ private static class ManagedInputStream extends FilterInputStream {
+ private DataSourceManager manager;
+
+ public ManagedInputStream(DataSourceManager manager, InputStream parent) {
+ super(parent);
+ this.manager = manager;
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (manager != null) {
+ manager.notifyStreamClosed(this);
+ manager = null;
+ }
+ super.close();
+ }
+ }
+
+ private static class DataSourceManager implements InvocationHandler {
+ private static final Log log = LogFactory.getLog(DataSourceManager.class);
+
+ private static final Method getInputStreamMethod;
+ private static final Method destroyMethod;
+
+ static {
+ try {
+ getInputStreamMethod = DataSource.class.getMethod("getInputStream");
+ destroyMethod = ManagedDataSource.class.getMethod("destroy");
+ } catch (NoSuchMethodException ex) {
+ throw new NoSuchMethodError(ex.getMessage());
+ }
+ }
+
+ private final DataSource dataSource;
+ private final List<ManagedInputStream> openStreams = Collections.synchronizedList(
+ new LinkedList<ManagedInputStream>());
+
+ public DataSourceManager(DataSource dataSource) {
+ this.dataSource = dataSource;
+ }
+
+ public void notifyStreamClosed(ManagedInputStream managedInputStream) {
+ if (!openStreams.remove(managedInputStream)) {
+ throw new IllegalStateException();
+ }
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ try {
+ if (method.equals(getInputStreamMethod)) {
+ InputStream in = (InputStream)method.invoke(dataSource, args);
+ ManagedInputStream in2 = new ManagedInputStream(this, in);
+ openStreams.add(in2);
+ return in2;
+ } else if (method.equals(destroyMethod)) {
+ while (!openStreams.isEmpty()) {
+ try {
+ openStreams.get(0).close();
+ } catch (IOException ex) {
+ log.warn("Exception when closing open stream from managed data source", ex);
+ }
+ }
+ return null;
+ } else {
+ return method.invoke(dataSource, args);
+ }
+ } catch (InvocationTargetException ex) {
+ throw ex.getCause();
+ }
+ }
+
+ }
+
+ /**
+ * Create a {@link ManagedDataSource} proxy for an existing data source.
+ * This will create a dynamic proxy implementing the same interfaces as
+ * the original data source.
+ *
+ * @param ds the original data source
+ * @return a data source proxy implementing {@link ManagedDataSource}
+ */
+ public static ManagedDataSource create(DataSource ds) {
+ Class<?>[] orgIfaces = ds.getClass().getInterfaces();
+ Class<?>[] ifaces = new Class[orgIfaces.length+1];
+ ifaces[0] = ManagedDataSource.class;
+ System.arraycopy(orgIfaces, 0, ifaces, 1, orgIfaces.length);
+ return (ManagedDataSource)Proxy.newProxyInstance(
+ ManagedDataSourceFactory.class.getClassLoader(), ifaces,
+ new DataSourceManager(ds));
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/MessageFormatterEx.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/MessageFormatterEx.java
new file mode 100644
index 0000000000..2e6b691938
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/MessageFormatterEx.java
@@ -0,0 +1,44 @@
+/*
+ * 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.tuscany.sca.binding.ws.axis2.format;
+
+import javax.activation.DataSource;
+
+import org.apache.axiom.om.OMOutputFormat;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.context.MessageContext;
+import org.apache.axis2.transport.MessageFormatter;
+
+/**
+ * Message formatter with extended capabilities.
+ * This interface adds new methods to the {@link MessageFormatter}
+ * interface, allowing transport to optimize data transfers.
+ */
+public interface MessageFormatterEx extends MessageFormatter {
+ /**
+ * Get the formatted message as a {@link DataSource} object.
+ *
+ * @param messageContext
+ * @param format
+ * @param soapAction
+ * @return
+ * @throws AxisFault
+ */
+ DataSource getDataSource(MessageContext messageContext, OMOutputFormat format, String soapAction) throws AxisFault;
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/MessageFormatterExAdapter.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/MessageFormatterExAdapter.java
new file mode 100644
index 0000000000..e93df23974
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/MessageFormatterExAdapter.java
@@ -0,0 +1,83 @@
+/*
+ * 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.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.OutputStream;
+import java.net.URL;
+
+import javax.activation.DataSource;
+
+import org.apache.axiom.attachments.ByteArrayDataSource;
+import org.apache.axiom.om.OMOutputFormat;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.context.MessageContext;
+import org.apache.axis2.transport.MessageFormatter;
+
+/**
+ * Adapter to add the {@link MessageFormatterEx} interface to an
+ * existing {@link MessageFormatter}.
+ * It implements the {@link MessageFormatterEx#getDataSource(MessageContext, OMOutputFormat, String)} method
+ * using {@link MessageFormatter#getBytes(MessageContext, OMOutputFormat)} and
+ * {@link MessageFormatter#getContentType(MessageContext, OMOutputFormat, String)}.
+ */
+public class MessageFormatterExAdapter implements MessageFormatterEx {
+ private final MessageFormatter messageFormatter;
+
+ public MessageFormatterExAdapter(MessageFormatter messageFormatter) {
+ this.messageFormatter = messageFormatter;
+ }
+
+ public DataSource getDataSource(MessageContext messageContext,
+ OMOutputFormat format,
+ String soapAction) throws AxisFault {
+ return new ByteArrayDataSource(
+ getBytes(messageContext, format),
+ getContentType(messageContext, format, soapAction));
+ }
+
+ public String formatSOAPAction(MessageContext messageContext,
+ OMOutputFormat format,
+ String soapAction) {
+ return messageFormatter.formatSOAPAction(messageContext, format, soapAction);
+ }
+
+ public byte[] getBytes(MessageContext messageContext,
+ OMOutputFormat format) throws AxisFault {
+ return messageFormatter.getBytes(messageContext, format);
+ }
+
+ public String getContentType(MessageContext messageContext,
+ OMOutputFormat format,
+ String soapAction) {
+ return messageFormatter.getContentType(messageContext, format, soapAction);
+ }
+
+ public URL getTargetAddress(MessageContext messageContext,
+ OMOutputFormat format,
+ URL targetURL) throws AxisFault {
+ return messageFormatter.getTargetAddress(messageContext, format, targetURL);
+ }
+
+ public void writeTo(MessageContext messageContext,
+ OMOutputFormat format,
+ OutputStream outputStream,
+ boolean preserve) throws AxisFault {
+ messageFormatter.writeTo(messageContext, format, outputStream, preserve);
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/PlainTextBuilder.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/PlainTextBuilder.java
new file mode 100644
index 0000000000..c2851bac4c
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/PlainTextBuilder.java
@@ -0,0 +1,114 @@
+/*
+ * 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.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+
+import javax.activation.DataSource;
+import javax.xml.namespace.QName;
+
+import org.apache.axiom.om.OMAbstractFactory;
+import org.apache.axiom.om.OMElement;
+import org.apache.axiom.om.OMFactory;
+import org.apache.axiom.om.impl.llom.OMSourcedElementImpl;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.builder.BuilderUtil;
+import org.apache.axis2.context.MessageContext;
+import org.apache.axis2.description.Parameter;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.BaseConstants;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.BaseUtils;
+
+/**
+ * Message builder for plain text payloads.
+ * <p>
+ * This builder processes the input message as plain text and wraps
+ * the text in a wrapper element. The name of the wrapper element can
+ * be configured as a service parameter (see {@link BaseConstants#WRAPPER_PARAM}).
+ * It defaults to {@link BaseConstants#DEFAULT_TEXT_WRAPPER}.
+ * If the content is provided as an {@link InputStream} and the content type specifies a
+ * <tt>charset</tt> parameter (e.g. <tt>text/plain; charset=ISO-8859-15</tt>),
+ * this information is used to decode the text.
+ * If the content is provided as an {@link InputStream} but no <tt>charset</tt> parameter
+ * is specified on the content type, the default charset encoding specified by
+ * {@link MessageContext#DEFAULT_CHAR_SET_ENCODING} is used.
+ */
+public class PlainTextBuilder implements TextMessageBuilder, DataSourceMessageBuilder {
+ private static QName getWrapperQName(MessageContext msgContext) {
+ QName wrapperQName = BaseConstants.DEFAULT_TEXT_WRAPPER;
+ if (msgContext.getAxisService() != null) {
+ Parameter wrapperParam
+ = msgContext.getAxisService().getParameter(BaseConstants.WRAPPER_PARAM);
+ if (wrapperParam != null) {
+ wrapperQName = BaseUtils.getQNameFromString(wrapperParam.getValue());
+ }
+ }
+ return wrapperQName;
+ }
+
+ public OMElement processDocument(InputStream inputStream,
+ String contentType,
+ MessageContext msgContext) throws AxisFault {
+
+ OMFactory factory = OMAbstractFactory.getOMFactory();
+ String charSetEnc = BuilderUtil.getCharSetEncoding(contentType);
+ QName wrapperQName = getWrapperQName(msgContext);
+ Reader reader;
+ try {
+ reader = new InputStreamReader(inputStream, charSetEnc);
+ } catch (UnsupportedEncodingException ex) {
+ throw new AxisFault("Unsupported encoding: " + charSetEnc, ex);
+ }
+ return new OMSourcedElementImpl(wrapperQName, factory,
+ new WrappedTextNodeOMDataSourceFromReader(wrapperQName, reader));
+ }
+
+ public OMElement processDocument(Reader reader,
+ String contentType,
+ MessageContext msgContext) throws AxisFault {
+
+ OMFactory factory = OMAbstractFactory.getOMFactory();
+ QName wrapperQName = getWrapperQName(msgContext);
+ return new OMSourcedElementImpl(wrapperQName, factory,
+ new WrappedTextNodeOMDataSourceFromReader(wrapperQName, reader));
+ }
+
+ public OMElement processDocument(String content,
+ String contentType,
+ MessageContext msgContext) throws AxisFault {
+ OMFactory factory = OMAbstractFactory.getOMFactory();
+ OMElement wrapper = factory.createOMElement(getWrapperQName(msgContext), null);
+ factory.createOMText(wrapper, content);
+ return wrapper;
+ }
+
+ public OMElement processDocument(DataSource dataSource,
+ String contentType,
+ MessageContext msgContext) throws AxisFault {
+
+ OMFactory factory = OMAbstractFactory.getOMFactory();
+ Charset cs = Charset.forName(BuilderUtil.getCharSetEncoding(contentType));
+ QName wrapperQName = getWrapperQName(msgContext);
+ return new OMSourcedElementImpl(wrapperQName, factory,
+ new WrappedTextNodeOMDataSourceFromDataSource(wrapperQName, dataSource, cs));
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/PlainTextFormatter.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/PlainTextFormatter.java
new file mode 100644
index 0000000000..3dc8684471
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/PlainTextFormatter.java
@@ -0,0 +1,98 @@
+/*
+ * 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.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.net.URL;
+
+import javax.activation.DataSource;
+import javax.xml.stream.XMLStreamException;
+
+import org.apache.axiom.om.OMElement;
+import org.apache.axiom.om.OMOutputFormat;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.context.MessageContext;
+import org.apache.axis2.transport.http.util.URLTemplatingUtil;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.BaseConstants;
+
+public class PlainTextFormatter implements MessageFormatterEx {
+
+ public byte[] getBytes(MessageContext messageContext, OMOutputFormat format) throws AxisFault {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ writeTo(messageContext, format, baos, true);
+ return baos.toByteArray();
+ }
+
+ public void writeTo(MessageContext messageContext, OMOutputFormat format, OutputStream outputStream, boolean preserve) throws AxisFault {
+ OMElement textElt = messageContext.getEnvelope().getBody().getFirstElement();
+ if (BaseConstants.DEFAULT_TEXT_WRAPPER.equals(textElt.getQName())) {
+ try {
+ Writer out = new OutputStreamWriter(outputStream, format.getCharSetEncoding());
+ ElementHelper.writeTextTo(textElt, out, preserve);
+ out.flush();
+ } catch (IOException e) {
+ throw new AxisFault("Error writing text message to stream", e);
+ } catch (XMLStreamException e) {
+ throw new AxisFault("Error extracting the text payload from the message", e);
+ }
+ }
+ }
+
+ public String getContentType(MessageContext messageContext, OMOutputFormat format, String soapAction) {
+ String encoding = format.getCharSetEncoding();
+ String contentType = "text/plain";
+
+ if (encoding != null) {
+ contentType += "; charset=" + encoding;
+ }
+
+ // if soap action is there (can be there is soap response MEP is used) add it.
+ if ((soapAction != null)
+ && !"".equals(soapAction.trim())
+ && !"\"\"".equals(soapAction.trim())) {
+ contentType = contentType + ";action=\"" + soapAction + "\";";
+ }
+
+ return contentType;
+ }
+
+ public URL getTargetAddress(MessageContext msgCtxt, OMOutputFormat format, URL targetURL) throws AxisFault {
+ // Check whether there is a template in the URL, if so we have to replace then with data
+ // values and create a new target URL.
+ targetURL = URLTemplatingUtil.getTemplatedURL(targetURL, msgCtxt, false);
+ return targetURL;
+ }
+
+ public String formatSOAPAction(MessageContext messageContext, OMOutputFormat format, String soapAction) {
+ return null;
+ }
+
+ public DataSource getDataSource(MessageContext messageContext,
+ OMOutputFormat format, String soapAction) throws AxisFault {
+ return new TextFromElementDataSource(
+ messageContext.getEnvelope().getBody().getFirstElement(),
+ format.getCharSetEncoding(),
+ getContentType(messageContext, format, soapAction));
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextFromElementDataSource.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextFromElementDataSource.java
new file mode 100644
index 0000000000..a5597eeea8
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextFromElementDataSource.java
@@ -0,0 +1,65 @@
+/*
+ * 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.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.activation.DataSource;
+
+import org.apache.axiom.om.OMElement;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.streams.ReaderInputStream;
+
+/**
+ * Data source that represents the text of a given {@link OMElement}.
+ * <p>
+ * The expression
+ * <pre>new TextFromElementDataSource(element, charset, contentType)</pre>
+ * produces a DataSource implementation that is equivalent to
+ * <pre>new ByteArrayDataSource(element.getText().getBytes(charset), contentType)</pre>
+ * but that is more efficient.
+ */
+public class TextFromElementDataSource implements DataSource {
+ private final OMElement element;
+ private final String charset;
+ private final String contentType;
+
+ public TextFromElementDataSource(OMElement element, String charset, String contentType) {
+ this.element = element;
+ this.charset = charset;
+ this.contentType = contentType;
+ }
+
+ public String getContentType() {
+ return contentType;
+ }
+
+ public String getName() {
+ return null;
+ }
+
+ public InputStream getInputStream() throws IOException {
+ return new ReaderInputStream(ElementHelper.getTextAsStream(element, true), charset);
+ }
+
+ public OutputStream getOutputStream() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextFromElementReader.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextFromElementReader.java
new file mode 100644
index 0000000000..e4f0931bfc
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextFromElementReader.java
@@ -0,0 +1,160 @@
+/*
+ * 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.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import org.apache.axiom.om.OMException;
+
+/**
+ * {@link Reader} implementation that extracts the text nodes from an element given by an
+ * {@link XMLStreamReader}. The expected input is a document with only a document
+ * element (as produced by {@link org.apache.axiom.om.OMElement.OMElement#getXMLStreamReader()}).
+ * The class will extract the text nodes that are direct children of that element, i.e. it uses
+ * the same conventions as {@link org.apache.axiom.om.OMElement.OMElement#getText()}.
+ * It will call {@link XMLStreamReader#close()} when the end of the document is reached or when
+ * {@link #close()} is called.
+ * <p>
+ * The main purpose of this class is to provide a convenient and efficient way to get the text
+ * content of an element without converting it first to a string, i.e. without using
+ * {@link org.apache.axiom.om.OMElement.OMElement#getText()}. This is important for potentially
+ * large contents, for which this class guarantees constant memory usage.
+ * <p>
+ * Note that this class should in general not be used directly. Instead,
+ * {@link ElementHelper#getTextAsStream(org.apache.axiom.om.OMElement)}
+ * should be called to get the most efficient stream implementation for a given an element.
+ * <p>
+ * NOTICE: The code in this class will be moved to Axiom (or somewhere else). Use with care!
+ */
+public class TextFromElementReader extends Reader {
+ private final XMLStreamReader stream;
+
+ /**
+ * Flag indicating that we have reached the end of the document and that the underlying
+ * parser has been closed.
+ */
+ private boolean endOfStream;
+
+ /**
+ * The current depth relative to the document element (not the document). A value greater than
+ * 0 indicates that we are inside a nested element and that we need to skip text nodes.
+ */
+ private int skipDepth;
+
+ /**
+ * The current position in the character data of the event, or -1 if all the character data
+ * has been consumed and a new event needs to be requested from the parser.
+ */
+ private int sourceStart = -1;
+
+ /**
+ * Constructor.
+ *
+ * @param stream the stream to extract the text nodes from
+ * @throws OMException if the stream doesn't start with the expected events
+ */
+ public TextFromElementReader(XMLStreamReader stream) {
+ this.stream = stream;
+ try {
+ if (stream.getEventType() != XMLStreamReader.START_DOCUMENT) {
+ throw new OMException("Expected START_DOCUMENT as first event from parser");
+ }
+ if (stream.next() != XMLStreamReader.START_ELEMENT) {
+ throw new OMException("Expected START_ELEMENT event");
+ }
+ } catch (XMLStreamException ex) {
+ throw new OMException(ex);
+ }
+ }
+
+ @Override
+ public int read(char[] cbuf, int off, int len) throws IOException {
+ if (endOfStream) {
+ return -1;
+ }
+ int read = 0;
+ try {
+ while (true) {
+ if (sourceStart == -1) {
+ eventLoop: while (true) {
+ int type = stream.next();
+ switch (type) {
+ case XMLStreamReader.CHARACTERS:
+ case XMLStreamReader.CDATA:
+ if (skipDepth == 0) {
+ sourceStart = 0;
+ break eventLoop;
+ }
+ break;
+ case XMLStreamReader.START_ELEMENT:
+ skipDepth++;
+ break;
+ case XMLStreamReader.END_ELEMENT:
+ if (skipDepth == 0) {
+ if (stream.next() == XMLStreamReader.END_DOCUMENT) {
+ endOfStream = true;
+ stream.close();
+ return read == 0 ? -1 : read;
+ } else {
+ throw new IOException(
+ "End of document expected after element");
+ }
+ } else {
+ skipDepth--;
+ }
+ }
+ }
+ }
+ int c = stream.getTextCharacters(sourceStart, cbuf, off, len);
+ sourceStart += c;
+ off += c;
+ len -= c;
+ read += c;
+ if (len > 0) {
+ sourceStart = -1;
+ } else {
+ return read;
+ }
+ }
+ } catch (XMLStreamException ex) {
+ IOException ex2 = new IOException("Got an exception from the underlying parser " +
+ "while reading the content of an element");
+ ex2.initCause(ex);
+ throw ex2;
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (!endOfStream) {
+ try {
+ stream.close();
+ } catch (XMLStreamException ex) {
+ IOException ex2 = new IOException("Error when trying to close underlying parser");
+ ex2.initCause(ex);
+ throw ex2;
+ }
+ }
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextMessageBuilder.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextMessageBuilder.java
new file mode 100644
index 0000000000..5c7623d7e2
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextMessageBuilder.java
@@ -0,0 +1,48 @@
+/*
+ * 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.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.Reader;
+
+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 a character stream.
+ * This interface can be optionally implemented by {@link Builder}
+ * implementations that support building a message from a character
+ * stream.
+ * <p>
+ * The character stream can either be provided as a string or a
+ * {@link Reader} object. The caller should use a {@link Reader} object
+ * except if the content of the message is available as a string anyway.
+ * <p>
+ * This interface is currently used by the JMS transport to process
+ * {@link javax.jms.TextMessage} instances.
+ */
+public interface TextMessageBuilder extends Builder {
+ public OMElement processDocument(Reader reader, String contentType,
+ MessageContext messageContext) throws AxisFault;
+
+ public OMElement processDocument(String content, String contentType,
+ MessageContext messageContext) throws AxisFault;
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextMessageBuilderAdapter.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextMessageBuilderAdapter.java
new file mode 100644
index 0000000000..692c4900b7
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/TextMessageBuilderAdapter.java
@@ -0,0 +1,78 @@
+/*
+ * 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.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+
+import javax.mail.internet.ContentType;
+import javax.mail.internet.ParseException;
+
+import org.apache.axiom.om.OMElement;
+import org.apache.axis2.AxisFault;
+import org.apache.axis2.Constants;
+import org.apache.axis2.builder.Builder;
+import org.apache.axis2.context.MessageContext;
+import org.apache.tuscany.sca.binding.ws.axis2.transport.base.streams.ReaderInputStream;
+
+/**
+ * Adapter to add the {@link TextMessageBuilder} interface to an
+ * existing {@link Builder}.
+ * It implements the {@link TextMessageBuilder#processDocument(Reader, String, MessageContext)}
+ * and {@link TextMessageBuilder#processDocument(String, String, MessageContext)} by converting
+ * the character stream to a byte stream using {@link ReaderInputStream}.
+ *
+ * TODO: specifying encoding
+ */
+public class TextMessageBuilderAdapter implements TextMessageBuilder {
+ private final Builder builder;
+
+ public TextMessageBuilderAdapter(Builder builder) {
+ this.builder = builder;
+ }
+
+ public OMElement processDocument(InputStream inputStream, String contentType,
+ MessageContext messageContext) throws AxisFault {
+ return builder.processDocument(inputStream, contentType, messageContext);
+ }
+
+ public OMElement processDocument(Reader reader, String contentType,
+ MessageContext messageContext) throws AxisFault {
+ String charset;
+ try {
+ ContentType ct = new ContentType(contentType);
+ charset = ct.getParameter("charset");
+ } catch (ParseException ex) {
+ charset = null;
+ }
+ if (charset == null) {
+ charset = MessageContext.DEFAULT_CHAR_SET_ENCODING;
+ }
+ messageContext.setProperty(Constants.Configuration.CHARACTER_SET_ENCODING, charset);
+ return processDocument(new ReaderInputStream(reader, charset), contentType,
+ messageContext);
+ }
+
+ public OMElement processDocument(String content, String contentType,
+ MessageContext messageContext) throws AxisFault {
+ return processDocument(new StringReader(content), contentType, messageContext);
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/WrappedTextNodeOMDataSourceFromDataSource.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/WrappedTextNodeOMDataSourceFromDataSource.java
new file mode 100644
index 0000000000..d8a5e65f59
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/WrappedTextNodeOMDataSourceFromDataSource.java
@@ -0,0 +1,114 @@
+/*
+ * 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.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.nio.charset.Charset;
+
+import javax.activation.DataSource;
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.apache.axiom.om.OMDataSourceExt;
+import org.apache.axiom.om.OMOutputFormat;
+import org.apache.axiom.om.ds.OMDataSourceExtBase;
+import org.apache.axiom.om.impl.MTOMXMLStreamWriter;
+import org.apache.axiom.om.impl.serialize.StreamingOMSerializer;
+import org.apache.axiom.om.util.StAXUtils;
+
+/**
+ * {@link org.apache.axiom.om.OMDataSource} implementation that represents a text node wrapped
+ * inside an element. The text data is provided by a {@link DataSource} object.
+ */
+public class WrappedTextNodeOMDataSourceFromDataSource extends OMDataSourceExtBase {
+ private final QName wrapperElementName;
+ private final DataSource binaryData;
+ private final Charset charset;
+
+ public WrappedTextNodeOMDataSourceFromDataSource(QName wrapperElementName, DataSource binaryData,
+ Charset charset) {
+ this.wrapperElementName = wrapperElementName;
+ this.binaryData = binaryData;
+ this.charset = charset;
+ }
+
+ @Override
+ public void serialize(OutputStream out, OMOutputFormat format) throws XMLStreamException {
+ XMLStreamWriter writer = new MTOMXMLStreamWriter(out, format);
+ serialize(writer);
+ writer.flush();
+ }
+
+ @Override
+ public void serialize(Writer writer, OMOutputFormat format) throws XMLStreamException {
+ MTOMXMLStreamWriter xmlWriter =
+ new MTOMXMLStreamWriter(StAXUtils.createXMLStreamWriter(writer));
+ xmlWriter.setOutputFormat(format);
+ serialize(xmlWriter);
+ xmlWriter.flush();
+ }
+
+ @Override
+ public void serialize(XMLStreamWriter xmlWriter) throws XMLStreamException {
+ StreamingOMSerializer serializer = new StreamingOMSerializer();
+ serializer.serialize(getReader(), xmlWriter);
+ }
+
+ public XMLStreamReader getReader() throws XMLStreamException {
+ InputStream is;
+ try {
+ is = binaryData.getInputStream();
+ }
+ catch (IOException ex) {
+ throw new XMLStreamException(ex);
+ }
+ return new WrappedTextNodeStreamReader(wrapperElementName, new InputStreamReader(is, charset));
+ }
+
+ public Object getObject() {
+ return binaryData;
+ }
+
+ public boolean isDestructiveRead() {
+ return false;
+ }
+
+ public boolean isDestructiveWrite() {
+ return false;
+ }
+
+ public byte[] getXMLBytes(String encoding) throws UnsupportedEncodingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void close() {
+ }
+
+ public OMDataSourceExt copy() {
+ return new WrappedTextNodeOMDataSourceFromDataSource(wrapperElementName, binaryData, charset);
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/WrappedTextNodeOMDataSourceFromReader.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/WrappedTextNodeOMDataSourceFromReader.java
new file mode 100644
index 0000000000..21c35f610c
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/WrappedTextNodeOMDataSourceFromReader.java
@@ -0,0 +1,99 @@
+/*
+ * 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.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.apache.axiom.om.OMDataSourceExt;
+import org.apache.axiom.om.OMOutputFormat;
+import org.apache.axiom.om.ds.OMDataSourceExtBase;
+import org.apache.axiom.om.impl.MTOMXMLStreamWriter;
+import org.apache.axiom.om.impl.serialize.StreamingOMSerializer;
+import org.apache.axiom.om.util.StAXUtils;
+
+/**
+ * {@link org.apache.axiom.om.OMDataSource} implementation that represents a text node wrapped
+ * inside an element. The text data is provided by a {@link Reader} object. Since the stream
+ * can only be read once, this data source is destructive.
+ */
+public class WrappedTextNodeOMDataSourceFromReader extends OMDataSourceExtBase {
+ private final QName wrapperElementName;
+ private final Reader reader;
+
+ public WrappedTextNodeOMDataSourceFromReader(QName wrapperElementName, Reader reader) {
+ this.wrapperElementName = wrapperElementName;
+ this.reader = reader;
+ }
+
+ @Override
+ public void serialize(Writer writer, OMOutputFormat format) throws XMLStreamException {
+ MTOMXMLStreamWriter xmlWriter =
+ new MTOMXMLStreamWriter(StAXUtils.createXMLStreamWriter(writer));
+ xmlWriter.setOutputFormat(format);
+ serialize(xmlWriter);
+ xmlWriter.flush();
+ }
+
+ @Override
+ public void serialize(XMLStreamWriter xmlWriter) throws XMLStreamException {
+ StreamingOMSerializer serializer = new StreamingOMSerializer();
+ serializer.serialize(getReader(), xmlWriter);
+ }
+
+ public XMLStreamReader getReader() throws XMLStreamException {
+ return new WrappedTextNodeStreamReader(wrapperElementName, reader);
+ }
+
+ public Object getObject() {
+ return null;
+ }
+
+ public boolean isDestructiveRead() {
+ return true;
+ }
+
+ public boolean isDestructiveWrite() {
+ return true;
+ }
+
+ public byte[] getXMLBytes(String encoding) throws UnsupportedEncodingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void close() {
+ try {
+ reader.close();
+ } catch (IOException ex) {
+ // Ignore
+ }
+ }
+
+ public OMDataSourceExt copy() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/WrappedTextNodeStreamReader.java b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/WrappedTextNodeStreamReader.java
new file mode 100644
index 0000000000..c656949b1f
--- /dev/null
+++ b/branches/sca-java-1.x/modules/binding-ws-axis2-jms/src/main/java/org/apache/tuscany/sca/binding/ws/axis2/format/WrappedTextNodeStreamReader.java
@@ -0,0 +1,437 @@
+/*
+ * 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.tuscany.sca.binding.ws.axis2.format;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Collections;
+
+import javax.xml.XMLConstants;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.namespace.QName;
+import javax.xml.stream.Location;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import org.apache.axiom.om.impl.EmptyOMLocation;
+import org.apache.axiom.om.impl.llom.util.NamespaceContextImpl;
+import org.apache.commons.io.IOUtils;
+
+/**
+ * {@link XMLInputStreamReader} implementation that
+ * represents a text node wrapped inside an element. The text data is provided by a
+ * {@link java.io.Reader Reader}.
+ * <p>
+ * It will produce the following sequence of XML events:
+ * <ul>
+ * <li>START_DOCUMENT</li>
+ * <li>START_ELEMENT</li>
+ * <li>(CHARACTER)*</li>
+ * <li>END_ELEMENT</li>
+ * <li>END_DOCMENT</li>
+ * </ul>
+ * The class is implemented as a simple state machine, where the state is identified
+ * by the current event type. The initial state is <tt>START_DOCUMENT</tt> and the
+ * following transitions are triggered by {@link #next()}:
+ * <ul>
+ * <li>START_DOCUMENT &rarr; START_ELEMENT</li>
+ * <li>START_ELEMENT &rarr; END_ELEMENT (if character stream is empty)</li>
+ * <li>START_ELEMENT &rarr; CHARACTERS (if character stream is not empty)</li>
+ * <li>CHARACTERS &rarr; CHARACTERS (if data available in stream)</li>
+ * <li>CHARACTERS &rarr; END_ELEMENT (if end of stream reached)</li>
+ * <li>END_ELEMENT &rarr; END_DOCUMENT</li>
+ * </ul>
+ * Additionally, {@link #getElementText()} triggers the following transition:
+ * <ul>
+ * <li>START_ELEMENT &rarr; END_ELEMENT</li>
+ * </ul>
+ * Note that since multiple consecutive CHARACTERS events may be returned, this
+ * "parser" is not coalescing.
+ *
+ */
+// TODO: This class has been copied from Synapse (package org.apache.synapse.util).
+// Once it has been moved to Axis2 or Axiom, remove the duplicate from Synapse.
+public class WrappedTextNodeStreamReader implements XMLStreamReader {
+ /**
+ * Location object returned by {@link #getLocation()}.
+ * It always returns -1 for the location and null for the publicId and systemId.
+ */
+ private final static Location EMPTY_LOCATION = new EmptyOMLocation();
+
+ /**
+ * The qualified name of the wrapper element.
+ */
+ private final QName wrapperElementName;
+
+ /**
+ * The Reader object that represents the text data.
+ */
+ private final Reader reader;
+
+ /**
+ * The maximum number of characters to return for each CHARACTER event.
+ */
+ private final int chunkSize;
+
+ /**
+ * The type of the current XML event.
+ */
+ private int eventType = START_DOCUMENT;
+
+ /**
+ * The character data for the current event. This is only set if the current
+ * event is a CHARACTER event. The size of the array is determined by
+ * {@link #chunkSize}
+ */
+ private char[] charData;
+
+ /**
+ * The length of the character data in {@link #charData}.
+ */
+ private int charDataLength;
+
+ /**
+ * The namespace context applicable in the scope of the wrapper element.
+ * Beside the default mappings for xml and xmlns, it only contains the
+ * mapping for the namespace of the wrapper element.
+ * This attribute is initialized lazily by {@link #getNamespaceContext()}.
+ */
+ private NamespaceContext namespaceContext;
+
+ /**
+ * Create a new instance.
+ *
+ * @param wrapperElementName the qualified name of the wrapper element
+ * @param reader the Reader object holding the character data to be wrapped
+ * @param chunkSize the maximum number of characters that are returned for each CHARACTER event
+ */
+ public WrappedTextNodeStreamReader(QName wrapperElementName, Reader reader, int chunkSize) {
+ this.wrapperElementName = wrapperElementName;
+ this.reader = reader;
+ this.chunkSize = chunkSize;
+ }
+
+ /**
+ * Create a new instance with chunk size 4096.
+ *
+ * @param wrapperElementName the qualified name of the wrapper element
+ * @param reader the Reader object holding the character data to be wrapped
+ */
+ public WrappedTextNodeStreamReader(QName wrapperElementName, Reader reader) {
+ this(wrapperElementName, reader, 4096);
+ }
+
+ public Object getProperty(String name) throws IllegalArgumentException {
+ // We don't define any properties
+ return null;
+ }
+
+ //
+ // Methods to manipulate the parser state
+ //
+
+ public boolean hasNext() throws XMLStreamException {
+ return eventType != END_DOCUMENT;
+ }
+
+ public int next() throws XMLStreamException {
+ // Determine next event type based on current event type. If current event type
+ // is START_ELEMENT or CHARACTERS, pull new data from the reader.
+ switch (eventType) {
+ case START_DOCUMENT:
+ eventType = START_ELEMENT;
+ break;
+ case START_ELEMENT:
+ charData = new char[chunkSize];
+ // No break here!
+ case CHARACTERS:
+ try {
+ charDataLength = reader.read(charData);
+ }
+ catch (IOException ex) {
+ throw new XMLStreamException(ex);
+ }
+ if (charDataLength == -1) {
+ charData = null;
+ eventType = END_ELEMENT;
+ } else {
+ eventType = CHARACTERS;
+ }
+ break;
+ case END_ELEMENT:
+ eventType = END_DOCUMENT;
+ break;
+ default:
+ throw new IllegalStateException();
+ }
+ return eventType;
+ }
+
+ public int nextTag() throws XMLStreamException {
+ // We don't have white space, comments or processing instructions
+ throw new XMLStreamException("Current event is not white space");
+ }
+
+ public int getEventType() {
+ return eventType;
+ }
+
+ public boolean isStartElement() { return eventType == START_ELEMENT; }
+ public boolean isEndElement() { return eventType == END_ELEMENT; }
+ public boolean isCharacters() { return eventType == CHARACTERS; }
+ public boolean isWhiteSpace() { return false; }
+ public boolean hasText() { return eventType == CHARACTERS; }
+ public boolean hasName() { return eventType == START_ELEMENT || eventType == END_ELEMENT; }
+
+ public void require(int type, String namespaceURI, String localName) throws XMLStreamException {
+ if (type != eventType
+ || (namespaceURI != null && !namespaceURI.equals(getNamespaceURI()))
+ || (localName != null && !namespaceURI.equals(getLocalName()))) {
+ throw new XMLStreamException("Unexpected event type");
+ }
+ }
+
+ public Location getLocation() {
+ // We do not support location information
+ return EMPTY_LOCATION;
+ }
+
+ public void close() throws XMLStreamException {
+ // Javadoc says that this method should not close the underlying input source,
+ // but we need to close the reader somewhere.
+ try {
+ reader.close();
+ }
+ catch (IOException ex) {
+ throw new XMLStreamException(ex);
+ }
+ }
+
+ //
+ // Methods related to the xml declaration.
+ //
+
+ public String getEncoding() {
+ // Encoding is not known (not relevant?)
+ return null;
+ }
+
+ public String getCharacterEncodingScheme() {
+ // Encoding is not known (not relevant?)
+ return null;
+ }
+
+ public String getVersion() {
+ // Version is not relevant
+ return null;
+ }
+
+ public boolean standaloneSet() {
+ return false;
+ }
+
+ public boolean isStandalone() {
+ return true;
+ }
+
+ //
+ // Methods related to the namespace context
+ //
+
+ public NamespaceContext getNamespaceContext() {
+ if (namespaceContext == null) {
+ namespaceContext = new NamespaceContextImpl(Collections.singletonMap(wrapperElementName.getPrefix(), wrapperElementName.getNamespaceURI()));
+ }
+ return namespaceContext;
+ }
+
+ public String getNamespaceURI(String prefix) {
+ String namespaceURI = getNamespaceContext().getNamespaceURI(prefix);
+ // NamespaceContext#getNamespaceURI and XMLStreamReader#getNamespaceURI have slightly
+ // different semantics for unbound prefixes.
+ return namespaceURI.equals(XMLConstants.NULL_NS_URI) ? null : prefix;
+ }
+
+ //
+ // Methods related to elements
+ //
+
+ private void checkStartElement() {
+ if (eventType != START_ELEMENT) {
+ throw new IllegalStateException();
+ }
+ }
+
+ public String getAttributeValue(String namespaceURI, String localName) {
+ checkStartElement();
+ return null;
+ }
+
+ public int getAttributeCount() {
+ checkStartElement();
+ return 0;
+ }
+
+ public QName getAttributeName(int index) {
+ checkStartElement();
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ public String getAttributeLocalName(int index) {
+ checkStartElement();
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ public String getAttributePrefix(int index) {
+ checkStartElement();
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ public String getAttributeNamespace(int index) {
+ checkStartElement();
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ public String getAttributeType(int index) {
+ checkStartElement();
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ public String getAttributeValue(int index) {
+ checkStartElement();
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ public boolean isAttributeSpecified(int index) {
+ checkStartElement();
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ private void checkElement() {
+ if (eventType != START_ELEMENT && eventType != END_ELEMENT) {
+ throw new IllegalStateException();
+ }
+ }
+
+ public QName getName() {
+ return null;
+ }
+
+ public String getLocalName() {
+ checkElement();
+ return wrapperElementName.getLocalPart();
+ }
+
+ public String getPrefix() {
+ return wrapperElementName.getPrefix();
+ }
+
+ public String getNamespaceURI() {
+ checkElement();
+ return wrapperElementName.getNamespaceURI();
+ }
+
+ public int getNamespaceCount() {
+ checkElement();
+ // There is one namespace declared on the wrapper element
+ return 1;
+ }
+
+ public String getNamespacePrefix(int index) {
+ checkElement();
+ if (index == 0) {
+ return wrapperElementName.getPrefix();
+ } else {
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
+ public String getNamespaceURI(int index) {
+ checkElement();
+ if (index == 0) {
+ return wrapperElementName.getNamespaceURI();
+ } else {
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
+ public String getElementText() throws XMLStreamException {
+ if (eventType == START_ELEMENT) {
+ // Actually the purpose of this class is to avoid storing
+ // the character data entirely in memory, but if the caller
+ // wants a String, we don't have the choice...
+ try {
+ String result = IOUtils.toString(reader);
+ eventType = END_ELEMENT;
+ return result;
+ }
+ catch (IOException ex) {
+ throw new XMLStreamException(ex);
+ }
+ } else {
+ throw new XMLStreamException("Current event is not a START_ELEMENT");
+ }
+ }
+
+ private void checkCharacters() {
+ if (eventType != CHARACTERS) {
+ throw new IllegalStateException();
+ }
+ }
+
+ public String getText() {
+ checkCharacters();
+ return new String(charData, 0, charDataLength);
+ }
+
+ public char[] getTextCharacters() {
+ checkCharacters();
+ return charData;
+ }
+
+ public int getTextStart() {
+ checkCharacters();
+ return 0;
+ }
+
+ public int getTextLength() {
+ checkCharacters();
+ return charDataLength;
+ }
+
+ public int getTextCharacters(int sourceStart, char[] target, int targetStart, int length) throws XMLStreamException {
+ checkCharacters();
+ int c = Math.min(charDataLength-sourceStart, length);
+ System.arraycopy(charData, sourceStart, target, targetStart, c);
+ return c;
+ }
+
+ //
+ // Methods related to processing instructions
+ //
+
+ public String getPIData() {
+ throw new IllegalStateException();
+ }
+
+ public String getPITarget() {
+ throw new IllegalStateException();
+ }
+}