SLING-8649 - Missing dependencies when installing computed
content-packages

improved management in the transitive dependencies chain
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverter.java b/src/main/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverter.java
index 53bf1a2..09abfcd 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverter.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverter.java
@@ -18,6 +18,7 @@
 
 import static java.util.Objects.requireNonNull;
 import static org.apache.sling.feature.cpconverter.vltpkg.VaultPackageUtils.detectPackageType;
+import static org.apache.sling.feature.cpconverter.vltpkg.VaultPackageUtils.getDependencies;
 
 import java.io.File;
 import java.util.Collection;
@@ -65,7 +66,7 @@
 
     private final List<VaultPackageAssembler> assemblers = new LinkedList<>();
 
-    private final Set<PackageId> mutableContentsIds = new HashSet<>();
+    private final Map<PackageId, Set<Dependency>> mutableContentsIds = new LinkedHashMap<>();
 
     private EntryHandlersManager handlersManager;
 
@@ -201,6 +202,10 @@
 
                 traverse(vaultPackage);
 
+                // make sure 
+
+                mainPackageAssembler.updateDependencies(mutableContentsIds);
+
                 // attach all unmatched resources as new content-package
 
                 File contentPackageArchive = mainPackageAssembler.createPackage();
@@ -266,7 +271,7 @@
 
         emitter.startSubPackage(path, vaultPackage);
 
-        PackageId packageId = vaultPackage.getId();
+        PackageId originalPackageId = vaultPackage.getId();
         ArtifactId mvnPackageId = toArtifactId(vaultPackage);
         VaultPackageAssembler clonedPackage = VaultPackageAssembler.create(vaultPackage);
 
@@ -279,10 +284,12 @@
         // scan the detected package, first
         traverse(vaultPackage);
 
+        clonedPackage.updateDependencies(mutableContentsIds);
+
         File contentPackageArchive = clonedPackage.createPackage();
 
         // deploy the new content-package to the local mvn bundles dir and attach it to the feature
-        processContentPackageArchive(contentPackageArchive, mvnPackageId, packageId);
+        processContentPackageArchive(contentPackageArchive, mvnPackageId, originalPackageId);
 
         // restore the previous assembler
         mainPackageAssembler = handler;
@@ -292,7 +299,7 @@
 
     private void processContentPackageArchive(File contentPackageArchive,
                                               ArtifactId mvnPackageId,
-                                              PackageId packageId) throws Exception {
+                                              PackageId originalPackageId) throws Exception {
         try (VaultPackage vaultPackage = open(contentPackageArchive)) {
             PackageType packageType = detectPackageType(vaultPackage);
             // don't deploy & add content-packages of type content to featuremodel if dropContent is set
@@ -301,8 +308,9 @@
                 artifactsDeployer.deploy(new FileArtifactWriter(contentPackageArchive), mvnPackageId);
                 featuresManager.addArtifact(null, mvnPackageId);
             } else {
-                mutableContentsIds.add(packageId);
-                logger.info("Dropping package of PackageType.CONTENT {} (content-package id: {})", mvnPackageId.getArtifactId(), packageId);
+                mutableContentsIds.put(originalPackageId, getDependencies(vaultPackage));
+                logger.info("Dropping package of PackageType.CONTENT {} (content-package id: {})",
+                            mvnPackageId.getArtifactId(), originalPackageId);
             }
         }
     }
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/VaultPackageAssembler.java b/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/VaultPackageAssembler.java
index 763a4b8..96ddfff 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/VaultPackageAssembler.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/VaultPackageAssembler.java
@@ -24,6 +24,8 @@
 import static org.apache.jackrabbit.vault.util.Constants.ROOT_DIR;
 import static org.apache.jackrabbit.vault.util.Constants.SETTINGS_XML;
 import static org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter.PACKAGE_CLASSIFIER;
+import static org.apache.sling.feature.cpconverter.vltpkg.VaultPackageUtils.getDependencies;
+import static org.apache.sling.feature.cpconverter.vltpkg.VaultPackageUtils.setDependencies;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -31,12 +33,10 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.util.HashSet;
+import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
-import java.util.StringTokenizer;
 import java.util.regex.Pattern;
-import java.util.stream.Collectors;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.jackrabbit.vault.fs.api.ImportMode;
@@ -45,6 +45,7 @@
 import org.apache.jackrabbit.vault.fs.config.DefaultWorkspaceFilter;
 import org.apache.jackrabbit.vault.fs.io.Archive;
 import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
+import org.apache.jackrabbit.vault.packaging.Dependency;
 import org.apache.jackrabbit.vault.packaging.PackageId;
 import org.apache.jackrabbit.vault.packaging.PackageProperties;
 import org.apache.jackrabbit.vault.packaging.VaultPackage;
@@ -56,8 +57,6 @@
 
 public class VaultPackageAssembler implements EntryHandler {
 
-    private static final String DEPENDENCIES_DELIMITER = ",";
-
     private static final String NAME_PATH = "path";
 
     private static final String[] INCLUDE_RESOURCES = { PACKAGE_DEFINITION_XML, CONFIG_XML, SETTINGS_XML };
@@ -110,19 +109,7 @@
             }
         }
 
-        Set<PackageId> dependencies = new HashSet<>();
-        String dependenciesString = properties.getProperty(PackageProperties.NAME_DEPENDENCIES);
-
-        if (dependenciesString != null && !dependenciesString.isEmpty()) {
-         // from https://jackrabbit.apache.org/filevault/properties.html
-            // Comma-separated list of dependencies
-            StringTokenizer tokenizer = new StringTokenizer(dependenciesString, DEPENDENCIES_DELIMITER);
-            while (tokenizer.hasMoreTokens()) {
-                String dependencyString = tokenizer.nextToken();
-                PackageId dependency = PackageId.fromString(dependencyString);
-                dependencies.add(dependency);
-            }
-        }
+        Set<Dependency> dependencies = getDependencies(vaultPackage);
 
         VaultPackageAssembler assembler = new VaultPackageAssembler(storingDirectory, properties, dependencies);
         assembler.mergeFilters(filter);
@@ -131,7 +118,7 @@
 
     private final DefaultWorkspaceFilter filter = new DefaultWorkspaceFilter();
 
-    private final Set<PackageId> dependencies;
+    private final Set<Dependency> dependencies;
 
     private final File storingDirectory;
 
@@ -151,7 +138,7 @@
     /**
      * This class can not be instantiated from outside
      */
-    private VaultPackageAssembler(File storingDirectory, Properties properties, Set<PackageId> dependencies) {
+    private VaultPackageAssembler(File storingDirectory, Properties properties, Set<Dependency> dependencies) {
         this.storingDirectory = storingDirectory;
         this.properties = properties;
         this.dependencies = dependencies;
@@ -197,8 +184,15 @@
         return new File(storingDirectory, path);
     }
 
-    public void removeDependencies(Set<PackageId> mutableContentsIds) {
-        dependencies.removeAll(mutableContentsIds);
+    public void updateDependencies(Map<PackageId, Set<Dependency>> mutableContentsIds) {
+        for (Dependency dependency : dependencies) {
+            for (java.util.Map.Entry<PackageId, Set<Dependency>> mutableContentId : mutableContentsIds.entrySet()) {
+                if (dependency.matches(mutableContentId.getKey())) {
+                    dependencies.remove(dependency);
+                    dependencies.addAll(mutableContentId.getValue());
+                }
+            }
+        }
     }
 
     public File createPackage() throws IOException {
@@ -213,10 +207,7 @@
             metaDir.mkdirs();
         }
 
-        if (!dependencies.isEmpty()) {
-            String dependenciesString = dependencies.stream().map(id -> id.toString()).collect(Collectors.joining(DEPENDENCIES_DELIMITER));
-            properties.setProperty(PackageProperties.NAME_DEPENDENCIES, dependenciesString);
-        }
+        setDependencies(dependencies, properties);
 
         File xmlProperties = new File(metaDir, PROPERTIES_XML);
 
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/VaultPackageUtils.java b/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/VaultPackageUtils.java
index e8b4fee..af6d8f1 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/VaultPackageUtils.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/vltpkg/VaultPackageUtils.java
@@ -16,13 +16,23 @@
  */
 package org.apache.sling.feature.cpconverter.vltpkg;
 
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+import java.util.stream.Collectors;
+
 import org.apache.jackrabbit.vault.fs.api.PathFilterSet;
 import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
+import org.apache.jackrabbit.vault.packaging.Dependency;
+import org.apache.jackrabbit.vault.packaging.PackageProperties;
 import org.apache.jackrabbit.vault.packaging.PackageType;
 import org.apache.jackrabbit.vault.packaging.VaultPackage;
 
 public class VaultPackageUtils {
 
+    private static final String DEPENDENCIES_DELIMITER = ",";
+
     private VaultPackageUtils() {
         // this class must not be instantiated from outside
     }
@@ -62,4 +72,25 @@
         return PackageType.MIXED;
     }
 
+    public static Set<Dependency> getDependencies(VaultPackage vaultPackage) {
+        Dependency[] originalDepenencies = vaultPackage.getDependencies();
+
+        Set<Dependency> dependencies = new HashSet<>();
+
+        if (originalDepenencies != null && originalDepenencies.length > 0) {
+            dependencies.addAll(Arrays.asList(originalDepenencies));
+        }
+
+        return dependencies;
+    }
+
+    public static void setDependencies(Set<Dependency> dependencies, Properties properties) {
+        if (dependencies == null || dependencies.isEmpty()) {
+            return;
+        }
+
+        String dependenciesString = dependencies.stream().map(d -> d.toString()).collect(Collectors.joining(DEPENDENCIES_DELIMITER));
+        properties.setProperty(PackageProperties.NAME_DEPENDENCIES, dependenciesString);
+    }
+
 }
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverterTest.java b/src/test/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverterTest.java
index 4e5d85e..ff75514 100644
--- a/src/test/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverterTest.java
+++ b/src/test/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverterTest.java
@@ -556,7 +556,10 @@
     // see SLING-8649
     @Test
     public void filteredOutContentPackagesAreExcludedDependencies() throws Exception {
-        File[] contentPackages = load("test_dep_a-1.0.zip", "test_dep_b-1.0.zip");
+        File[] contentPackages = load("test_dep_a-1.0.zip", "test_dep_b-1.0.zip", "test_dep_b-1.0.zip");
+
+        // input: c <- a <- b
+        // expected output: c <- a
 
         File outputDirectory = new File(System.getProperty("java.io.tmpdir"), getClass().getName() + '_' + System.currentTimeMillis());
 
@@ -576,8 +579,8 @@
 
         File contentPackage = new File(outputDirectory, "my_packages/test_b/1.0/test_b-1.0-cp2fm-converted.zip");
         VaultPackage vaultPackage = new PackageManagerImpl().open(contentPackage);
-        String depencies = vaultPackage.getProperties().getProperty(PackageProperties.NAME_DEPENDENCIES);
-        assertNull(depencies);
+        String dependencies = vaultPackage.getProperties().getProperty(PackageProperties.NAME_DEPENDENCIES);
+        assertEquals("my_packages:test_c", dependencies);
     }
 
     private File[] load(String...resources) {
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/test_dep_a-1.0.zip b/src/test/resources/org/apache/sling/feature/cpconverter/test_dep_a-1.0.zip
index 4d9cd27..fd85645 100644
--- a/src/test/resources/org/apache/sling/feature/cpconverter/test_dep_a-1.0.zip
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/test_dep_a-1.0.zip
Binary files differ
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/test_dep_c-1.0.zip b/src/test/resources/org/apache/sling/feature/cpconverter/test_dep_c-1.0.zip
new file mode 100644
index 0000000..3d4698e
--- /dev/null
+++ b/src/test/resources/org/apache/sling/feature/cpconverter/test_dep_c-1.0.zip
Binary files differ