[jboss-svn-commits] JBoss Common SVN: r3934 - in shrinkwrap/trunk: impl-base/src/main/java/org/jboss/shrinkwrap/impl/base and 3 other directories.

jboss-svn-commits at lists.jboss.org jboss-svn-commits at lists.jboss.org
Tue Jan 19 16:10:37 EST 2010


Author: ALRubinger
Date: 2010-01-19 16:10:36 -0500 (Tue, 19 Jan 2010)
New Revision: 3934

Added:
   shrinkwrap/trunk/api/src/main/java/org/jboss/shrinkwrap/api/exporter/ZipExportHandle.java
   shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/IsReadReportingInputStream.java
   shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/ZipExportHandleImpl.java
Modified:
   shrinkwrap/trunk/api/src/main/java/org/jboss/shrinkwrap/api/exporter/ZipExporter.java
   shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/ArchiveInputStreamFactory.java
   shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/ZipArchiveInputStreamFactory.java
   shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/ZipExportDelegate.java
   shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/ZipExporterImpl.java
   shrinkwrap/trunk/impl-base/src/test/java/org/jboss/shrinkwrap/impl/base/exporter/ZipExporterTestCase.java
   shrinkwrap/trunk/impl-base/src/test/java/org/jboss/shrinkwrap/impl/base/importer/ZipImporterImplTestCase.java
Log:
[SHRINKWRAP-116] Avoid OOM and encode as ZIP incrementally as the caller reads

Added: shrinkwrap/trunk/api/src/main/java/org/jboss/shrinkwrap/api/exporter/ZipExportHandle.java
===================================================================
--- shrinkwrap/trunk/api/src/main/java/org/jboss/shrinkwrap/api/exporter/ZipExportHandle.java	                        (rev 0)
+++ shrinkwrap/trunk/api/src/main/java/org/jboss/shrinkwrap/api/exporter/ZipExportHandle.java	2010-01-19 21:10:36 UTC (rev 3934)
@@ -0,0 +1,56 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2010, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+  *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.shrinkwrap.api.exporter;
+
+import java.io.InputStream;
+
+/**
+ * Handle returned to callers from a request to export via
+ * the {@link ZipExporter}.  As the encoding process is an asynchronous
+ * operation, here we provide the user access to read the 
+ * content as well as check for completeness and integrity.
+ *
+ * @author <a href="mailto:andrew.rubinger at jboss.org">ALR</a>
+ */
+public interface ZipExportHandle
+{
+   /**
+    * Obtains an {@link InputStream} from which the encoded
+    * content may be read.
+    * 
+    * @return
+    */
+   InputStream getContent();
+
+   /**
+    * Blocking operation which will wait until the encoding process's internal
+    * streams have been closed and verified for integrity.  Do not call this method
+    * until all bytes have been read from {@link ZipExportHandle#getContent()}; otherwise 
+    * this may introduce a deadlock.  Any problems with the encoding process will be reported
+    * by throwing {@link ArchiveExportException}.
+    * @return
+    * @throws ArchiveExportException If an error occurred during export
+    * @throws IllegalStateException If invoked before {@link ZipExportHandle#getContent()} has been
+    * fully-read
+    */
+   void checkComplete() throws ArchiveExportException, IllegalStateException;
+}

Modified: shrinkwrap/trunk/api/src/main/java/org/jboss/shrinkwrap/api/exporter/ZipExporter.java
===================================================================
--- shrinkwrap/trunk/api/src/main/java/org/jboss/shrinkwrap/api/exporter/ZipExporter.java	2010-01-19 17:32:38 UTC (rev 3933)
+++ shrinkwrap/trunk/api/src/main/java/org/jboss/shrinkwrap/api/exporter/ZipExporter.java	2010-01-19 21:10:36 UTC (rev 3934)
@@ -22,7 +22,7 @@
 import org.jboss.shrinkwrap.api.Assignable;
 
 /**
- * Exporter used to represent an Archive as a ZIP format. 
+ * Exporter used to represent an {@link Assignable} in ZIP format. 
  * 
  * @see http://www.pkware.com/documents/casestudies/APPNOTE.TXT
  * @author <a href="mailto:baileyje at gmail.com">John Bailey</a>
@@ -37,13 +37,11 @@
    //-------------------------------------------------------------------------------------||
 
    /**
-    * Exports provided archive as a Zip archive.
+    * Exports this reference as a Zip archive.
     * 
-    * @param archive
     * @return {@link InputStream} for exported Zip
-    * @throws ArchiveExportException if the export process fails
     */
-   InputStream exportZip() throws ArchiveExportException;
+   ZipExportHandle exportZip();
 
    /**
     * Exports provided archive as a ZIP archive, written to the 

Modified: shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/ArchiveInputStreamFactory.java
===================================================================
--- shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/ArchiveInputStreamFactory.java	2010-01-19 17:32:38 UTC (rev 3933)
+++ shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/ArchiveInputStreamFactory.java	2010-01-19 21:10:36 UTC (rev 3934)
@@ -49,7 +49,7 @@
     * 
     * @throws IllegalArgumentException if the Archive is null 
     */
-   public static InputStream getInputStream(Archive<?> archive)
+   public static InputStream getInputStream(final Archive<?> archive)
    {
       Validate.notNull(archive, "archive was not provided");
 

Added: shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/IsReadReportingInputStream.java
===================================================================
--- shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/IsReadReportingInputStream.java	                        (rev 0)
+++ shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/IsReadReportingInputStream.java	2010-01-19 21:10:36 UTC (rev 3934)
@@ -0,0 +1,116 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.shrinkwrap.impl.base.exporter;
+
+import java.io.IOException;
+import java.io.PipedInputStream;
+
+/**
+ * {@link PipedInputStream} which may report whether or not
+ * it has been fully read.
+ *
+ * @author <a href="mailto:andrew.rubinger at jboss.org">ALR</a>
+ */
+class IsReadReportingInputStream extends PipedInputStream
+{
+
+   //-------------------------------------------------------------------------------------||
+   // Class Members ----------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Number of bytes read signaling the end has been reached
+    */
+   private static final int EOF = -1;
+
+   //-------------------------------------------------------------------------------------||
+   // Instance Members -------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Flag showing whether or not we've been fully-read
+    */
+   private boolean isRead;
+
+   //-------------------------------------------------------------------------------------||
+   // Constructor ------------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Creates a new Stream
+    */
+   public IsReadReportingInputStream()
+   {
+      super();
+   }
+
+   /**
+    * {@inheritDoc}
+    * @see java.io.PipedInputStream#read()
+    */
+   @Override
+   public synchronized int read() throws IOException
+   {
+      final int bytesRead = super.read();
+      this.markReadOnStreamEnd(bytesRead);
+      return bytesRead;
+   }
+
+   /**
+    * {@inheritDoc}
+    * @see java.io.PipedInputStream#read(byte[], int, int)
+    */
+   @Override
+   public synchronized int read(byte[] b, int off, int len) throws IOException
+   {
+      final int bytesRead = super.read(b, off, len);
+      this.markReadOnStreamEnd(bytesRead);
+      return bytesRead;
+   }
+
+   //-------------------------------------------------------------------------------------||
+   // Internal Helper Methods ------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Marks this stream as read
+    * if the number of bytes specified is equal to {@link IsReadReportingInputStream#EOF} 
+    */
+   private void markReadOnStreamEnd(final int bytesRead)
+   {
+      if (bytesRead == EOF)
+      {
+         try
+         {
+            isRead = true;
+         }
+         catch (final Exception e)
+         {
+            throw new RuntimeException("Encountered exception in callback", e);
+         }
+      }
+   }
+
+   /**
+    * Returns whether or not this stream has been fully read
+    * @return
+    */
+   boolean isRead()
+   {
+      return isRead;
+   }
+}

Modified: shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/ZipArchiveInputStreamFactory.java
===================================================================
--- shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/ZipArchiveInputStreamFactory.java	2010-01-19 17:32:38 UTC (rev 3933)
+++ shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/ZipArchiveInputStreamFactory.java	2010-01-19 21:10:36 UTC (rev 3934)
@@ -36,7 +36,7 @@
    //-------------------------------------------------------------------------------------||
    // Required Implementations - ArchiveInputStreamFactory  ------------------------------||
    //-------------------------------------------------------------------------------------||
-   
+
    /**
     * {@inheritDoc}
     * @see org.jboss.shrinkwrap.impl.base.ArchiveInputStreamFactory#doGetInputStream(Archive)
@@ -45,7 +45,7 @@
    protected InputStream doGetInputStream(Archive<?> archive)
    {
       // Get InputStream from the ZipExporter
-      final InputStream inputStream = archive.as(ZipExporter.class).exportZip();
+      final InputStream inputStream = archive.as(ZipExporter.class).exportZip().getContent();
       // Return input stream
       return inputStream;
    }

Modified: shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/ZipExportDelegate.java
===================================================================
--- shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/ZipExportDelegate.java	2010-01-19 17:32:38 UTC (rev 3933)
+++ shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/ZipExportDelegate.java	2010-01-19 21:10:36 UTC (rev 3934)
@@ -16,11 +16,16 @@
  */
 package org.jboss.shrinkwrap.impl.base.exporter;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PipedOutputStream;
 import java.util.HashSet;
 import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.zip.ZipEntry;
@@ -28,16 +33,17 @@
 import java.util.zip.ZipOutputStream;
 
 import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.ArchivePath;
 import org.jboss.shrinkwrap.api.Asset;
-import org.jboss.shrinkwrap.api.ArchivePath;
 import org.jboss.shrinkwrap.api.exporter.ArchiveExportException;
+import org.jboss.shrinkwrap.api.exporter.ZipExportHandle;
 import org.jboss.shrinkwrap.impl.base.asset.DirectoryAsset;
 import org.jboss.shrinkwrap.impl.base.io.IOUtil;
 import org.jboss.shrinkwrap.impl.base.io.StreamErrorHandler;
 import org.jboss.shrinkwrap.impl.base.io.StreamTask;
 import org.jboss.shrinkwrap.impl.base.path.PathUtil;
 
-public class ZipExportDelegate extends AbstractExporterDelegate<InputStream>
+public class ZipExportDelegate extends AbstractExporterDelegate<ZipExportHandle>
 {
    //-------------------------------------------------------------------------------------||
    // Class Members ----------------------------------------------------------------------||
@@ -48,19 +54,28 @@
     */
    private static final Logger log = Logger.getLogger(ZipExportDelegate.class.getName());
 
+   /**
+    * Services used to submit new jobs (encoding occurs in a separate Thread)
+    */
+   private static final ExecutorService service;
+   static
+   {
+      service = Executors.newCachedThreadPool();
+   }
+
    //-------------------------------------------------------------------------------------||
    // Instance Members -------------------------------------------------------------------||
    //-------------------------------------------------------------------------------------||
 
    /**
-    * OutputStream to hold the output contents
+    * ZipOutputStream used to write the zip entries
     */
-   private final ByteArrayOutputStream output = new ByteArrayOutputStream(8192);
+   private ZipOutputStream zipOutputStream;
 
    /**
-    * ZipOutputStream used to write the zip entries
+    * Handle to be returned to the caller
     */
-   private ZipOutputStream zipOutputStream;
+   private ZipExportHandle handle;
 
    /**
     * A Set of Paths we've exported so far (so that we don't write
@@ -88,30 +103,61 @@
     * {@inheritDoc}
     * @see org.jboss.shrinkwrap.impl.base.exporter.AbstractExporterDelegate#export()
     */
-   @Override
    protected void export()
    {
-      zipOutputStream = new ZipOutputStream(output);
-      // Enclose every IO Operation so we can close up cleanly
-      IOUtil.closeOnComplete(zipOutputStream, new StreamTask<ZipOutputStream>()
+
+      // Define the task to operate in another Thread so we can pipe the output to an InStream
+      final Callable<Void> exportTask = new Callable<Void>()
       {
 
          @Override
-         public void execute(ZipOutputStream stream) throws Exception
+         public Void call() throws Exception
          {
-            ZipExportDelegate.super.export();
+            try
+            {
+               ZipExportDelegate.super.export();
+            }
+            finally
+            {
+               try
+               {
+                  zipOutputStream.close();
+               }
+               catch (final IOException ioe)
+               {
+                  // Ignore
+                  log.warning("Got exception on closing the ZIP out stream: " + ioe.getMessage());
+               }
+            }
+
+            return null;
          }
+      };
 
-      }, new StreamErrorHandler()
+      // Stream to return to the caller
+      final IsReadReportingInputStream input = new IsReadReportingInputStream();
+
+      /**
+       * OutputStream which will be associated with the returned InStream, and the 
+       * chained IO point for the Zip OutStrea,
+       */
+      final OutputStream output;
+      try
       {
+         output = new PipedOutputStream(input);
+      }
+      catch (final IOException e)
+      {
+         throw new RuntimeException("Error in setting up output stream", e);
+      }
 
-         @Override
-         public void handle(Throwable t)
-         {
-            throw new ArchiveExportException("Failed to export Zip: " + getArchive().getName(), t);
-         }
+      // Set up the stream to which we'll write entries, backed by the piped stream
+      zipOutputStream = new ZipOutputStream(output);
 
-      });
+      // Get a handle and return it to the caller
+      final Future<Void> job = service.submit(exportTask);
+      final ZipExportHandle handle = new ZipExportHandleImpl(input, job);
+      this.handle = handle;
    }
 
    /**
@@ -127,11 +173,11 @@
          throw new IllegalArgumentException("Path must be specified");
       }
 
-      if(isParentOfAnyPathsExported(path))
+      if (isParentOfAnyPathsExported(path))
       {
          return;
       }
-      
+
       /*
        * SHRINKWRAP-94
        * Add entries for all parents of this Path
@@ -179,12 +225,13 @@
                final ZipEntry entry = new ZipEntry(resolvedPath);
 
                // Write the Asset under the same Path name in the Zip
-               try{
+               try
+               {
                   zipOutputStream.putNextEntry(entry);
                }
-               catch(final ZipException ze)
+               catch (final ZipException ze)
                {
-                  log.log(Level.SEVERE,pathsExported.toString());
+                  log.log(Level.SEVERE, pathsExported.toString());
                   throw new RuntimeException(ze);
                }
 
@@ -219,22 +266,11 @@
     * @see org.jboss.shrinkwrap.impl.base.exporter.AbstractExporterDelegate#getResult()
     */
    @Override
-   protected InputStream getResult()
+   protected ZipExportHandle getResult()
    {
-      // Flush the output to a byte array
-      final byte[] zipContent = output.toByteArray();
-      if (log.isLoggable(Level.FINE))
-      {
-         log.fine("Created Zip of size: " + zipContent.length + " bytes");
-      }
+      return handle;
+   }
 
-      // Make an instream
-      final InputStream inputStream = new ByteArrayInputStream(zipContent);
-
-      // Return
-      return inputStream;
-   }
-   
    /**
     * Returns whether or not this Path is a parent of any Paths exported 
     * @param path
@@ -244,35 +280,37 @@
    private boolean isParentOfAnyPathsExported(final ArchivePath path)
    {
       // For all Paths already exported
-      for(final ArchivePath exportedPath :this.pathsExported)
+      for (final ArchivePath exportedPath : this.pathsExported)
       {
-         if( this.isParentOfSpecifiedHierarchy(path, exportedPath)){
+         if (this.isParentOfSpecifiedHierarchy(path, exportedPath))
+         {
             return true;
          }
       }
-      
+
       return false;
    }
-   
+
    /**
     * 
     * @param path
     * @param compare
     * @return
     */
-   private boolean isParentOfSpecifiedHierarchy(final ArchivePath path,final ArchivePath compare){
+   private boolean isParentOfSpecifiedHierarchy(final ArchivePath path, final ArchivePath compare)
+   {
       // If we've reached the root, we're not a parent of any paths already exported
       final ArchivePath parent = PathUtil.getParent(compare);
-      if(parent==null)
+      if (parent == null)
       {
          return false;
       }
       // If equal to me, yes
-      if(path.equals(compare))
+      if (path.equals(compare))
       {
          return true;
       }
-      
+
       // Check my parent
       return this.isParentOfSpecifiedHierarchy(path, parent);
    }

Added: shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/ZipExportHandleImpl.java
===================================================================
--- shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/ZipExportHandleImpl.java	                        (rev 0)
+++ shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/ZipExportHandleImpl.java	2010-01-19 21:10:36 UTC (rev 3934)
@@ -0,0 +1,136 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2010, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+  *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.shrinkwrap.impl.base.exporter;
+
+import java.io.InputStream;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.logging.Logger;
+
+import org.jboss.shrinkwrap.api.exporter.ArchiveExportException;
+import org.jboss.shrinkwrap.api.exporter.ZipExportHandle;
+
+/**
+ * Implementation of a {@link ZipExportHandle}
+ *
+ * @author <a href="mailto:andrew.rubinger at jboss.org">ALR</a>
+ */
+class ZipExportHandleImpl implements ZipExportHandle
+{
+   //-------------------------------------------------------------------------------------||
+   // Instance Members -------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Contents of the export process
+    */
+   private final IsReadReportingInputStream content;
+
+   /**
+    * Underlying job carrying out the encoding process; we never expose
+    * this because if the user blocks on {@link Future#get()} than this could
+    * deadlock the process (the writer Thread would be waiting for the reader Thread
+    * to pull off the buffer).  So just provide a mechanism for the caller to see 
+    * if we're done, and if there was an exception raised via {@link ZipExportHandle#isDone()}
+    */
+   private final Future<Void> job;
+
+   //-------------------------------------------------------------------------------------||
+   // Constructor ------------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Creates a new handle with the specified content and pointer to the encoding
+    * job.  Both arguments must be specified. 
+    */
+   ZipExportHandleImpl(final IsReadReportingInputStream content, final Future<Void> job)
+   {
+      // Precondition checks
+      assert content != null : "Contents must be specified";
+      assert job != null : "job must be specified";
+
+      // Set    
+      final Logger log = Logger.getAnonymousLogger();
+      log.info("Content: " + content + "; JOB: " + job);
+      this.content = content;
+      this.job = job;
+   }
+
+   //-------------------------------------------------------------------------------------||
+   // Required Implementations -----------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * @see org.jboss.shrinkwrap.api.exporter.ZipExportHandle#getContent()
+    */
+   @Override
+   public InputStream getContent()
+   {
+      return content;
+   }
+
+   /**
+    * @see org.jboss.shrinkwrap.api.exporter.ZipExportHandle#checkComplete()
+    */
+   @Override
+   public void checkComplete() throws ArchiveExportException, IllegalStateException
+   {
+      // Ensure we can be called; the Stream must have been fully-read
+      if (!this.content.isRead())
+      {
+         throw new IllegalStateException(
+               "Cannot invoke until the stream has been fully-read; otherwise we might lead to deadlock");
+      }
+
+      // See if we're done
+      final boolean done = job.isDone();
+
+      // If done (either completed or exception)
+      if (done)
+      {
+         try
+         {
+            // Block until the streams have been closed in the underlying job
+            job.get();
+         }
+         catch (final InterruptedException e)
+         {
+            Thread.interrupted();
+         }
+         // Some error
+         catch (final ExecutionException ee)
+         {
+            // Unwrap and rethrow
+            final Throwable cause = ee.getCause();
+            if (cause == null)
+            {
+               throw new IllegalStateException("Cause of execution failure not specified: ", ee);
+            }
+            // Wrap as our exception type and rethrow
+            throw new ArchiveExportException(cause);
+         }
+      }
+
+      // Return
+      return;
+   }
+}

Modified: shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/ZipExporterImpl.java
===================================================================
--- shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/ZipExporterImpl.java	2010-01-19 17:32:38 UTC (rev 3933)
+++ shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/ZipExporterImpl.java	2010-01-19 21:10:36 UTC (rev 3934)
@@ -27,6 +27,7 @@
 import org.jboss.shrinkwrap.api.Archive;
 import org.jboss.shrinkwrap.api.exporter.ArchiveExportException;
 import org.jboss.shrinkwrap.api.exporter.FileExistsException;
+import org.jboss.shrinkwrap.api.exporter.ZipExportHandle;
 import org.jboss.shrinkwrap.api.exporter.ZipExporter;
 import org.jboss.shrinkwrap.impl.base.AssignableBase;
 import org.jboss.shrinkwrap.impl.base.Validate;
@@ -89,7 +90,7 @@
     * @see org.jboss.shrinkwrap.api.exporter.ZipExporter#exportZip()
     */
    @Override
-   public InputStream exportZip()
+   public ZipExportHandle exportZip()
    {
       // Create export delegate
       ZipExportDelegate exportDelegate = new ZipExportDelegate(archive);
@@ -97,10 +98,10 @@
       // Execute export
       exportDelegate.export();
       // Get results
-      InputStream inputStream = exportDelegate.getResult();
+      ZipExportHandle handle = exportDelegate.getResult();
 
-      // Return input stream
-      return inputStream;
+      // Return 
+      return handle;
    }
 
    /**
@@ -124,7 +125,8 @@
       }
 
       // Get Streams
-      final InputStream in = this.exportZip();
+      final ZipExportHandle handle = this.exportZip();
+      final InputStream in = handle.getContent();
       final OutputStream out;
       try
       {
@@ -145,6 +147,8 @@
          throw new ArchiveExportException("Error encountered in exporting archive to " + target.getAbsolutePath(), e);
       }
 
+      // Ensure done and no exceptions (this also will throw ArchiveExportException)
+      handle.checkComplete();
    }
 
    /**

Modified: shrinkwrap/trunk/impl-base/src/test/java/org/jboss/shrinkwrap/impl/base/exporter/ZipExporterTestCase.java
===================================================================
--- shrinkwrap/trunk/impl-base/src/test/java/org/jboss/shrinkwrap/impl/base/exporter/ZipExporterTestCase.java	2010-01-19 17:32:38 UTC (rev 3933)
+++ shrinkwrap/trunk/impl-base/src/test/java/org/jboss/shrinkwrap/impl/base/exporter/ZipExporterTestCase.java	2010-01-19 21:10:36 UTC (rev 3934)
@@ -34,6 +34,7 @@
 import org.jboss.shrinkwrap.api.Asset;
 import org.jboss.shrinkwrap.api.exporter.ArchiveExportException;
 import org.jboss.shrinkwrap.api.exporter.FileExistsException;
+import org.jboss.shrinkwrap.api.exporter.ZipExportHandle;
 import org.jboss.shrinkwrap.api.exporter.ZipExporter;
 import org.jboss.shrinkwrap.api.spec.JavaArchive;
 import org.jboss.shrinkwrap.impl.base.asset.ByteArrayAsset;
@@ -82,16 +83,39 @@
       Archive<?> archive = createArchiveWithAssets();
 
       // Export as Zip InputStream
-      InputStream zipStream = archive.as(ZipExporter.class).exportZip();
+      final ZipExportHandle handle = archive.as(ZipExporter.class).exportZip();
+      final InputStream zipStream = handle.getContent();
 
       // Write zip content to temporary file 
       ZipFile expectedZip = getExportedZipFile(NAME_ARCHIVE, zipStream, tempDirectory);
 
+      // Ensure all's OK
+      handle.checkComplete();
+
       // Validate
       ensureZipFileInExpectedForm(expectedZip);
    }
 
    /**
+    * Test to make sue an archive can be exported to Zip and all contents are correctly located in the Zip.
+    * @throws Exception
+    */
+   @Test(expected = IllegalStateException.class)
+   public void checkCompleteBeforeReadingContents() throws Exception
+   {
+      log.info("checkCompleteBeforeReadingContents");
+
+      // Get an archive instance
+      Archive<?> archive = createArchiveWithAssets();
+
+      // Export as Zip InputStream
+      final ZipExportHandle handle = archive.as(ZipExporter.class).exportZip();
+      // We cannot check complete until we fully read the instream from the handle
+      handle.checkComplete();
+
+   }
+
+   /**
     * Ensures that we can export archives of large sizes without
     * leading to {@link OutOfMemoryError}
     */
@@ -203,7 +227,7 @@
       Archive<?> archive = createArchiveWithNestedArchives();
 
       // Export as Zip InputStream
-      InputStream zipStream = archive.as(ZipExporter.class).exportZip();
+      InputStream zipStream = archive.as(ZipExporter.class).exportZip().getContent();
 
       // Write out and retrieve Zip 
       ZipFile expectedZip = getExportedZipFile(NAME_ARCHIVE, zipStream, tempDirectory);
@@ -251,7 +275,7 @@
    }
 
    @Test(expected = ArchiveExportException.class)
-   public void testExportThrowsArchiveExceptionOnAssetWriteFailure()
+   public void testExportThrowsArchiveExceptionOnAssetWriteFailure() throws IOException
    {
       log.info("testExportThrowsArchiveExcepitonOnAssetWriteFailure");
       Archive<?> archive = createArchiveWithAssets();
@@ -266,7 +290,23 @@
 
       }, PATH_ONE);
 
-      archive.as(ZipExporter.class).exportZip();
+      // Export
+      final ZipExportHandle handle = archive.as(ZipExporter.class).exportZip();
+
+      // Read in the full content (to in turn empty the underlying buffer and ensure we complete)
+      final InputStream in = handle.getContent();
+      final OutputStream sink = new OutputStream()
+      {
+
+         @Override
+         public void write(int b) throws IOException
+         {
+         }
+      };
+      IOUtil.copyWithClose(in, sink);
+      // Get access to the underlying exception
+      handle.checkComplete();
+
    }
 
    //-------------------------------------------------------------------------------------||

Modified: shrinkwrap/trunk/impl-base/src/test/java/org/jboss/shrinkwrap/impl/base/importer/ZipImporterImplTestCase.java
===================================================================
--- shrinkwrap/trunk/impl-base/src/test/java/org/jboss/shrinkwrap/impl/base/importer/ZipImporterImplTestCase.java	2010-01-19 17:32:38 UTC (rev 3933)
+++ shrinkwrap/trunk/impl-base/src/test/java/org/jboss/shrinkwrap/impl/base/importer/ZipImporterImplTestCase.java	2010-01-19 21:10:36 UTC (rev 3934)
@@ -114,7 +114,7 @@
       
       File tempFile = new File("target/test.zip");
       tempFile.deleteOnExit();
-      InputStream zipStream = archive.as(ZipExporter.class).exportZip();
+      InputStream zipStream = archive.as(ZipExporter.class).exportZip().getContent();
       IOUtil.copyWithClose(zipStream, new FileOutputStream(tempFile));
       
       assertContent(archive, tempFile.toURI());



More information about the jboss-svn-commits mailing list