You are viewing a plain text version of this content. The canonical link for it is here.
Posted to java-dev@axis.apache.org by di...@apache.org on 2005/09/20 16:07:40 UTC
svn commit: r290456 - in /webservices/axis/trunk/java/src/org/apache/axis:
attachments/ i18n/
Author: dims
Date: Tue Sep 20 07:07:35 2005
New Revision: 290456
URL: http://svn.apache.org/viewcvs?rev=290456&view=rev
Log:
Fix for AXIS-2221 - Attachment Streaming directly from HTTP Request
from Brian Husted
Added:
webservices/axis/trunk/java/src/org/apache/axis/attachments/DimeAttachmentStreams.java
webservices/axis/trunk/java/src/org/apache/axis/attachments/IncomingAttachmentStreams.java
webservices/axis/trunk/java/src/org/apache/axis/attachments/MultipartAttachmentStreams.java
Modified:
webservices/axis/trunk/java/src/org/apache/axis/attachments/Attachments.java
webservices/axis/trunk/java/src/org/apache/axis/attachments/AttachmentsImpl.java
webservices/axis/trunk/java/src/org/apache/axis/i18n/resource.properties
Modified: webservices/axis/trunk/java/src/org/apache/axis/attachments/Attachments.java
URL: http://svn.apache.org/viewcvs/webservices/axis/trunk/java/src/org/apache/axis/attachments/Attachments.java?rev=290456&r1=290455&r2=290456&view=diff
==============================================================================
--- webservices/axis/trunk/java/src/org/apache/axis/attachments/Attachments.java (original)
+++ webservices/axis/trunk/java/src/org/apache/axis/attachments/Attachments.java Tue Sep 20 07:07:35 2005
@@ -237,4 +237,14 @@
*/
public void dispose();
+
+
+
+ /**
+ * Once this method is called, attachments can only be accessed via the InputStreams.
+ * Any other access to the attachments collection (e.g. via getAttachments()) is
+ * prohibited and will cause a ConcurrentModificationException to be thrown.
+ * @return All of the attachment streams.
+ */
+ public IncomingAttachmentStreams getIncomingAttachmentStreams();
}
Modified: webservices/axis/trunk/java/src/org/apache/axis/attachments/AttachmentsImpl.java
URL: http://svn.apache.org/viewcvs/webservices/axis/trunk/java/src/org/apache/axis/attachments/AttachmentsImpl.java?rev=290456&r1=290455&r2=290456&view=diff
==============================================================================
--- webservices/axis/trunk/java/src/org/apache/axis/attachments/AttachmentsImpl.java (original)
+++ webservices/axis/trunk/java/src/org/apache/axis/attachments/AttachmentsImpl.java Tue Sep 20 07:07:35 2005
@@ -69,6 +69,14 @@
*/
private HashMap stackDataHandler = new HashMap();
+ /**
+ * Used to distribute attachment streams without caching them.
+ */
+ private IncomingAttachmentStreams _streams = null;
+
+ private boolean _askedForAttachments = false;
+ private boolean _askedForStreams = false;
+
/**
* Construct one of these on a parent Message.
* Should only ever be called by Message constructor!
@@ -131,6 +139,8 @@
soapPart = new org.apache.axis.SOAPPart(null,
mpartStream,
false);
+ MultiPartRelatedInputStream specificType = (MultiPartRelatedInputStream) mpartStream;
+ _streams = new MultipartAttachmentStreams(specificType.boundaryDelimitedStream, specificType.orderedParts);
} else if (token.equalsIgnoreCase(org.apache.axis.Message.MIME_APPLICATION_DIME)) {
try{
mpartStream=
@@ -138,6 +148,8 @@
soapPart = new org.apache.axis.SOAPPart(null, mpartStream, false);
}catch(Exception e){ throw org.apache.axis.AxisFault.makeFault(e);}
sendtype= SEND_TYPE_DIME;
+ MultiPartDimeInputStream specificType = (MultiPartDimeInputStream) mpartStream;
+ _streams = new DimeAttachmentStreams(specificType.dimeDelimitedStream);
} else if (token.indexOf(org.apache.axis.Message.CONTENT_TYPE_MTOM)!=-1){
sendtype = SEND_TYPE_MTOM;
}
@@ -179,6 +191,9 @@
*/
public Part removeAttachmentPart(String reference)
throws org.apache.axis.AxisFault {
+ if (_askedForStreams) {
+ throw new IllegalStateException(Messages.getMessage("concurrentModificationOfStream"));
+ }
multipart = null;
@@ -207,7 +222,9 @@
*/
public Part addAttachmentPart(Part newPart)
throws org.apache.axis.AxisFault {
-
+ if (_askedForStreams) {
+ throw new IllegalStateException(Messages.getMessage("concurrentModificationOfStream"));
+ }
multipart = null;
dimemultipart = null;
@@ -272,6 +289,9 @@
*/
public void setAttachmentParts(java.util.Collection parts)
throws org.apache.axis.AxisFault {
+ if (_askedForStreams) {
+ throw new IllegalStateException(Messages.getMessage("concurrentModificationOfStream"));
+ }
removeAllAttachments();
@@ -304,6 +324,9 @@
*/
public Part getAttachmentByReference(String reference)
throws org.apache.axis.AxisFault {
+ if (_askedForStreams) {
+ throw new IllegalStateException(Messages.getMessage("concurrentModificationOfStream"));
+ }
if (null == reference) {
return null;
@@ -360,6 +383,9 @@
*/
public java.util.Collection getAttachments()
throws org.apache.axis.AxisFault {
+ if (_askedForStreams) {
+ throw new IllegalStateException(Messages.getMessage("concurrentModificationOfStream"));
+ }
mergeinAttachments();
@@ -399,6 +425,9 @@
* @throws org.apache.axis.AxisFault
*/
public long getContentLength() throws org.apache.axis.AxisFault {
+ if (_askedForStreams) {
+ throw new IllegalStateException(Messages.getMessage("concurrentModificationOfStream"));
+ }
mergeinAttachments();
@@ -513,6 +542,9 @@
* @return the number of attachments
*/
public int getAttachmentCount() {
+ if (_askedForStreams) {
+ throw new IllegalStateException(Messages.getMessage("concurrentModificationOfStream"));
+ }
try {
mergeinAttachments();
@@ -548,6 +580,9 @@
* <P>This method does not touch the SOAP part.</P>
*/
public void removeAllAttachments() {
+ if (_askedForStreams) {
+ throw new IllegalStateException(Messages.getMessage("concurrentModificationOfStream"));
+ }
try {
multipart = null;
dimemultipart = null;
@@ -573,6 +608,9 @@
*/
public java.util.Iterator getAttachments(
javax.xml.soap.MimeHeaders headers) {
+ if (_askedForStreams) {
+ throw new IllegalStateException(Messages.getMessage("concurrentModificationOfStream"));
+ }
java.util.Vector vecParts = new java.util.Vector();
java.util.Iterator iterator = GetAttachmentsIterator();
while(iterator.hasNext()){
@@ -676,5 +714,21 @@
return "NONE";
}
return null;
+ }
+
+ /**
+ * Once this method is called, attachments can only be accessed via the InputStreams.
+ * Any other access to the attachments collection (e.g. via getAttachments()) is
+ * prohibited and will cause a IllegalStateException to be thrown.
+ *
+ * @return All of the attachment streams.
+ */
+ public IncomingAttachmentStreams getIncomingAttachmentStreams() {
+ if (_askedForAttachments) {
+ throw new IllegalStateException(Messages.getMessage("concurrentModificationOfStream"));
+ }
+ _askedForStreams = true;
+ mpartStream = null; // todo: comment
+ return _streams;
}
}
Added: webservices/axis/trunk/java/src/org/apache/axis/attachments/DimeAttachmentStreams.java
URL: http://svn.apache.org/viewcvs/webservices/axis/trunk/java/src/org/apache/axis/attachments/DimeAttachmentStreams.java?rev=290456&view=auto
==============================================================================
--- webservices/axis/trunk/java/src/org/apache/axis/attachments/DimeAttachmentStreams.java (added)
+++ webservices/axis/trunk/java/src/org/apache/axis/attachments/DimeAttachmentStreams.java Tue Sep 20 07:07:35 2005
@@ -0,0 +1,75 @@
+
+package org.apache.axis.attachments;
+
+import java.io.IOException;
+
+import org.apache.axis.AxisFault;
+import org.apache.axis.transport.http.HTTPConstants;
+import org.apache.axis.utils.Messages;
+
+/**
+ *
+ * This is the concrete implementation of the IncomingAttachmentStreams class
+ * and is used to parse data that is in the DIME format. This class will make
+ * use of Axis DimeDelimitedInputStream to parse the data in the HTTP stream
+ * which will give this class the capability of creating
+ * IncomingAttachmentInputStream objects at each marker within the HTTP stream.
+ *
+ * @author David Wong
+ * @author Brian Husted
+ *
+ */
+public final class DimeAttachmentStreams extends IncomingAttachmentStreams
+{
+ private DimeDelimitedInputStream _delimitedStream = null;
+
+ public DimeAttachmentStreams(DimeDelimitedInputStream stream)
+ throws AxisFault
+ {
+ if (stream == null)
+ {
+ throw new AxisFault(Messages.getMessage("nullDelimitedStream"));
+ }
+ _delimitedStream = stream;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.axis.attachments.IncomingAttachmentStreams#getNextStream()
+ */
+ public IncomingAttachmentInputStream getNextStream() throws AxisFault
+ {
+ IncomingAttachmentInputStream stream = null;
+
+ if (!isReadyToGetNextStream())
+ {
+ throw new IllegalStateException(Messages.getMessage("nextStreamNotReady"));
+ }
+ try
+ {
+ _delimitedStream = _delimitedStream.getNextStream();
+ if (_delimitedStream == null)
+ {
+ return null;
+ }
+ stream = new IncomingAttachmentInputStream(_delimitedStream);
+ }
+ catch (IOException e)
+ {
+ throw new AxisFault(Messages.getMessage("failedToGetDelimitedAttachmentStream"), e);
+ }
+
+ String value = _delimitedStream.getContentId();
+ if (value != null && value.length() > 0)
+ {
+ stream.addHeader(HTTPConstants.HEADER_CONTENT_ID, value);
+ }
+ value = _delimitedStream.getType();
+ if (value != null && value.length() > 0)
+ {
+ stream.addHeader(HTTPConstants.HEADER_CONTENT_TYPE, value);
+ }
+ setReadyToGetNextStream(false);
+ return stream;
+ }
+
+}
Added: webservices/axis/trunk/java/src/org/apache/axis/attachments/IncomingAttachmentStreams.java
URL: http://svn.apache.org/viewcvs/webservices/axis/trunk/java/src/org/apache/axis/attachments/IncomingAttachmentStreams.java?rev=290456&view=auto
==============================================================================
--- webservices/axis/trunk/java/src/org/apache/axis/attachments/IncomingAttachmentStreams.java (added)
+++ webservices/axis/trunk/java/src/org/apache/axis/attachments/IncomingAttachmentStreams.java Tue Sep 20 07:07:35 2005
@@ -0,0 +1,163 @@
+
+package org.apache.axis.attachments;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.axis.AxisFault;
+import org.apache.axis.transport.http.HTTPConstants;
+import org.apache.axis.utils.Messages;
+
+/**
+ * Similiar in concept to an iterator over the delimited streams inside
+ * of the HTTP stream. One difference between this class and a full fledge
+ * iterator is that the class is unable to tell if there are more streams until
+ * the last one has been fully read. It will however, return null when the end
+ * of the HTTP stream has been reached. Since the HTTP stream can contain data
+ * in different formats (e.g. DIME or SwA), the IncomingAttachmentStreams class
+ * will be an abstract class letting its derivatives handle the specifics to
+ * parsing out the HTTP stream. However, the class will implement methods that
+ * keep track of when each of the delimited streams are completely read. This is
+ * necessary since the next stream cannot be created until the previous stream
+ * has been fully read due to the fact that we are actually dealing with a
+ * single stream delimited by markers.
+ *
+ * @author David Wong
+ * @author Brian Husted
+ */
+public abstract class IncomingAttachmentStreams {
+ private boolean _readyToGetNextStream = true;
+
+ /**
+ * @return The next delimited stream or null if no additional streams are
+ * left.
+ */
+ public abstract IncomingAttachmentInputStream getNextStream()
+ throws AxisFault;
+
+ /**
+ * @return True if the next stream can be read, false otherwise.
+ */
+ public final boolean isReadyToGetNextStream() {
+ return _readyToGetNextStream;
+ }
+
+ /**
+ * Set the ready flag. Intended for the inner class to use.
+ *
+ * @param ready
+ */
+ protected final void setReadyToGetNextStream(boolean ready) {
+ _readyToGetNextStream = ready;
+ }
+
+ public final class IncomingAttachmentInputStream extends InputStream {
+ private HashMap _headers = null;
+
+ private InputStream _stream = null;
+
+ /**
+ * @param in
+ */
+ public IncomingAttachmentInputStream(InputStream in) {
+ _stream = in;
+ }
+
+ /**
+ * @return MIME headers for this attachment. May be null if no headers
+ * were set.
+ */
+ public Map getHeaders() {
+ return _headers;
+ }
+
+ /**
+ * Add a header.
+ *
+ * @param name
+ * @param value
+ */
+ public void addHeader(String name, String value) {
+ if (_headers == null) {
+ _headers = new HashMap();
+ }
+ _headers.put(name, value);
+ }
+
+ /**
+ * Get a header value.
+ *
+ * @param name
+ * @return The header found or null if not found.
+ */
+ public String getHeader(String name) {
+ Object header = null;
+ if (_headers == null || (header = _headers.get(name)) == null) {
+ return null;
+ }
+ return header.toString();
+ }
+
+ /**
+ * @return The header with HTTPConstants.HEADER_CONTENT_ID as the key.
+ */
+ public String getContentId() {
+ return getHeader(HTTPConstants.HEADER_CONTENT_ID);
+ }
+
+ /**
+ * @return The header with HTTPConstants.HEADER_CONTENT_LOCATION as the
+ * key.
+ */
+ public String getContentLocation() {
+ return getHeader(HTTPConstants.HEADER_CONTENT_LOCATION);
+ }
+
+ /**
+ * @return The header with HTTPConstants.HEADER_CONTENT_TYPE as the key.
+ */
+ public String getContentType() {
+ return getHeader(HTTPConstants.HEADER_CONTENT_TYPE);
+ }
+
+ /**
+ * Don't want to support mark and reset since this may get us into
+ * concurrency problem when different pieces of software may have a
+ * handle to the underlying InputStream.
+ */
+ public boolean markSupported() {
+ return false;
+ }
+
+ public void reset() throws IOException {
+ throw new IOException(Messages.getMessage("markNotSupported"));
+ }
+
+ public void mark(int readLimit) {
+ // do nothing
+ }
+
+ public int read() throws IOException {
+ int retval = _stream.read();
+ IncomingAttachmentStreams.this
+ .setReadyToGetNextStream(retval == -1);
+ return retval;
+ }
+
+ public int read(byte[] b) throws IOException {
+ int retval = _stream.read(b);
+ IncomingAttachmentStreams.this
+ .setReadyToGetNextStream(retval == -1);
+ return retval;
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException {
+ int retval = _stream.read(b, off, len);
+ IncomingAttachmentStreams.this
+ .setReadyToGetNextStream(retval == -1);
+ return retval;
+ }
+ }
+}
\ No newline at end of file
Added: webservices/axis/trunk/java/src/org/apache/axis/attachments/MultipartAttachmentStreams.java
URL: http://svn.apache.org/viewcvs/webservices/axis/trunk/java/src/org/apache/axis/attachments/MultipartAttachmentStreams.java?rev=290456&view=auto
==============================================================================
--- webservices/axis/trunk/java/src/org/apache/axis/attachments/MultipartAttachmentStreams.java (added)
+++ webservices/axis/trunk/java/src/org/apache/axis/attachments/MultipartAttachmentStreams.java Tue Sep 20 07:07:35 2005
@@ -0,0 +1,146 @@
+package org.apache.axis.attachments;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.StringTokenizer;
+
+import javax.mail.Header;
+import javax.mail.MessagingException;
+import javax.mail.internet.InternetHeaders;
+import javax.mail.internet.MimeUtility;
+import javax.xml.soap.SOAPException;
+
+import org.apache.axis.AxisFault;
+import org.apache.axis.transport.http.HTTPConstants;
+import org.apache.axis.utils.Messages;
+
+/**
+ * The MultipartAttachmentStreams class is used to create
+ * IncomingAttachmentInputStream objects when the HTTP stream shows a marked
+ * separation between the SOAP and each attachment parts. Unlike the DIME
+ * version, this class will use the BoundaryDelimitedStream to parse data in the
+ * SwA format. Another difference between the two is that the
+ * MultipartAttachmentStreams class must also provide a way to hold attachment
+ * parts parsed prior to where the SOAP part appears in the HTTP stream (i.e.
+ * the root part of the multipart-related message). Our DIME counterpart didnt
+ * have to worry about this since the SOAP part is guaranteed to be the first in
+ * the stream. But since SwA has no such guarantee, we must fall back to caching
+ * these first parts. Afterwards, we can stream the rest of the attachments that
+ * are after the SOAP part of the request message.
+ *
+ * @author David Wong
+ * @author Brian Husted
+ *
+ */
+public final class MultipartAttachmentStreams extends IncomingAttachmentStreams {
+ private BoundaryDelimitedStream _delimitedStream = null;
+
+ private Iterator _attachmentParts = null;
+
+ public MultipartAttachmentStreams(BoundaryDelimitedStream delimitedStream)
+ throws AxisFault {
+ this(delimitedStream, null);
+ }
+
+ public MultipartAttachmentStreams(BoundaryDelimitedStream delimitedStream,
+ Collection priorParts) throws AxisFault {
+ if (delimitedStream == null) {
+ throw new AxisFault(Messages.getMessage("nullDelimitedStream"));
+ }
+ _delimitedStream = delimitedStream;
+ if (priorParts != null) {
+ setAttachmentsPriorToSoapPart(priorParts.iterator());
+ }
+ }
+
+ public void setAttachmentsPriorToSoapPart(Iterator iterator) {
+ _attachmentParts = iterator;
+ }
+
+ /**
+ *
+ * @see org.apache.axis.attachments.IncomingAttachmentStreams#getNextStream()
+ */
+ public IncomingAttachmentInputStream getNextStream() throws AxisFault {
+ IncomingAttachmentInputStream stream = null;
+ if (!isReadyToGetNextStream()) {
+ throw new IllegalStateException(Messages
+ .getMessage("nextStreamNotReady"));
+ }
+ if (_attachmentParts != null && _attachmentParts.hasNext()) {
+ AttachmentPart part = (AttachmentPart) _attachmentParts.next();
+
+ try {
+ stream = new IncomingAttachmentInputStream(part
+ .getDataHandler().getInputStream());
+ } catch (IOException e) {
+ throw new AxisFault(Messages
+ .getMessage("failedToGetAttachmentPartStream"), e);
+ } catch (SOAPException e) {
+ throw new AxisFault(Messages
+ .getMessage("failedToGetAttachmentPartStream"), e);
+ }
+ stream.addHeader(HTTPConstants.HEADER_CONTENT_ID, part
+ .getContentId());
+ stream.addHeader(HTTPConstants.HEADER_CONTENT_LOCATION, part
+ .getContentLocation());
+ stream.addHeader(HTTPConstants.HEADER_CONTENT_TYPE, part
+ .getContentType());
+ } else {
+ InternetHeaders headers = null;
+
+ try {
+ _delimitedStream = _delimitedStream.getNextStream();
+ if (_delimitedStream == null) {
+ return null;
+ }
+ headers = new InternetHeaders(_delimitedStream);
+ String delimiter = null; // null for the first header
+ String encoding = headers.getHeader(
+ HTTPConstants.HEADER_CONTENT_TRANSFER_ENCODING,
+ delimiter);
+ if (encoding != null && encoding.length() > 0) {
+ encoding = encoding.trim();
+ stream = new IncomingAttachmentInputStream(MimeUtility
+ .decode(_delimitedStream, encoding));
+ stream.addHeader(
+ HTTPConstants.HEADER_CONTENT_TRANSFER_ENCODING,
+ encoding);
+ } else {
+ stream = new IncomingAttachmentInputStream(_delimitedStream);
+ }
+ } catch (IOException e) {
+ throw new AxisFault(Messages
+ .getMessage("failedToGetDelimitedAttachmentStream"), e);
+ } catch (MessagingException e) {
+ throw new AxisFault(Messages
+ .getMessage("failedToGetDelimitedAttachmentStream"), e);
+ }
+ Header header = null;
+ Enumeration enum = headers.getAllHeaders();
+ String name = null;
+ String value = null;
+ while (enum != null && enum.hasMoreElements()) {
+ header = (Header) enum.nextElement();
+ name = header.getName();
+ value = header.getValue();
+ if (HTTPConstants.HEADER_CONTENT_ID.equals(name)
+ || HTTPConstants.HEADER_CONTENT_TYPE.equals(name)
+ || HTTPConstants.HEADER_CONTENT_LOCATION.equals(name)) {
+ value = value.trim();
+ if ((HTTPConstants.HEADER_CONTENT_ID.equals(name) || HTTPConstants.HEADER_CONTENT_LOCATION
+ .equals(name))
+ && (name.indexOf('>') > 0 || name.indexOf('<') > 0)) {
+ value = new StringTokenizer(value, "<>").nextToken();
+ }
+ }
+ stream.addHeader(name, value);
+ }
+ }
+ setReadyToGetNextStream(false);
+ return stream;
+ }
+
+}
\ No newline at end of file
Modified: webservices/axis/trunk/java/src/org/apache/axis/i18n/resource.properties
URL: http://svn.apache.org/viewcvs/webservices/axis/trunk/java/src/org/apache/axis/i18n/resource.properties?rev=290456&r1=290455&r2=290456&view=diff
==============================================================================
--- webservices/axis/trunk/java/src/org/apache/axis/i18n/resource.properties (original)
+++ webservices/axis/trunk/java/src/org/apache/axis/i18n/resource.properties Tue Sep 20 07:07:35 2005
@@ -1078,6 +1078,12 @@
cannotDoWrappedMode00=Warning: Element {0} has no type declaration, hence it is not a wrapper element. Switching off wrapped mode.
+nextStreamNotReady=Asked for the next delimited stream before the previous delimited stream is fully read
+failedToGetAttachmentPartStream=Exception occured when asking AttachmentPart.DataHandler for InputStream.
+nullDelimitedStream=The delimited stream used to initialize an IncomingAttachmentStreams object cannot be null.
+failedToGetDelimitedAttachmentStream=Exception occured when asking the delimited stream for the next stream.
+markNotSupported=Mark and reset features are not supported by this InputStream.
+concurrentModificationOfStream=The attachments stream can only be accessed once; either by using the IncomingAttachmentStreams class or by getting a collection of AttachmentPart objects. They cannot both be called within the life time of the same service request.
# #
# In-use keys #
######################################################################