TUSCANY-4067: Update to support resources in jars and zips nested within contributions

git-svn-id: https://svn.apache.org/repos/asf/tuscany/sca-java-2.x/trunk@1367134 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/modules/contribution/src/main/java/org/apache/tuscany/sca/contribution/Contribution.java b/modules/contribution/src/main/java/org/apache/tuscany/sca/contribution/Contribution.java
index 07824bd..d092af3 100644
--- a/modules/contribution/src/main/java/org/apache/tuscany/sca/contribution/Contribution.java
+++ b/modules/contribution/src/main/java/org/apache/tuscany/sca/contribution/Contribution.java
@@ -19,6 +19,7 @@
 
 package org.apache.tuscany.sca.contribution;
 
+import java.net.URL;
 import java.util.List;
 import java.util.Set;
 
@@ -133,4 +134,9 @@
     void mergeMetaData(ContributionMetadata metaData);
 
     void addComposite(Composite composite);
+    
+    List<URL> getExtractedArchives();
+    
+    boolean useNestedArchives();
+    void setUseNestedArcives(boolean b);
 }
\ No newline at end of file
diff --git a/modules/contribution/src/main/java/org/apache/tuscany/sca/contribution/impl/ContributionImpl.java b/modules/contribution/src/main/java/org/apache/tuscany/sca/contribution/impl/ContributionImpl.java
index b795ac9..aa6d75c 100644
--- a/modules/contribution/src/main/java/org/apache/tuscany/sca/contribution/impl/ContributionImpl.java
+++ b/modules/contribution/src/main/java/org/apache/tuscany/sca/contribution/impl/ContributionImpl.java
@@ -19,6 +19,7 @@
 
 package org.apache.tuscany.sca.contribution.impl;
 
+import java.net.URL;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
@@ -51,9 +52,11 @@
     private List<Contribution> dependencies = new ArrayList<Contribution>();
     private ModelResolver modelResolver;
     private Set<String> types = new HashSet<String>();
+    private List<URL> extractedArchives = new ArrayList<URL>();
 
     // FIXME remove this dependency on Java ClassLoaders
     private ClassLoader classLoader;
+    private boolean useNestedArchives = true;
 
     ContributionImpl() {
     }
@@ -193,4 +196,19 @@
         a.setUnresolved(false);
         artifacts.add(a);
     }
+
+    @Override
+    public List<URL> getExtractedArchives() {
+        return extractedArchives;
+    }
+
+    @Override
+    public boolean useNestedArchives() {
+        return useNestedArchives;
+    }
+
+    @Override
+    public void setUseNestedArcives(boolean b) {
+       this.useNestedArchives = b;
+    }
 }
diff --git a/modules/contribution/src/main/java/org/apache/tuscany/sca/contribution/java/impl/ContributionHelper.java b/modules/contribution/src/main/java/org/apache/tuscany/sca/contribution/java/impl/ContributionHelper.java
index 3726071..f8ca086 100644
--- a/modules/contribution/src/main/java/org/apache/tuscany/sca/contribution/java/impl/ContributionHelper.java
+++ b/modules/contribution/src/main/java/org/apache/tuscany/sca/contribution/java/impl/ContributionHelper.java
@@ -18,85 +18,23 @@
  */
 package org.apache.tuscany.sca.contribution.java.impl;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
-import java.net.URI;
 import java.net.URL;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
 
-import org.apache.tuscany.sca.contribution.Artifact;
 import org.apache.tuscany.sca.contribution.Contribution;
 
 public class ContributionHelper {
 
     public static List<URL> getNestedJarUrls(final Contribution contribution) throws IOException {
         List<URL> urls = new ArrayList<URL>();
-        final boolean isZipContribution = contribution.getLocation().endsWith(".zip");
-        final URI uri = URI.create(contribution.getLocation());
-        boolean isFolderContribution = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
-            public Boolean run() {
-               return !isZipContribution && uri.getScheme().equals("file") && new File(uri.getSchemeSpecificPart()).isDirectory();
-            }
-        });
-        if (isZipContribution || isFolderContribution) {
-            for (Artifact a : contribution.getArtifacts()) {
-                if (a.getLocation().endsWith(".jar")) {
-                    if (isZipContribution) {
-                        urls.add(createTempJar(a, contribution));
-                    } else {
-                        urls.add(new URL(a.getLocation()));
-                    }
-                }
+        for (URL url : contribution.getExtractedArchives()) {
+            if (url.toString().endsWith(".jar")) {
+                urls.add(url);
             }
         }
         return urls;
     }
 
-    /**
-     * URLClassLoader doesn't seem to work with URLs to jars within an archive so as a work around
-     * copy the jar to a temp file and use the url to that.
-     */
-    private static URL createTempJar(Artifact artifact, Contribution contribution) throws IOException {
-        FileOutputStream fileOutputStream = null;
-        ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(new File(URI.create(contribution.getLocation()))));
-        try {
-            ZipEntry zipEntry = zipInputStream.getNextEntry();
-            while (zipEntry != null) {
-                if (artifact.getLocation().endsWith(zipEntry.getName())) {
-
-                    String tempName = ("tmp." + artifact.getURI().substring(0, artifact.getURI().length() - 3)).replace('/', '.');
-                    File tempFile = File.createTempFile(tempName, ".jar");
-                    tempFile.deleteOnExit();
-                    fileOutputStream = new FileOutputStream(tempFile);
-
-                    byte[] buf = new byte[2048];
-                    int n;
-                    while ((n = zipInputStream.read(buf, 0, buf.length)) > -1) {
-                        fileOutputStream.write(buf, 0, n);
-                    }
-
-                    fileOutputStream.close();
-                    zipInputStream.closeEntry();
-
-                    return tempFile.toURI().toURL();
-
-                }
-                zipEntry = zipInputStream.getNextEntry();
-            }
-        } finally {
-            zipInputStream.close();
-            if (fileOutputStream != null) {
-                fileOutputStream.close();
-            }
-        }
-        
-        throw new IllegalStateException();
-    }
 }
diff --git a/modules/contribution/src/main/java/org/apache/tuscany/sca/contribution/processor/impl/ContributionContentProcessor.java b/modules/contribution/src/main/java/org/apache/tuscany/sca/contribution/processor/impl/ContributionContentProcessor.java
index 58abc98..75e34bb 100644
--- a/modules/contribution/src/main/java/org/apache/tuscany/sca/contribution/processor/impl/ContributionContentProcessor.java
+++ b/modules/contribution/src/main/java/org/apache/tuscany/sca/contribution/processor/impl/ContributionContentProcessor.java
@@ -19,12 +19,19 @@
 package org.apache.tuscany.sca.contribution.processor.impl;
 
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.ListIterator;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
 
 import org.apache.tuscany.sca.assembly.Composite;
 import org.apache.tuscany.sca.contribution.Artifact;
@@ -154,7 +161,7 @@
                         } catch (MalformedURLException e) {
                             //ignore
                         }
-
+                        
                         Object model =
                             artifactProcessor.read(contributionURL,
                                                    URI.create(artifact.getURI()),
@@ -185,6 +192,21 @@
 
                 List<Artifact> contributionArtifacts = contribution.getArtifacts();
                 contributionArtifacts.addAll(artifacts);
+                
+                if (contribution.useNestedArchives() && !!!isWebApp(contribution)) {
+                    List<Artifact> nestedArchives = getNestedArchives(contribution);
+                    List<URL> nestedArchiveURLs;
+                    if (scanner instanceof DirectoryContributionScanner) {
+                        nestedArchiveURLs = getNestedArchiveURLs(nestedArchives, contribution.getLocation());
+                    } else {
+                        try {
+                            nestedArchiveURLs = extractNestedArchives(nestedArchives, contribution.getLocation());
+                        } catch (IOException e) {
+                            throw new ContributionReadException(e);
+                        }
+                    }
+                    processNestedArchives(nestedArchiveURLs, contribution, context, monitor);
+                }
 
                 // If no sca-contribution.xml file was provided then just consider
                 // all composites in the contribution as deployables
@@ -224,6 +246,126 @@
         return contribution;
     }
 
+    private List<URL> getNestedArchiveURLs(List<Artifact> nestedArchives, String location) throws ContributionReadException {
+        List<URL> urls = new ArrayList<URL>();
+        for (Artifact a : nestedArchives) {
+            try {
+                urls.add(new URL(a.getLocation()));
+            } catch (MalformedURLException e) {
+                throw new ContributionReadException(e);
+            }
+        }
+        return urls;
+    }
+
+    private boolean isWebApp(Contribution contribution) {
+        for (Artifact a : contribution.getArtifacts()) {
+            if (a.getURI().equalsIgnoreCase("web-inf/web.xml")) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void processNestedArchives(List<URL> nestedArchiveURLs, Contribution contribution, ProcessorContext context, Monitor monitor) throws ContributionReadException {
+        for (URL url : nestedArchiveURLs) {
+            Contribution tempC = contributionFactory.createContribution();
+            tempC.setURI("temp");
+            tempC.setLocation(url.toString());
+            JarContributionScanner scanner = new JarContributionScanner(contributionFactory);
+            List<Artifact> artifacts = scanner.scan(tempC);
+            for (Artifact artifact : artifacts) {
+                // Add the deployed artifact model to the contribution
+                contribution.getModelResolver().addModel(artifact, context);
+
+                monitor.pushContext("Artifact: " + artifact.getURI());
+
+                Artifact oldArtifact = context.setArtifact(artifact);
+                try {
+                    // Read each artifact
+                    URL artifactLocationURL = null;
+                    try {
+                        artifactLocationURL = new URL(artifact.getLocation());
+                    } catch (MalformedURLException e) {
+                        //ignore
+                    }
+                    
+                    Object model;
+                    try {
+                        model = artifactProcessor.read(new URL(contribution.getLocation()),  URI.create(artifact.getURI()), artifactLocationURL, context);
+                    } catch (MalformedURLException e) {
+                        throw new ContributionReadException(e);
+                    }
+                    if (model != null) {
+                        artifact.setModel(model);
+
+                        // Add the loaded model to the model resolver
+                        contribution.getModelResolver().addModel(model, context);
+
+                    }
+                } finally {
+                    monitor.popContext();
+                    context.setArtifact(oldArtifact);
+                }
+            }
+            contribution.getArtifacts().addAll(artifacts);
+            contribution.getExtractedArchives().add(url);
+        }
+    }
+
+    private List<Artifact> getNestedArchives(Contribution contribution) {
+        List<Artifact> nestedArchives = new ArrayList<Artifact>();
+        for (Artifact a : contribution.getArtifacts()) {
+            if (a.getURI().endsWith(".zip") || a.getURI().endsWith(".jar")) {
+                nestedArchives.add(a);
+            }
+        }
+        return nestedArchives;
+    }
+
+    private List<URL> extractNestedArchives(List<Artifact> nestedArchives, String contributionLocation) throws IOException {
+        if (nestedArchives.size() < 1) {
+            return Collections.emptyList();
+        }
+        List<URL> extractedArchiveURLs = new ArrayList<URL>();
+        FileOutputStream fileOutputStream = null;
+        ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(new File(URI.create(contributionLocation))));
+        try {
+            ZipEntry zipEntry = zipInputStream.getNextEntry();
+            while (zipEntry != null) {
+                for (Artifact artifact : nestedArchives) {
+                    if (artifact.getLocation().endsWith(zipEntry.getName())) {
+
+                        String tempName = ("tmp." + artifact.getURI().substring(0, artifact.getURI().length() - 3)).replace('/', '.');
+                        File tempFile = File.createTempFile(tempName, ".jar");
+                        tempFile.deleteOnExit();
+                        fileOutputStream = new FileOutputStream(tempFile);
+
+                        byte[] buf = new byte[4096];
+                        int n;
+                        while ((n = zipInputStream.read(buf, 0, buf.length)) > -1) {
+                            fileOutputStream.write(buf, 0, n);
+                        }
+
+                        fileOutputStream.close();
+                        zipInputStream.closeEntry();
+
+                        extractedArchiveURLs.add(tempFile.toURI().toURL());
+
+                    }
+                }
+                zipEntry = zipInputStream.getNextEntry();
+            }
+        } finally {
+            zipInputStream.close();
+            if (fileOutputStream != null) {
+                fileOutputStream.close();
+            }
+        }
+        
+        return extractedArchiveURLs;
+    }
+
     /**
      * A pre-resolution step, which is required for Contributions to handle the resolution of imports and exports so that
      * at resolve time, imports can be followed to exports and anything exported that is required can be resolved on demand
@@ -346,4 +488,46 @@
         } // end for
     } // end method resolveImports
 
+    
+    /**
+     * URLClassLoader doesn't seem to work with URLs to jars within an archive so as a work around
+     * copy the jar to a temp file and use the url to that.
+     */
+    private static URL createTempJar(Artifact artifact, Contribution contribution) throws IOException {
+        FileOutputStream fileOutputStream = null;
+        ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(new File(URI.create(contribution.getLocation()))));
+        try {
+            ZipEntry zipEntry = zipInputStream.getNextEntry();
+            while (zipEntry != null) {
+                if (artifact.getLocation().endsWith(zipEntry.getName())) {
+
+                    String tempName = ("tmp." + artifact.getURI().substring(0, artifact.getURI().length() - 3)).replace('/', '.');
+                    File tempFile = File.createTempFile(tempName, ".jar");
+                    tempFile.deleteOnExit();
+                    fileOutputStream = new FileOutputStream(tempFile);
+
+                    byte[] buf = new byte[2048];
+                    int n;
+                    while ((n = zipInputStream.read(buf, 0, buf.length)) > -1) {
+                        fileOutputStream.write(buf, 0, n);
+                    }
+
+                    fileOutputStream.close();
+                    zipInputStream.closeEntry();
+
+                    return tempFile.toURI().toURL();
+
+                }
+                zipEntry = zipInputStream.getNextEntry();
+            }
+        } finally {
+            zipInputStream.close();
+            if (fileOutputStream != null) {
+                fileOutputStream.close();
+            }
+        }
+        
+        throw new IllegalStateException();
+    }
+    
 } // end class ContributionContentProcessor