SLING-8390 - Converter not handling serviceusers and acls spread across
multiple packages

ordering of packages
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncher.java b/src/main/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncher.java
index 29090c8..f0e811c 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncher.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncher.java
@@ -17,9 +17,20 @@
 package org.apache.sling.feature.cpconverter.cli;
 
 import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.TimeZone;
 
+import org.apache.jackrabbit.vault.packaging.CyclicDependencyException;
+import org.apache.jackrabbit.vault.packaging.Dependency;
+import org.apache.jackrabbit.vault.packaging.PackageId;
+import org.apache.jackrabbit.vault.packaging.impl.ZipVaultPackage;
 import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -114,7 +125,7 @@
                 }
             }
 
-            List<File> orderedContentPackages = order(contentPackages);
+            List<File> orderedContentPackages = order(contentPackages, logger);
 
             for (File contentPackage : orderedContentPackages) {
                 converter.convert(contentPackage);
@@ -141,9 +152,51 @@
         logger.info( "+-----------------------------------------------------+" );
     }
 
-    private List<File> order(List<File> contentPackages) {
-        // TODO
-        return null;
+    protected List<File> order(List<File> contentPackages, final Logger logger) throws CyclicDependencyException {
+        
+        LinkedHashMap<PackageId, File> idFileMap = new LinkedHashMap<>();
+        
+        Map<ZipVaultPackage, File> packageFileMapping = new HashMap<>();
+        Map<PackageId, ZipVaultPackage> idPackageMapping = new HashMap<>();
+        
+        for (File file : contentPackages) {
+            try {
+                ZipVaultPackage pack = new ZipVaultPackage(file, false, true);
+                packageFileMapping.put(pack, file);
+                idPackageMapping.put(pack.getId(), pack);
+            } catch (IOException e) {
+                String msg = String.format("Package couldn't be parsed as ZipVaultPackage %s", file.getAbsolutePath());
+                logger.error(msg);
+                System.exit(1);
+            }
+        }
+        
+
+        for (ZipVaultPackage pack : packageFileMapping.keySet()) {
+            orderDependencies(idFileMap, packageFileMapping, idPackageMapping, pack, new HashSet<PackageId>());
+        }
+        
+        return new LinkedList<>(idFileMap.values());
+    }
+
+    private void orderDependencies(LinkedHashMap<PackageId, File> idFileMap,
+            Map<ZipVaultPackage, File> packageFileMapping, Map<PackageId, ZipVaultPackage> idPackageMapping,
+            ZipVaultPackage pack, Set<PackageId> visited) throws CyclicDependencyException {
+        if(visited.contains(pack.getId())) {
+            throw new CyclicDependencyException();
+        }
+        visited.add(pack.getId());
+        Dependency[] deps = pack.getDependencies();
+        for (Dependency dep : deps) {
+            for (PackageId id : new HashSet<>(idPackageMapping.keySet())) {
+                if (dep.matches(id)) {
+                    orderDependencies(idFileMap, packageFileMapping, idPackageMapping, idPackageMapping.get(id), visited);
+                    break;
+                }
+            }
+        }
+        idFileMap.put(pack.getId(), packageFileMapping.get(pack));
+        idPackageMapping.remove(pack.getId());
     }
 
     private static void printVersion(final Logger logger) {
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncherTest.java b/src/test/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncherTest.java
new file mode 100644
index 0000000..24f4696
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncherTest.java
@@ -0,0 +1,61 @@
+package org.apache.sling.feature.cpconverter.cli;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.jackrabbit.vault.packaging.CyclicDependencyException;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ContentPackage2FeatureModelConverterLauncherTest {
+    
+    /**
+     * Test package A-1.0. Depends on B and C-1.X
+     * Test package B-1.0. Depends on C
+     */
+    private static String[] TEST_PACKAGES_INPUT = {"test_c-1.0.zip","test_a-1.0.zip","test_b-1.0.zip"}; 
+    private static String[] TEST_PACKAGES_OUTPUT = {"test_c-1.0.zip","test_b-1.0.zip","test_a-1.0.zip"}; 
+    
+    private static String[] TEST_PACKAGES_CYCLIC_DEPENDENCY = {"test_d-1.0.zip","test_c-1.0.zip","test_a-1.0.zip","test_b-1.0.zip","test_e-1.0.zip"}; 
+    
+
+    @Test
+    public void testPackageOrdering() throws CyclicDependencyException {
+        ContentPackage2FeatureModelConverterLauncher launcher = new ContentPackage2FeatureModelConverterLauncher();
+        Logger logger = LoggerFactory.getLogger("test");
+        List<File> contentPackages = new ArrayList<File>();
+        
+        for (String pkgName : TEST_PACKAGES_INPUT) {
+            URL packageUrl = getClass().getResource(pkgName);
+            contentPackages.add(FileUtils.toFile(packageUrl));
+        }
+        List<File> ordered = launcher.order(contentPackages, logger);
+        Iterator<File> fileIt = ordered.iterator();
+        for (String expected : TEST_PACKAGES_OUTPUT) {
+            File next = fileIt.next();
+            assertEquals(expected, next.getName());
+        }
+        
+    }
+    
+    @Test(expected = CyclicDependencyException.class)
+    public void testDependencyCycle() throws CyclicDependencyException {
+        ContentPackage2FeatureModelConverterLauncher launcher = new ContentPackage2FeatureModelConverterLauncher();
+        Logger logger = LoggerFactory.getLogger("test");
+        List<File> contentPackages = new ArrayList<File>();
+        
+        for (String pkgName : TEST_PACKAGES_CYCLIC_DEPENDENCY) {
+            URL packageUrl = getClass().getResource(pkgName);
+            contentPackages.add(FileUtils.toFile(packageUrl));
+        }
+        launcher.order(contentPackages, logger);
+    }
+
+}
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/cli/test_a-1.0.zip b/src/test/resources/org/apache/sling/feature/cpconverter/cli/test_a-1.0.zip
new file mode 100644
index 0000000..08df03a
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/cli/test_a-1.0.zip
Binary files differ
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/cli/test_b-1.0.zip b/src/test/resources/org/apache/sling/feature/cpconverter/cli/test_b-1.0.zip
new file mode 100644
index 0000000..85fac13
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/cli/test_b-1.0.zip
Binary files differ
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/cli/test_c-1.0.zip b/src/test/resources/org/apache/sling/feature/cpconverter/cli/test_c-1.0.zip
new file mode 100644
index 0000000..245291a
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/cli/test_c-1.0.zip
Binary files differ
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/cli/test_d-1.0.zip b/src/test/resources/org/apache/sling/feature/cpconverter/cli/test_d-1.0.zip
new file mode 100644
index 0000000..7acf3d1
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/cli/test_d-1.0.zip
Binary files differ
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/cli/test_e-1.0.zip b/src/test/resources/org/apache/sling/feature/cpconverter/cli/test_e-1.0.zip
new file mode 100644
index 0000000..372bbb9
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/cli/test_e-1.0.zip
Binary files differ