You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commons-dev@ws.apache.org by ve...@apache.org on 2009/01/01 20:24:53 UTC

svn commit: r730594 - in /webservices/commons/trunk/modules/axiom/modules/axiom-api/src: main/java/org/apache/axiom/attachments/ main/java/org/apache/axiom/attachments/impl/ test/java/org/apache/axiom/attachments/impl/

Author: veithen
Date: Thu Jan  1 11:24:52 2009
New Revision: 730594

URL: http://svn.apache.org/viewvc?rev=730594&view=rev
Log:
WSCOMMONS-424: Replaced BufferUtils#doesDataHandlerExceedLimit with a better implementation (see JIRA issue for more details).

Added:
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/SizeAwareDataSource.java
Modified:
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/ByteArrayDataSource.java
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/impl/BufferUtils.java
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/attachments/impl/BufferUtilsTest.java

Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/ByteArrayDataSource.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/ByteArrayDataSource.java?rev=730594&r1=730593&r2=730594&view=diff
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/ByteArrayDataSource.java (original)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/ByteArrayDataSource.java Thu Jan  1 11:24:52 2009
@@ -19,13 +19,12 @@
 
 package org.apache.axiom.attachments;
 
-import javax.activation.DataSource;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 
-public class ByteArrayDataSource implements DataSource {
+public class ByteArrayDataSource implements SizeAwareDataSource {
 
     private byte[] data;
 
@@ -65,5 +64,9 @@
     public OutputStream getOutputStream() throws IOException {
         throw new IOException("Not Supported");
     }
+
+    public long getSize() {
+        return data == null ? 0 : data.length;
+    }
 }
 

Added: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/SizeAwareDataSource.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/SizeAwareDataSource.java?rev=730594&view=auto
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/SizeAwareDataSource.java (added)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/SizeAwareDataSource.java Thu Jan  1 11:24:52 2009
@@ -0,0 +1,41 @@
+/*
+ * 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.axiom.attachments;
+
+import javax.activation.DataSource;
+
+/**
+ * Optional extension interface that can be implemented by data sources that support a
+ * getSize method.
+ * Code working with data sources can use this interface to optimize certain operations.
+ * An example is
+ * {@link org.apache.axiom.attachments.impl.BufferUtils#doesDataHandlerExceedLimit(javax.activation.DataHandler, int)}.
+ */
+public interface SizeAwareDataSource extends DataSource {
+    /**
+     * Get the size of the data source.
+     * Implementations must return the number of bytes that can be read from
+     * the input stream returned by {@link #getInputStream()} before reaching
+     * the end of the stream.
+     * 
+     * @return the size of the data source
+     */
+    long getSize();
+}

Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/impl/BufferUtils.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/impl/BufferUtils.java?rev=730594&r1=730593&r2=730594&view=diff
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/impl/BufferUtils.java (original)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/impl/BufferUtils.java Thu Jan  1 11:24:52 2009
@@ -18,6 +18,7 @@
  */
 package org.apache.axiom.attachments.impl;
 
+import java.io.ByteArrayInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -30,8 +31,8 @@
 import javax.activation.DataSource;
 import javax.activation.FileDataSource;
 
+import org.apache.axiom.attachments.SizeAwareDataSource;
 import org.apache.axiom.attachments.utils.BAAOutputStream;
-import org.apache.axiom.om.OMException;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
@@ -216,8 +217,48 @@
         return baaos.receive(is, limit);
     }
     
+    /**
+     * Exception used by SizeLimitedOutputStream if the size limit has been exceeded.
+     */
+    private static class SizeLimitExceededException extends IOException {
+        private static final long serialVersionUID = -6644887187061182165L;
+    }
     
     /**
+     * An output stream that counts the number of bytes written to it and throws an
+     * exception when the size exceeds a given limit.
+     */
+    private static class SizeLimitedOutputStream extends OutputStream {
+        private final int maxSize;
+        private int size;
+        
+        public SizeLimitedOutputStream(int maxSize) {
+            this.maxSize = maxSize;
+        }
+
+        public void write(byte[] b, int off, int len) throws IOException {
+            size += len;
+            checkSize();
+        }
+
+        public void write(byte[] b) throws IOException {
+            size += b.length;
+            checkSize();
+        }
+
+        public void write(int b) throws IOException {
+            size++;
+            checkSize();
+        }
+        
+        private void checkSize() throws SizeLimitExceededException {
+            if (size > maxSize) {
+                throw new SizeLimitExceededException();
+            }
+        }
+    }
+
+    /**
      * The method checks to see if attachment is eligble for optimization.
      * An attachment is eligible for optimization if and only if the size of 
      * the attachment is greated then the optimzation threshold size limit. 
@@ -232,80 +273,46 @@
      * @throws IOException
      */
     public static int doesDataHandlerExceedLimit(DataHandler dh, int limit){
-        if(log.isDebugEnabled()){
-            log.debug("start isEligibleForOptimization");
-        }
         //If Optimized Threshold not set return true.
         if(limit==0){
-            if(log.isDebugEnabled()){
-                log.debug("optimizedThreshold not set");
-            }
             return -1;
         }
-        
-        InputStream in=null;
-        //read bytes from input stream to check if 
-        //attachment size is greater than the optimized size.        
-        int totalRead = 0;
-        try{
-            in = getInputStream(dh);
-            if(in.markSupported()){
-                in.mark(limit);
-            }
-            if(in == null){
-                if(log.isDebugEnabled()){
-                    log.debug("Input Stream is null");
-                }
+        DataSource ds = dh.getDataSource();
+        if (ds instanceof SizeAwareDataSource) {
+            return ((SizeAwareDataSource)ds).getSize() > limit ? 1 : 0;
+        } else if (ds instanceof javax.mail.util.ByteArrayDataSource) {
+            // Special optimization for JavaMail's ByteArrayDataSource (Axiom's ByteArrayDataSource
+            // already implements SizeAwareDataSource and doesn't need further optimization):
+            // we know that ByteArrayInputStream#available() directly returns the size of the
+            // data source.
+            try {
+                return ((ByteArrayInputStream)ds.getInputStream()).available() > limit ? 1 : 0;
+            } catch (IOException ex) {
+                // We will never get here...
                 return -1;
             }
-            do{
-                byte[] buffer = getTempBuffer();
-                int bytesRead = in.read(buffer, 0, BUFFER_LEN);
-                totalRead = totalRead+bytesRead;
-                releaseTempBuffer(buffer);
-            }while((limit>totalRead) && (in.available()>0));
-            
-            if(in.markSupported()){                
-                in.reset();
-            }
-            if(totalRead > limit){
-                if(log.isDebugEnabled()){
-                    log.debug("Attachment size greater than limit");
-                }
+        } else if (ds instanceof FileDataSource) {
+            // Special optimization for FileDataSources: no need to open and read the file
+            // to know its size!
+            return ((FileDataSource)ds).getFile().length() > limit ? 1 : 0;
+        } else {
+            // In all other cases, we prefer DataHandler#writeTo over DataSource#getInputStream.
+            // The reason is that if the DataHandler was constructed from an Object rather than
+            // a DataSource, a call to DataSource#getInputStream() will start a new thread and
+            // return a PipedInputStream. This is so for Geronimo's as well as Sun's JAF
+            // implementaion. The reason is that DataContentHandler only has a writeTo and no
+            // getInputStream method. Obviously starting a new thread just to check the size of
+            // the data is an overhead that we should avoid.
+            try {
+                dh.writeTo(new SizeLimitedOutputStream(limit));
+            } catch (SizeLimitExceededException ex) {
                 return 1;
+            } catch (IOException ex) {
+                log.warn(ex.getMessage());
+                return -1;
             }
-        }catch(Exception e){            
-            log.warn(e.getMessage());
-            return -1;
-        }
-        
-        if(log.isDebugEnabled()){
-            log.debug("Attachment Size smaller than limit");
-        }
-        
-        return 0;        
-    }
-    
-    private static java.io.InputStream getInputStream(DataHandler dataHandlerObject) throws OMException {
-        InputStream inStream = null;
-        javax.activation.DataHandler dataHandler =
-            (javax.activation.DataHandler) dataHandlerObject;
-        try {
-            DataSource ds = dataHandler.getDataSource();
-            if(ds instanceof FileDataSource){
-                inStream = ds.getInputStream();
-            }else{
-                inStream = dataHandler.getDataSource().getInputStream();
-                if(!inStream.markSupported()){
-                    throw new OMException("Stream does not support mark, Cannot read the stream as DataSource will be consumed.");
-                }
-            }
-        } catch (IOException e) {
-            throw new OMException(
-                "Cannot get InputStream from DataHandler." + e);
+            return 0;
         }
-        return inStream;
-
     }
     
     private static synchronized byte[] getTempBuffer() {

Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/attachments/impl/BufferUtilsTest.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/attachments/impl/BufferUtilsTest.java?rev=730594&r1=730593&r2=730594&view=diff
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/attachments/impl/BufferUtilsTest.java (original)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/test/java/org/apache/axiom/attachments/impl/BufferUtilsTest.java Thu Jan  1 11:24:52 2009
@@ -110,8 +110,9 @@
             int unsupported= BufferUtils.doesDataHandlerExceedLimit(dh, 0);
             assertEquals(-1, unsupported);
             int doesExceed = BufferUtils.doesDataHandlerExceedLimit(dh, 10);
-            //Expecting Mark NotSupported
-            assertEquals(-1, doesExceed);
+            assertEquals(1, doesExceed);
+            int doesNotExceed = BufferUtils.doesDataHandlerExceedLimit(dh, 100);
+            assertEquals(0, doesNotExceed);
         }catch(Exception e){
             e.printStackTrace();
             fail();