[jboss-svn-commits] JBoss Common SVN: r4552 - in shrinkwrap/trunk: extension-tar and 16 other directories.

jboss-svn-commits at lists.jboss.org jboss-svn-commits at lists.jboss.org
Thu Jun 24 23:20:54 EDT 2010


Author: ALRubinger
Date: 2010-06-24 23:20:53 -0400 (Thu, 24 Jun 2010)
New Revision: 4552

Added:
   shrinkwrap/trunk/extension-tar/
   shrinkwrap/trunk/extension-tar/pom.xml
   shrinkwrap/trunk/extension-tar/src/
   shrinkwrap/trunk/extension-tar/src/main/
   shrinkwrap/trunk/extension-tar/src/main/java/
   shrinkwrap/trunk/extension-tar/src/main/java/org/
   shrinkwrap/trunk/extension-tar/src/main/java/org/jboss/
   shrinkwrap/trunk/extension-tar/src/main/java/org/jboss/shrinkwrap/
   shrinkwrap/trunk/extension-tar/src/main/java/org/jboss/shrinkwrap/tar/
   shrinkwrap/trunk/extension-tar/src/main/java/org/jboss/shrinkwrap/tar/api/
   shrinkwrap/trunk/extension-tar/src/main/java/org/jboss/shrinkwrap/tar/api/exporter/
   shrinkwrap/trunk/extension-tar/src/main/java/org/jboss/shrinkwrap/tar/api/exporter/TarGzExporter.java
   shrinkwrap/trunk/extension-tar/src/main/java/org/jboss/shrinkwrap/tar/impl/
   shrinkwrap/trunk/extension-tar/src/main/java/org/jboss/shrinkwrap/tar/impl/exporter/
   shrinkwrap/trunk/extension-tar/src/main/java/org/jboss/shrinkwrap/tar/impl/exporter/TarGzExporterDelegate.java
   shrinkwrap/trunk/extension-tar/src/main/java/org/jboss/shrinkwrap/tar/impl/exporter/TarGzExporterImpl.java
   shrinkwrap/trunk/extension-tar/src/main/resources/
   shrinkwrap/trunk/extension-tar/src/main/resources/META-INF/
   shrinkwrap/trunk/extension-tar/src/main/resources/META-INF/services/
   shrinkwrap/trunk/extension-tar/src/main/resources/META-INF/services/org.jboss.shrinkwrap.tar.api.exporter.TarGzExporter
   shrinkwrap/trunk/extension-tar/src/test/
   shrinkwrap/trunk/extension-tar/src/test/java/
   shrinkwrap/trunk/extension-tar/src/test/resources/
   shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/AbstractStreamExporterImpl.java
   shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/StreamExporterDelegateBase.java
Modified:
   shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/AbstractExporterDelegate.java
   shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/ExplodedExporterImpl.java
   shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/FutureCompletionInputStream.java
   shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/JdkZipExporterDelegate.java
   shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/ZipExporterImpl.java
   shrinkwrap/trunk/pom.xml
Log:
[SHRINKWRAP-194] Refactoring to the stream-based export delegates to encourage code reuse before introducing a TAR.GZ exporter.  Add a naive, untested TAR.GZ exporter as well.  Still needs an importer and tests for all; probably some refactoring of the tests to get reuse there too.


Property changes on: shrinkwrap/trunk/extension-tar
___________________________________________________________________
Name: svn:ignore
   + target
bin
.classpath
.project
.settings


Added: shrinkwrap/trunk/extension-tar/pom.xml
===================================================================
--- shrinkwrap/trunk/extension-tar/pom.xml	                        (rev 0)
+++ shrinkwrap/trunk/extension-tar/pom.xml	2010-06-25 03:20:53 UTC (rev 4552)
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+  <!--
+  vi:ts=2:sw=2:expandtab:
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <!-- Parent -->
+  <parent>
+    <groupId>org.jboss.shrinkwrap</groupId>
+    <artifactId>shrinkwrap-build</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <relativePath>../build/pom.xml</relativePath>
+  </parent>
+
+  <!-- Model Version -->
+  <modelVersion>4.0.0</modelVersion>
+
+  <!-- Artifact Configuration -->
+  <artifactId>shrinkwrap-extension-tar</artifactId>
+  <name>ShrinkWrap Extension TAR</name>
+  <description>ShrinkWrap Extension for TAR Import/Export Support</description>
+
+
+  <!-- Properties -->
+  <properties>
+  
+    <!-- Versioning -->
+    <version.org.jboss.javatar>1.0.0-SNAPSHOT</version.org.jboss.javatar>
+
+  </properties>
+
+  <!-- Dependencies -->
+  <dependencies>
+
+    <!-- 
+    org.jboss.shrinkwrap    
+     -->
+    <dependency>
+      <groupId>org.jboss.shrinkwrap</groupId>
+      <artifactId>shrinkwrap-impl-base</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <!-- 
+    External Projects
+     -->
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    
+    <!-- org.jboss.javatar --> 
+    <dependency>
+      <groupId>org.jboss.javatar</groupId>
+      <artifactId>javatar</artifactId>
+      <version>${version.org.jboss.javatar}</version>
+    </dependency>
+    
+  </dependencies>
+
+  <!-- Build Configuration -->
+  <build>
+  
+    <plugins>
+
+    </plugins>
+    
+  </build>
+  
+</project>
+

Added: shrinkwrap/trunk/extension-tar/src/main/java/org/jboss/shrinkwrap/tar/api/exporter/TarGzExporter.java
===================================================================
--- shrinkwrap/trunk/extension-tar/src/main/java/org/jboss/shrinkwrap/tar/api/exporter/TarGzExporter.java	                        (rev 0)
+++ shrinkwrap/trunk/extension-tar/src/main/java/org/jboss/shrinkwrap/tar/api/exporter/TarGzExporter.java	2010-06-25 03:20:53 UTC (rev 4552)
@@ -0,0 +1,86 @@
+/*
+ * 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.tar.api.exporter;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.jboss.shrinkwrap.api.Assignable;
+import org.jboss.shrinkwrap.api.exporter.ArchiveExportException;
+import org.jboss.shrinkwrap.api.exporter.FileExistsException;
+
+/**
+ * Exporter used to represent an {@link Assignable} in TAR format encoded w/
+ * GZIP compression
+ * 
+ * @author <a href="mailto:andrew.rubinger at jboss.org">ALR</a>
+ * @see http://www.gnu.org/software/tar/manual/html_node/Standard.html
+ * @see http://www.gzip.org/
+ */
+public interface TarGzExporter extends Assignable
+{
+   //-------------------------------------------------------------------------------------||
+   // Contracts --------------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Exports this reference as a TAR.GZ archive.
+    * 
+    * @return {@link InputStream} for exported TAR.GZ
+    */
+   InputStream exportTarGz();
+
+   /**
+    * Exports provided archive as a TAR.GZ archive, written to the 
+    * specified {@link File} target.  If the target exists this call will
+    * fail with {@link IllegalArgumentException}
+    * 
+    * @param archive
+    * @return {@link InputStream} for exported Zip
+    * @throws IllegalArgumentException If the target is not specified
+    * @throws FileExistsException If the target already exists 
+    * @throws ArchiveExportException if the export process fails
+    */
+   void exportTarGz(File target) throws ArchiveExportException, FileExistsException, IllegalArgumentException;
+
+   /**
+    * Exports provided archive as a TAR.GZ archive, written to the 
+    * specified {@link OutputStream} target.  The specified
+    * target will be closed upon completion.
+    * 
+    * @param target
+    * @throws ArchiveExportException
+    * @throws IllegalArgumentException If the target is not specified
+    */
+   void exportTarGz(OutputStream target) throws ArchiveExportException, IllegalArgumentException;
+
+   /**
+    * Exports provided archive as a TAR.GZ archive, written to the 
+    * specified {@link File} target.  If the target both exists and the "overwrite"
+    * flag is true, this call will allow the existing file to be overwritten, else
+    * the invocation will fail with {@link IllegalArgumentException}
+    * 
+    * @param archive
+    * @return {@link InputStream} for exported TAR.GZ
+    * @throws IllegalArgumentException If the target is not specified 
+    * @throws FileExistsException If the target both already exists and the overwrite flag is false
+    * @throws ArchiveExportException if the export process fails
+    */
+   void exportTarGz(File target, boolean overwrite) throws ArchiveExportException, FileExistsException,
+         IllegalArgumentException;
+}

Added: shrinkwrap/trunk/extension-tar/src/main/java/org/jboss/shrinkwrap/tar/impl/exporter/TarGzExporterDelegate.java
===================================================================
--- shrinkwrap/trunk/extension-tar/src/main/java/org/jboss/shrinkwrap/tar/impl/exporter/TarGzExporterDelegate.java	                        (rev 0)
+++ shrinkwrap/trunk/extension-tar/src/main/java/org/jboss/shrinkwrap/tar/impl/exporter/TarGzExporterDelegate.java	2010-06-25 03:20:53 UTC (rev 4552)
@@ -0,0 +1,175 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, 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.tar.impl.exporter;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.concurrent.Callable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import org.jboss.javatar.TarEntry;
+import org.jboss.javatar.TarGzOutputStream;
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.asset.Asset;
+import org.jboss.shrinkwrap.impl.base.exporter.StreamExporterDelegateBase;
+
+/**
+ * Implementation of an exporter for the TAR format, further encoded as GZIP.  
+ * 
+ * @author <a href="mailto:andrew.rubinger at jboss.org">ALR</a>
+ * @version $Revision: $
+ */
+public class TarGzExporterDelegate extends StreamExporterDelegateBase<TarGzOutputStream>
+{
+   //-------------------------------------------------------------------------------------||
+   // Class Members ----------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Logger
+    */
+   private static final Logger log = Logger.getLogger(TarGzExporterDelegate.class.getName());
+
+   //-------------------------------------------------------------------------------------||
+   // Constructor ------------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Creates a new exporter delegate for exporting archives as ZIP
+    * 
+    * @throws IllegalArgumentException If the archive has no {@link Asset}s; JDK ZIP
+    * handling cannot support writing out to a {@link ZipOutputStream} with no
+    * {@link ZipEntry}s.
+    */
+   public TarGzExporterDelegate(final Archive<?> archive) throws IllegalArgumentException
+   {
+      super(archive);
+
+      //TODO Can this impl export archives with no entries?
+//      // Precondition check
+//      if (archive.getContent().isEmpty())
+//      {
+//         throw new IllegalArgumentException(
+//               "[SHRINKWRAP-93] Cannot use this JDK-based implementation to export as ZIP an archive with no content: "
+//                     + archive.toString());
+//      }
+   }
+
+   //-------------------------------------------------------------------------------------||
+   // Required Implementations -----------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * {@inheritDoc}
+    * @see org.jboss.shrinkwrap.impl.base.exporter.StreamExporterDelegateBase#closeEntry(java.io.OutputStream)
+    */
+   @Override
+   protected final void closeEntry(final TarGzOutputStream outputStream) throws IOException
+   {
+      // Close the entry
+      outputStream.closeEntry();
+   }
+
+   /**
+    * {@inheritDoc}
+    * @see org.jboss.shrinkwrap.impl.base.exporter.StreamExporterDelegateBase#createOutputStream(java.io.OutputStream)
+    */
+   @Override
+   protected final TarGzOutputStream createOutputStream(final OutputStream out) throws IOException
+   {
+      // Create and return
+      return new TarGzOutputStream(out);
+   }
+
+   /**
+    * {@inheritDoc}
+    * @see org.jboss.shrinkwrap.impl.base.exporter.StreamExporterDelegateBase#putNextExtry(java.io.OutputStream, java.lang.String)
+    */
+   @Override
+   protected final void putNextExtry(final TarGzOutputStream outputStream, final String context) throws IOException
+   {
+      // Put
+      outputStream.putNextEntry(new TarEntry(context));
+   }
+
+   /**
+    * {@inheritDoc}
+    * @see org.jboss.shrinkwrap.impl.base.exporter.StreamExporterDelegateBase#getExportTask()
+    */
+   @Override
+   protected Callable<Void> getExportTask(final Callable<Void> wrappedTask)
+   {
+      assert wrappedTask != null : "Wrapped task must be specified";
+      return new Callable<Void>()
+      {
+
+         @Override
+         public Void call() throws Exception
+         {
+            try
+            {
+               // Attempt the wrapped task
+               wrappedTask.call();
+            }
+            catch (final Exception e)
+            {
+
+               // Log this and rethrow; otherwise if we go into deadlock we won't ever 
+               // be able to get the underlying cause from the Future 
+               log.log(Level.WARNING, "Exception encountered during export of archive", e);
+
+               //TODO Can this impl export archives with no entries?  JDK ZIP impl cannot. 
+//               // SHRINKWRAP-133 - if the output is empty, it won't close and a deadlock is triggered
+//               final Set<ArchivePath> pathsExported = TarGzExporterDelegate.this.getExportedPaths();
+//               if (pathsExported.isEmpty())
+//               {
+//                  // Ensure the streams are set up before we do any work on them;
+//                  // it's possible that we encountered an exception before 
+//                  // everything has been initialized by the main Thread
+//                  // SHRINKWRAP-137
+//                  latch.await();
+//
+//                  // Write a dummy entry just so the JDK ZIP impl can close cleanly
+//                  putNextExtry(outputStream, "dummy.txt");
+//               }
+
+               throw e;
+            }
+            finally
+            {
+
+               try
+               {
+                  outputStream.close();
+               }
+               catch (final IOException ioe)
+               {
+                  // Ignore, but warn of danger
+                  log.log(Level.WARNING,
+                        "[SHRINKWRAP-120] Possible deadlock scenario: Got exception on closing the TAR.GZ out stream: "
+                              + ioe.getMessage(), ioe);
+               }
+            }
+
+            return null;
+         }
+      };
+   }
+}

Added: shrinkwrap/trunk/extension-tar/src/main/java/org/jboss/shrinkwrap/tar/impl/exporter/TarGzExporterImpl.java
===================================================================
--- shrinkwrap/trunk/extension-tar/src/main/java/org/jboss/shrinkwrap/tar/impl/exporter/TarGzExporterImpl.java	                        (rev 0)
+++ shrinkwrap/trunk/extension-tar/src/main/java/org/jboss/shrinkwrap/tar/impl/exporter/TarGzExporterImpl.java	2010-06-25 03:20:53 UTC (rev 4552)
@@ -0,0 +1,135 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, 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.tar.impl.exporter;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.logging.Logger;
+
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.exporter.ArchiveExportException;
+import org.jboss.shrinkwrap.api.exporter.FileExistsException;
+import org.jboss.shrinkwrap.impl.base.exporter.AbstractExporterDelegate;
+import org.jboss.shrinkwrap.impl.base.exporter.AbstractStreamExporterImpl;
+import org.jboss.shrinkwrap.impl.base.io.IOUtil;
+import org.jboss.shrinkwrap.tar.api.exporter.TarGzExporter;
+
+/**
+ * Implementation of {@link TarGzExporter} used to export an Archive as a TAR format
+ * encoded in GZIP. 
+ * 
+ * @author <a href="mailto:andrew.rubinger at jboss.org">ALR</a>
+ * @version $Revision: $
+ */
+public class TarGzExporterImpl extends AbstractStreamExporterImpl implements TarGzExporter
+{
+
+   //-------------------------------------------------------------------------------------||
+   // Class Members ----------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Logger
+    */
+   private static final Logger log = Logger.getLogger(TarGzExporterImpl.class.getName());
+
+   //-------------------------------------------------------------------------------------||
+   // Constructor ------------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Creates a new exporter for the specified archive
+    */
+   public TarGzExporterImpl(final Archive<?> archive)
+   {
+      super(archive);
+   }
+
+   //-------------------------------------------------------------------------------------||
+   // Required Implementations -----------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * {@inheritDoc}
+    * @see org.jboss.shrinkwrap.tar.api.exporter.TarGzExporter#exportTarGz()
+    */
+   @Override
+   public InputStream exportTarGz()
+   {
+      // Create export delegate
+      final AbstractExporterDelegate<InputStream> exportDelegate = new TarGzExporterDelegate(this.getArchive());
+
+      // Execute export
+      return exportDelegate.export();
+   }
+
+   /**
+    * {@inheritDoc}
+    * @see org.jboss.shrinkwrap.tar.api.exporter.TarGzExporter#exportTarGz(java.io.OutputStream)
+    */
+   @Override
+   public void exportTarGz(final OutputStream target) throws ArchiveExportException, IllegalArgumentException
+   {
+      // Precondition checks
+      if (target == null)
+      {
+         throw new IllegalArgumentException("Target must be specified");
+      }
+
+      // Get Stream
+      final InputStream in = this.exportTarGz();
+
+      // Write out
+      try
+      {
+         IOUtil.copyWithClose(in, target);
+      }
+      catch (final IOException e)
+      {
+         throw new ArchiveExportException("Error encountered in exporting archive to " + target, e);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    * @see org.jboss.shrinkwrap.tar.api.exporter.TarGzExporter#exportTarGz(java.io.File, boolean)
+    */
+   @Override
+   public void exportTarGz(final File target, final boolean overwrite) throws ArchiveExportException,
+         FileExistsException, IllegalArgumentException
+   {
+      // Get stream and perform precondition checks
+      final OutputStream out = this.getOutputStreamToFile(target, overwrite);
+
+      // Write out
+      this.exportTarGz(out);
+   }
+
+   /**
+    * {@inheritDoc}
+    * @see org.jboss.shrinkwrap.tar.api.exporter.TarGzExporter#exportTarGz(java.io.File)
+    */
+   @Override
+   public void exportTarGz(final File target) throws ArchiveExportException, FileExistsException,
+         IllegalArgumentException
+   {
+      this.exportTarGz(target, false);
+   }
+
+}

Added: shrinkwrap/trunk/extension-tar/src/main/resources/META-INF/services/org.jboss.shrinkwrap.tar.api.exporter.TarGzExporter
===================================================================
--- shrinkwrap/trunk/extension-tar/src/main/resources/META-INF/services/org.jboss.shrinkwrap.tar.api.exporter.TarGzExporter	                        (rev 0)
+++ shrinkwrap/trunk/extension-tar/src/main/resources/META-INF/services/org.jboss.shrinkwrap.tar.api.exporter.TarGzExporter	2010-06-25 03:20:53 UTC (rev 4552)
@@ -0,0 +1 @@
+org.jboss.shrinkwrap.tar.impl.exporter.TarGzExporterImpl
\ No newline at end of file

Modified: shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/AbstractExporterDelegate.java
===================================================================
--- shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/AbstractExporterDelegate.java	2010-06-24 08:56:59 UTC (rev 4551)
+++ shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/AbstractExporterDelegate.java	2010-06-25 03:20:53 UTC (rev 4552)
@@ -66,39 +66,52 @@
       super();
       this.archive = archive;
    }
+   
+   /**
+    * Runs the export operation, returning the result
+    * @return
+    */
+   public final T export()
+   {
+      // Perform the actual export
+      this.doExport();
 
+      // Return the result
+      return this.getResult();
+   }
+
    /**
     * Primary method providing a template for exporting the contents of an archive
     */
-   protected void export()
+   protected void doExport()
    {
       // Get archive
-      Archive<?> archive = getArchive();
+      final Archive<?> archive = getArchive();
       if (log.isLoggable(Level.FINE))
       {
          log.fine("Exporting archive - " + archive.getName());
       }
 
-      // Obtain all content
-      final Node rootNode = archive.get(ArchivePaths.create("/"));
-      
-      // recursively process the node childs
-      for (Node child : rootNode.getChildren()) 
+      // Obtain the root
+      final Node rootNode = archive.get(ArchivePaths.root());
+
+      // Recursively process the root children
+      for (Node child : rootNode.getChildren())
       {
          processNode(child);
       }
    }
-   
+
    /**
     * Recursive call to process all the node hierarchy
     * @param node
     */
-   protected void processNode(final Node node) 
+   private void processNode(final Node node)
    {
       processNode(node.getPath(), node);
-      
+
       Set<Node> children = node.getChildren();
-      for (Node child : children) 
+      for (Node child : children)
       {
          processNode(child);
       }

Added: shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/AbstractStreamExporterImpl.java
===================================================================
--- shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/AbstractStreamExporterImpl.java	                        (rev 0)
+++ shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/AbstractStreamExporterImpl.java	2010-06-25 03:20:53 UTC (rev 4552)
@@ -0,0 +1,113 @@
+/*
+ * 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.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.exporter.ArchiveExportException;
+import org.jboss.shrinkwrap.api.exporter.FileExistsException;
+import org.jboss.shrinkwrap.impl.base.AssignableBase;
+import org.jboss.shrinkwrap.impl.base.Validate;
+
+/**
+ * Base support for I/O Stream-based exporters
+ * 
+ * @author <a href="mailto:andrew.rubinger at jboss.org">ALR</a>
+ */
+public abstract class AbstractStreamExporterImpl extends AssignableBase
+{
+   
+   //-------------------------------------------------------------------------------------||
+   // Instance Members -------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Archive to import into. 
+    */
+   private final Archive<?> archive;
+   
+   //-------------------------------------------------------------------------------------||
+   // Constructor ------------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+   
+   public AbstractStreamExporterImpl(final Archive<?> archive)
+   {
+      Validate.notNull(archive, "Archive must be specified");
+      this.archive = archive;
+   }
+   
+   //-------------------------------------------------------------------------------------||
+   // Required Implementations -----------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+   
+   /**
+    * {@inheritDoc}
+    * @see org.jboss.shrinkwrap.impl.base.AssignableBase#getArchive()
+    */
+   @Override
+   protected Archive<?> getArchive()
+   {
+      return archive;
+   }
+   
+   //-------------------------------------------------------------------------------------||
+   // Functional Methods -----------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Obtains an {@link OuputStream} to the provided {@link File}.
+    * @param target
+    * @param overwrite Whether we may overwrite an existing file
+    * @return
+    * @throws FileExistsException If the specified file exists and the overwrite flag is false
+    * @throws IllegalArgumentException If the file target is not specified
+    */
+   protected final OutputStream getOutputStreamToFile(final File target, final boolean overwrite)
+         throws FileExistsException, IllegalArgumentException
+   {
+      // Precondition checks
+      if (target == null)
+      {
+         throw new IllegalArgumentException("Target file must be specified");
+      }
+      // If target exists and we're not allowed to overwrite it
+      if (target.exists() && !overwrite)
+      {
+         throw new FileExistsException("Target exists and we haven't been flagged to overwrite it: "
+               + target.getAbsolutePath());
+      }
+
+      // Get Stream
+      final OutputStream out;
+      try
+      {
+         out = new FileOutputStream(target);
+      }
+      catch (final FileNotFoundException e)
+      {
+         throw new ArchiveExportException("File could not be created: " + target);
+      }
+
+      // Return
+      return out;
+   }
+
+}

Modified: shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/ExplodedExporterImpl.java
===================================================================
--- shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/ExplodedExporterImpl.java	2010-06-24 08:56:59 UTC (rev 4551)
+++ shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/ExplodedExporterImpl.java	2010-06-25 03:20:53 UTC (rev 4552)
@@ -78,11 +78,12 @@
    // Required Implementations - ExplodedExporter ----------------------------------------||
    //-------------------------------------------------------------------------------------||
 
-   /* (non-Javadoc)
+   /**
+    * {@inheritDoc}
     * @see org.jboss.shrinkwrap.api.exporter.ExplodedExporter#exportExploded(java.io.File)
     */
    @Override
-   public File exportExploded(File baseDirectory)
+   public File exportExploded(final File baseDirectory)
    {
       Validate.notNull(archive, "No archive provided");
       Validate.notNull(baseDirectory, "No baseDirectory provided");
@@ -99,13 +100,10 @@
       }
 
       // Get the export delegate
-      ExplodedExporterDelegate exporterDelegate = new ExplodedExporterDelegate(archive, baseDirectory);
-
-      // Run the export
-      exporterDelegate.export();
+      final ExplodedExporterDelegate exporterDelegate = new ExplodedExporterDelegate(archive, baseDirectory);
       
-      // Get Result
-      File explodedDirectory = exporterDelegate.getResult();
+      // Run the export and get the result
+      final File explodedDirectory = exporterDelegate.export();
 
       if (log.isLoggable(Level.FINE))
       {

Modified: shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/FutureCompletionInputStream.java
===================================================================
--- shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/FutureCompletionInputStream.java	2010-06-24 08:56:59 UTC (rev 4551)
+++ shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/FutureCompletionInputStream.java	2010-06-25 03:20:53 UTC (rev 4552)
@@ -33,7 +33,7 @@
  * @param <T> Response type of the {@link Future}
  * @author <a href="mailto:andrew.rubinger at jboss.org">ALR</a>
  */
-class FutureCompletionInputStream extends PipedInputStream
+public class FutureCompletionInputStream extends PipedInputStream
 {
 
    //-------------------------------------------------------------------------------------||

Modified: shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/JdkZipExporterDelegate.java
===================================================================
--- shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/JdkZipExporterDelegate.java	2010-06-24 08:56:59 UTC (rev 4551)
+++ shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/JdkZipExporterDelegate.java	2010-06-25 03:20:53 UTC (rev 4552)
@@ -17,32 +17,17 @@
 package org.jboss.shrinkwrap.impl.base.exporter;
 
 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.CountDownLatch;
-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;
-import java.util.zip.ZipException;
 import java.util.zip.ZipOutputStream;
 
 import org.jboss.shrinkwrap.api.Archive;
 import org.jboss.shrinkwrap.api.ArchivePath;
-import org.jboss.shrinkwrap.api.Node;
 import org.jboss.shrinkwrap.api.asset.Asset;
-import org.jboss.shrinkwrap.api.exporter.ArchiveExportException;
-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;
-import org.jboss.shrinkwrap.spi.Configurable;
 
 /**
  * JDK-based implementation of a ZIP exporter.  Cannot handle archives
@@ -52,7 +37,7 @@
  * @author <a href="mailto:andrew.rubinger at jboss.org">ALR</a>
  * @version $Revision: $
  */
-public class JdkZipExporterDelegate extends AbstractExporterDelegate<InputStream>
+public class JdkZipExporterDelegate extends StreamExporterDelegateBase<ZipOutputStream>
 {
    //-------------------------------------------------------------------------------------||
    // Class Members ----------------------------------------------------------------------||
@@ -64,35 +49,11 @@
    private static final Logger log = Logger.getLogger(JdkZipExporterDelegate.class.getName());
 
    //-------------------------------------------------------------------------------------||
-   // Instance Members -------------------------------------------------------------------||
-   //-------------------------------------------------------------------------------------||
-
-   /**
-    * ZipOutputStream used to write the zip entries
-    */
-   private ZipOutputStream zipOutputStream;
-
-   /**
-    * {@link InputStream} to be returned to the caller
-    */
-   private InputStream inputStream;
-
-   /**
-    * Used to see if we have exported at least one node
-    */
-   private Set<ArchivePath> pathsExported = new HashSet<ArchivePath>();
-
-   /**
-    * Synchronization point where the encoding process will wait until all streams have been set up
-    */
-   private final CountDownLatch latch = new CountDownLatch(1);
-
-   //-------------------------------------------------------------------------------------||
    // Constructor ------------------------------------------------------------------------||
    //-------------------------------------------------------------------------------------||
 
    /**
-    * Creates a new exporter delegate for exporting archives as Zip
+    * Creates a new exporter delegate for exporting archives as ZIP
     * 
     * @throws IllegalArgumentException If the archive has no {@link Asset}s; JDK ZIP
     * handling cannot support writing out to a {@link ZipOutputStream} with no
@@ -117,13 +78,46 @@
 
    /**
     * {@inheritDoc}
-    * @see org.jboss.shrinkwrap.impl.base.exporter.AbstractExporterDelegate#export()
+    * @see org.jboss.shrinkwrap.impl.base.exporter.StreamExporterDelegateBase#closeEntry(java.io.OutputStream)
     */
-   protected void export()
+   @Override
+   protected final void closeEntry(final ZipOutputStream outputStream) throws IOException
    {
+      // Close the entry
+      outputStream.closeEntry();
+   }
 
-      // Define the task to operate in another Thread so we can pipe the output to an InStream
-      final Callable<Void> exportTask = new Callable<Void>()
+   /**
+    * {@inheritDoc}
+    * @see org.jboss.shrinkwrap.impl.base.exporter.StreamExporterDelegateBase#createOutputStream(java.io.OutputStream)
+    */
+   @Override
+   protected final ZipOutputStream createOutputStream(final OutputStream out) throws IOException
+   {
+      // Create and return
+      return new ZipOutputStream(out);
+   }
+
+   /**
+    * {@inheritDoc}
+    * @see org.jboss.shrinkwrap.impl.base.exporter.StreamExporterDelegateBase#putNextExtry(java.io.OutputStream, java.lang.String)
+    */
+   @Override
+   protected final void putNextExtry(final ZipOutputStream outputStream, final String context) throws IOException
+   {
+      // Put
+      outputStream.putNextEntry(new ZipEntry(context));
+   }
+
+   /**
+    * {@inheritDoc}
+    * @see org.jboss.shrinkwrap.impl.base.exporter.StreamExporterDelegateBase#getExportTask()
+    */
+   @Override
+   protected Callable<Void> getExportTask(final Callable<Void> wrappedTask)
+   {
+      assert wrappedTask != null : "Wrapped task must be specified";
+      return new Callable<Void>()
       {
 
          @Override
@@ -131,7 +125,8 @@
          {
             try
             {
-               JdkZipExporterDelegate.super.export();
+               // Attempt the wrapped task
+               wrappedTask.call();
             }
             catch (final Exception e)
             {
@@ -140,7 +135,8 @@
                // be able to get the underlying cause from the Future 
                log.log(Level.WARNING, "Exception encountered during export of archive", e);
 
-               // SHRINKWRAP-133 - if the Zip is empty, it won't close and a deadlock is triggered
+               // SHRINKWRAP-133 - if the output is empty, it won't close and a deadlock is triggered
+               final Set<ArchivePath> pathsExported = JdkZipExporterDelegate.this.getExportedPaths();
                if (pathsExported.isEmpty())
                {
                   // Ensure the streams are set up before we do any work on them;
@@ -149,7 +145,8 @@
                   // SHRINKWRAP-137
                   latch.await();
 
-                  zipOutputStream.putNextEntry(new ZipEntry("dummy.txt"));
+                  // Write a dummy entry just so the JDK ZIP impl can close cleanly
+                  putNextExtry(outputStream, "dummy.txt");
                }
 
                throw e;
@@ -159,7 +156,7 @@
 
                try
                {
-                  zipOutputStream.close();
+                  outputStream.close();
                }
                catch (final IOException ioe)
                {
@@ -173,156 +170,5 @@
             return null;
          }
       };
-
-      // Get an ExecutorService to which we may submit jobs.  This is either supplied by the user
-      // in a custom domain, or if one has not been specified, we'll make one and shut it down right
-      // here.  ExecutorServices supplied by the user are under the user's lifecycle, therefore it's
-      // user responsibility to shut it down appropriately.
-      boolean executorServiceIsOurs = false;
-      ExecutorService service = this.getArchive().as(Configurable.class).getConfiguration().getExecutorService();
-      if (service == null)
-      {
-         service = Executors.newSingleThreadExecutor();
-         executorServiceIsOurs = true;
-      }
-
-      // Get a handle and return it to the caller
-      final Future<Void> job = service.submit(exportTask);
-
-      // If we've created the ES
-      if (executorServiceIsOurs)
-      {
-         // Tell the service to shut down after the job has completed, and accept no new jobs
-         service.shutdown();
-      }
-
-      /*
-       * At this point the job will start, but hit the latch until we set up the streams
-       * and tell it to proceed.
-       */
-
-      // Stream to return to the caller
-      final FutureCompletionInputStream input = new FutureCompletionInputStream(job);
-      inputStream = input;
-
-      /**
-       * 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);
-      }
-
-      // Set up the stream to which we'll write entries, backed by the piped stream
-      zipOutputStream = new ZipOutputStream(output);
-
-      /*
-       * The job is now waiting on us to signal that we've set up the streams; 
-       * let it continue
-       */
-      latch.countDown();
    }
-
-   /**
-    * {@inheritDoc}
-    * @see org.jboss.shrinkwrap.impl.base.exporter.AbstractExporterDelegate#processNode(ArchivePath, Node)
-    */
-   @Override
-   protected void processNode(final ArchivePath path, final Node node)
-   {
-      // Precondition checks
-      if (path == null)
-      {
-         throw new IllegalArgumentException("Path must be specified");
-      }
-      if (node == null)
-      {
-         throw new IllegalArgumentException("asset must be specified");
-      }
-
-      // Mark if we're writing a directory
-      final boolean isDirectory = node.getAsset() == null;
-
-      InputStream stream = null;
-      if (!isDirectory)
-      {
-         stream = node.getAsset().openStream();
-      }
-
-      final String pathName = PathUtil.optionallyRemovePrecedingSlash(path.get());
-
-      // Make a task for this stream and close when done
-      IOUtil.closeOnComplete(stream, new StreamTask<InputStream>()
-      {
-
-         @Override
-         public void execute(InputStream stream) throws Exception
-         {
-            String resolvedPath = pathName;
-            if (isDirectory)
-            {
-               resolvedPath = PathUtil.optionallyAppendSlash(resolvedPath);
-            }
-
-            // Make a ZipEntry
-            final ZipEntry entry = new ZipEntry(resolvedPath);
-
-            /*
-             * Wait until all streams have been set up for encoding, or
-             * do nothing if everything's set up already
-             */
-            latch.await();
-
-            // Write the Asset under the same Path name in the Zip
-            try
-            {
-               zipOutputStream.putNextEntry(entry);
-            }
-            catch (final ZipException ze)
-            {
-               log.log(Level.SEVERE, pathsExported.toString());
-               throw new RuntimeException(ze);
-            }
-
-            // Mark that we've written this Path 
-            pathsExported.add(path);
-
-            // Read the contents of the asset and write to the JAR, 
-            // if we're not just a directory
-            if (!isDirectory)
-            {
-               IOUtil.copy(stream, zipOutputStream);
-            }
-
-            // Close up the instream and the entry
-            zipOutputStream.closeEntry();
-         }
-
-      }, new StreamErrorHandler()
-      {
-
-         @Override
-         public void handle(Throwable t)
-         {
-            throw new ArchiveExportException("Failed to write asset to Zip: " + path.get(), t);
-         }
-
-      });
-   }
-
-   /* (non-Javadoc)
-    * @see org.jboss.shrinkwrap.impl.base.exporter.AbstractExporterDelegate#getResult()
-    */
-   @Override
-   protected InputStream getResult()
-   {
-      return inputStream;
-   }
-
 }

Added: shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/StreamExporterDelegateBase.java
===================================================================
--- shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/StreamExporterDelegateBase.java	                        (rev 0)
+++ shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/StreamExporterDelegateBase.java	2010-06-25 03:20:53 UTC (rev 4552)
@@ -0,0 +1,321 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, 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.InputStream;
+import java.io.OutputStream;
+import java.io.PipedOutputStream;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+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 org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.ArchivePath;
+import org.jboss.shrinkwrap.api.Node;
+import org.jboss.shrinkwrap.api.exporter.ArchiveExportException;
+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;
+import org.jboss.shrinkwrap.spi.Configurable;
+
+/**
+ * Base for exporters capable of writing to some implementation
+ * of {@link OutputStream}
+ * 
+ * @author <a href="mailto:andrew.rubinger at jboss.org">ALR</a>
+ * @version $Revision: $
+ */
+public abstract class StreamExporterDelegateBase<O extends OutputStream> extends AbstractExporterDelegate<InputStream>
+{
+   //-------------------------------------------------------------------------------------||
+   // Class Members ----------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Logger
+    */
+   private static final Logger log = Logger.getLogger(StreamExporterDelegateBase.class.getName());
+
+   //-------------------------------------------------------------------------------------||
+   // Instance Members -------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * {@link OutputStream} used to write the individual entries
+    */
+   protected O outputStream;
+
+   /**
+    * {@link InputStream} to be returned to the caller
+    */
+   private InputStream inputStream;
+
+   /**
+    * Used to see if we have exported at least one node
+    */
+   private Set<ArchivePath> pathsExported = new HashSet<ArchivePath>();
+
+   /**
+    * Synchronization point where the encoding process will wait until all streams have been set up
+    */
+   protected final CountDownLatch latch = new CountDownLatch(1);
+
+   //-------------------------------------------------------------------------------------||
+   // Constructor ------------------------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Creates a new exporter delegate for exporting archives
+    */
+   public StreamExporterDelegateBase(final Archive<?> archive) throws IllegalArgumentException
+   {
+      // Delegate to super
+      super(archive);
+   }
+
+   //-------------------------------------------------------------------------------------||
+   // Required Implementations -----------------------------------------------------------||
+   //-------------------------------------------------------------------------------------||
+
+   /**
+    * Returns the task used to run the export operation in another Thread.  Exposed
+    * such that the specified task (which handles the export) may be wrapped in some 
+    * error handling logic specific to the export process. 
+    * @param wrappedTask The export task to be wrapped in more specific handling logic
+    */
+   protected abstract Callable<Void> getExportTask(Callable<Void> wrappedTask);
+
+   /**
+    * {@inheritDoc}
+    * @see org.jboss.shrinkwrap.impl.base.exporter.AbstractExporterDelegate#doExport()
+    */
+   protected void doExport()
+   {
+      // Define the task to operate in another Thread so we can pipe the output to an InStream
+      final Callable<Void> exportTask = this.getExportTask(new Callable<Void>()
+      {
+
+         // Wrapped task is the super implementation
+         @Override
+         public Void call() throws Exception
+         {
+            StreamExporterDelegateBase.super.doExport();
+            return null;
+         }
+
+      });
+
+      // Get an ExecutorService to which we may submit jobs.  This is either supplied by the user
+      // in a custom domain, or if one has not been specified, we'll make one and shut it down right
+      // here.  ExecutorServices supplied by the user are under the user's lifecycle, therefore it's
+      // user responsibility to shut it down appropriately.
+      boolean executorServiceIsOurs = false;
+      ExecutorService service = this.getArchive().as(Configurable.class).getConfiguration().getExecutorService();
+      if (service == null)
+      {
+         service = Executors.newSingleThreadExecutor();
+         executorServiceIsOurs = true;
+      }
+
+      // Get a handle and return it to the caller
+      final Future<Void> job = service.submit(exportTask);
+
+      // If we've created the ES
+      if (executorServiceIsOurs)
+      {
+         // Tell the service to shut down after the job has completed, and accept no new jobs
+         service.shutdown();
+      }
+
+      /*
+       * At this point the job will start, but hit the latch until we set up the streams
+       * and tell it to proceed.
+       */
+
+      // Stream to return to the caller
+      final FutureCompletionInputStream input = new FutureCompletionInputStream(job);
+      inputStream = input;
+
+      /**
+       * OutputStream which will be associated with the returned InStream, and the 
+       * chained IO point for the final OutStream
+       */
+      final OutputStream output;
+      try
+      {
+         output = new PipedOutputStream(input);
+      }
+      catch (final IOException e)
+      {
+         throw new RuntimeException("Error in setting up output stream", e);
+      }
+
+      // Set up the stream to which we'll write entries, backed by the piped stream
+      try
+      {
+         outputStream = StreamExporterDelegateBase.this.createOutputStream(output);
+      }
+      catch (final IOException e)
+      {
+         throw new ArchiveExportException("Could not create the underlying stream to export: "
+               + this.getArchive().toString(), e);
+      }
+
+      /*
+       * The job is now waiting on us to signal that we've set up the streams; 
+       * let it continue
+       */
+      latch.countDown();
+   }
+
+   /**
+    * Writes the next entry (demarcates a new file/folder
+    * is to be written)
+    * @param outputStream
+    * @param context
+    * @throws IOException If an error occurred writing the entry
+    */
+   protected abstract void putNextExtry(O outputStream, String context) throws IOException;
+
+   /**
+    * Closes the current entry context for the specified {@link OutputStream}
+    * @param outputStream
+    */
+   protected abstract void closeEntry(O outputStream) throws IOException;
+
+   /**
+    * Creates the real {@link OutputStream} to which we'll write, 
+    * wrapping the provided target.
+    * @param out
+    * @return
+    * @throws IOException If an error occurred in creating the stream
+    */
+   protected abstract O createOutputStream(OutputStream out) throws IOException;
+
+   /**
+    * {@inheritDoc}
+    * @see org.jboss.shrinkwrap.impl.base.exporter.AbstractExporterDelegate#processNode(ArchivePath, Node)
+    */
+   @Override
+   protected void processNode(final ArchivePath path, final Node node)
+   {
+      // Precondition checks
+      if (path == null)
+      {
+         throw new IllegalArgumentException("Path must be specified");
+      }
+      if (node == null)
+      {
+         throw new IllegalArgumentException("asset must be specified");
+      }
+
+      // Mark if we're writing a directory
+      final boolean isDirectory = node.getAsset() == null;
+
+      InputStream stream = null;
+      if (!isDirectory)
+      {
+         stream = node.getAsset().openStream();
+      }
+
+      final String pathName = PathUtil.optionallyRemovePrecedingSlash(path.get());
+
+      // Make a task for this stream and close when done
+      IOUtil.closeOnComplete(stream, new StreamTask<InputStream>()
+      {
+
+         @Override
+         public void execute(InputStream stream) throws Exception
+         {
+            String resolvedPath = pathName;
+            if (isDirectory)
+            {
+               resolvedPath = PathUtil.optionallyAppendSlash(resolvedPath);
+            }
+
+            /*
+             * Wait until all streams have been set up for encoding, or
+             * do nothing if everything's set up already
+             */
+            latch.await();
+
+            // Write the Asset under the same Path name in the output
+            try
+            {
+               putNextExtry(outputStream, resolvedPath);
+            }
+            catch (final IOException ze)
+            {
+               log.log(Level.SEVERE, pathsExported.toString());
+               throw new RuntimeException(ze);
+            }
+
+            // Mark that we've written this Path 
+            pathsExported.add(path);
+
+            // Read the contents of the asset and write to the JAR, 
+            // if we're not just a directory
+            if (!isDirectory)
+            {
+               IOUtil.copy(stream, outputStream);
+            }
+
+            // Close up the instream and the entry
+            StreamExporterDelegateBase.this.closeEntry(outputStream);
+         }
+
+      }, new StreamErrorHandler()
+      {
+
+         @Override
+         public void handle(Throwable t)
+         {
+            throw new ArchiveExportException("Failed to write asset to output: " + path.get(), t);
+         }
+
+      });
+   }
+
+   /**
+    * {@inheritDoc}
+    * @see org.jboss.shrinkwrap.impl.base.exporter.AbstractExporterDelegate#getResult()
+    */
+   @Override
+   protected InputStream getResult()
+   {
+      return inputStream;
+   }
+
+   /**
+    * Returns an immutable view of all {@link ArchivePath}s currently exported
+    * @return
+    */
+   protected final Set<ArchivePath> getExportedPaths()
+   {
+      return Collections.unmodifiableSet(this.pathsExported);
+   }
+
+}

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-06-24 08:56:59 UTC (rev 4551)
+++ shrinkwrap/trunk/impl-base/src/main/java/org/jboss/shrinkwrap/impl/base/exporter/ZipExporterImpl.java	2010-06-25 03:20:53 UTC (rev 4552)
@@ -17,8 +17,6 @@
 package org.jboss.shrinkwrap.impl.base.exporter;
 
 import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -28,8 +26,6 @@
 import org.jboss.shrinkwrap.api.exporter.ArchiveExportException;
 import org.jboss.shrinkwrap.api.exporter.FileExistsException;
 import org.jboss.shrinkwrap.api.exporter.ZipExporter;
-import org.jboss.shrinkwrap.impl.base.AssignableBase;
-import org.jboss.shrinkwrap.impl.base.Validate;
 import org.jboss.shrinkwrap.impl.base.io.IOUtil;
 
 /**
@@ -40,7 +36,7 @@
  * @author <a href="mailto:andrew.rubinger at jboss.org">ALR</a>
  * @version $Revision: $
  */
-public class ZipExporterImpl extends AssignableBase implements ZipExporter
+public class ZipExporterImpl extends AbstractStreamExporterImpl implements ZipExporter
 {
 
    //-------------------------------------------------------------------------------------||
@@ -52,35 +48,16 @@
     */
    private static final Logger log = Logger.getLogger(ZipExporterImpl.class.getName());
 
-   /**
-    * Archive to import into. 
-    */
-   private Archive<?> archive;
-
    //-------------------------------------------------------------------------------------||
    // Constructor ------------------------------------------------------------------------||
    //-------------------------------------------------------------------------------------||
 
    public ZipExporterImpl(final Archive<?> archive)
    {
-      Validate.notNull(archive, "Archive must be specified");
-      this.archive = archive;
+      super(archive);
    }
 
    //-------------------------------------------------------------------------------------||
-   // Required Implementations -----------------------------------------------------------||
-   //-------------------------------------------------------------------------------------||
-
-   /* (non-Javadoc)
-    * @see org.jboss.shrinkwrap.impl.base.SpecializedBase#getArchive()
-    */
-   @Override
-   protected Archive<?> getArchive()
-   {
-      return archive;
-   }
-
-   //-------------------------------------------------------------------------------------||
    // Required Implementations - ZipExporter ---------------------------------------------||
    //-------------------------------------------------------------------------------------||
 
@@ -92,15 +69,10 @@
    public InputStream exportZip()
    {
       // Create export delegate
-      AbstractExporterDelegate<InputStream> exportDelegate = new JdkZipExporterDelegate(archive);
+      AbstractExporterDelegate<InputStream> exportDelegate = new JdkZipExporterDelegate(this.getArchive());
 
-      // Execute export
-      exportDelegate.export();
-      // Get results
-      InputStream stream = exportDelegate.getResult();
-
-      // Return 
-      return stream;
+      // Export and get result
+      return exportDelegate.export();
    }
 
    /**
@@ -137,29 +109,9 @@
    public void exportZip(final File target, final boolean overwrite) throws ArchiveExportException,
          FileExistsException, IllegalArgumentException
    {
-      // Precondition checks
-      if (target == null)
-      {
-         throw new IllegalArgumentException("Target file must be specified");
-      }
-      // If target exists and we're not allowed to overwrite it
-      if (target.exists() && !overwrite)
-      {
-         throw new FileExistsException("Target exists and we haven't been flagged to overwrite it: "
-               + target.getAbsolutePath());
-      }
+      // Get stream and perform precondition checks
+      final OutputStream out = this.getOutputStreamToFile(target, overwrite);
 
-      // Get Stream
-      final OutputStream out;
-      try
-      {
-         out = new FileOutputStream(target);
-      }
-      catch (final FileNotFoundException e)
-      {
-         throw new ArchiveExportException("File could not be created: " + target);
-      }
-
       // Write out
       this.exportZip(out);
    }

Modified: shrinkwrap/trunk/pom.xml
===================================================================
--- shrinkwrap/trunk/pom.xml	2010-06-24 08:56:59 UTC (rev 4551)
+++ shrinkwrap/trunk/pom.xml	2010-06-25 03:20:53 UTC (rev 4552)
@@ -72,6 +72,7 @@
     <module>extension-glassfish</module>
     <module>extension-jetty</module>
     <module>extension-openejb</module>
+    <module>extension-tar</module>
     <module>extension-vfs3</module>
     <module>extension-vdf</module>
   </modules>



More information about the jboss-svn-commits mailing list