JCRVLT-331 use DefaultArtifactRepositoryLayout for embedded filename (closes  #22)

git-svn-id: https://svn.apache.org/repos/asf/jackrabbit/commons/filevault-package-maven-plugin/trunk@1853772 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/GenerateMetadataMojo.java b/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/GenerateMetadataMojo.java
index 67ee2df..667c41b 100644
--- a/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/GenerateMetadataMojo.java
+++ b/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/GenerateMetadataMojo.java
@@ -17,6 +17,7 @@
 package org.apache.jackrabbit.filevault.maven.packaging;
 
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -35,10 +36,14 @@
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.jar.Manifest;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
+import aQute.bnd.header.Attrs;
+import aQute.bnd.header.Parameters;
+import aQute.bnd.osgi.Processor;
 import org.apache.jackrabbit.filevault.maven.packaging.impl.DependencyValidator;
 import org.apache.jackrabbit.filevault.maven.packaging.impl.PackageDependency;
 import org.apache.jackrabbit.vault.fs.api.PathFilterSet;
@@ -47,29 +52,29 @@
 import org.apache.jackrabbit.vault.fs.io.AccessControlHandling;
 import org.apache.jackrabbit.vault.packaging.PackageId;
 import org.apache.jackrabbit.vault.packaging.PackageType;
+import org.apache.jackrabbit.vault.util.Text;
 import org.apache.maven.archiver.ManifestConfiguration;
 import org.apache.maven.archiver.MavenArchiveConfiguration;
 import org.apache.maven.archiver.MavenArchiver;
 import org.apache.maven.artifact.Artifact;
 import org.apache.maven.artifact.DependencyResolutionRequiredException;
+import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
 import org.apache.maven.execution.MavenSession;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugin.descriptor.PluginDescriptor;
 import org.apache.maven.plugins.annotations.Component;
 import org.apache.maven.plugins.annotations.LifecyclePhase;
 import org.apache.maven.plugins.annotations.Mojo;
 import org.apache.maven.plugins.annotations.Parameter;
 import org.apache.maven.plugins.annotations.ResolutionScope;
+import org.apache.maven.project.MavenProject;
 import org.codehaus.plexus.archiver.jar.ManifestException;
 import org.codehaus.plexus.util.FileUtils;
 import org.codehaus.plexus.util.IOUtil;
 import org.codehaus.plexus.util.StringUtils;
 import org.sonatype.plexus.build.incremental.BuildContext;
 
-import aQute.bnd.header.Attrs;
-import aQute.bnd.header.Parameters;
-import aQute.bnd.osgi.Processor;
-
 /**
  * Maven goal which generates the metadata ending up in the package like {@code META-INF/MANIFEST.MF} as well as the
  * files ending up in {@code META-INF/vault} like {@code filter.xml}, {@code properties.xml}, {@code config.xml} and
@@ -78,7 +83,7 @@
  */
 @Mojo(
         name = "generate-metadata",
-        defaultPhase = LifecyclePhase.PREPARE_PACKAGE,
+        defaultPhase = LifecyclePhase.PROCESS_CLASSES,
         requiresDependencyResolution = ResolutionScope.COMPILE
 )
 public class GenerateMetadataMojo extends AbstractPackageMojo {
@@ -109,6 +114,12 @@
     private BuildContext buildContext;
 
     /**
+     * For correct source of standard embedded path base name.
+     */
+    @Component(hint = "default")
+    private ArtifactRepositoryLayout embedArtifactLayout;
+
+    /**
      * The Maven session.
      */
     @Parameter(property = "session", readonly = true, required = true)
@@ -410,6 +421,11 @@
         final File vaultDir = getVaultDir();
         vaultDir.mkdirs();
 
+        // JCRVLT-331 share work directory to expose vault metadata between process-classes and package phases for
+        // multi-module builds.
+        getArtifactWorkDirectoryLookup(getPluginContext())
+                .put(getModuleArtifactKey(project.getArtifact()), workDirectory);
+
         try {
             // calculate the embeddeds and subpackages
             Map<String, File> embeddedFiles = getEmbeddeds();
@@ -852,7 +868,32 @@
 
                 // todo: add support for patterns
                 if (destFileName == null) {
-                    destFileName = source.getName();
+                    // If the <destFileName> param is not specified...
+                    if (!source.isDirectory()) {
+                        // If the artifact file is not a directory, defer to File.getName().
+                        destFileName = source.getName();
+                    } else {
+                        // If the dependency file is a directory, the final artifact file has not yet been packaged.
+                        // Construct a fallback file name from the artifact coordinates.
+                        final String layoutBaseName = Text.getName(embedArtifactLayout.pathOf(artifact));
+                        // Look for a peer module in the session that the artifact is attached to.
+                        final MavenProject peerModule = findModuleForArtifact(artifact);
+                        if (peerModule != null) {
+                            // determine the finalName of the artifact, which is ${artifactId}-${version} by default.
+                            final Artifact attached = peerModule.getArtifact();
+                            final String defaultFinalName = attached.getArtifactId() + "-" + attached.getVersion();
+                            final String peerFinalName = peerModule.getBuild().getFinalName();
+                            if (peerFinalName != null) {
+                                // remove the default finalName from the beginning of the layout basename, and
+                                // prepend the specified finalName to create the destFileName.
+                                destFileName = peerFinalName + layoutBaseName.substring(defaultFinalName.length());
+                            }
+                        }
+                        // If destFileName is still null, fallback to layoutBaseName.
+                        if (destFileName == null) {
+                            destFileName = layoutBaseName;
+                        }
+                    }
                 }
                 final String targetPathName = targetPath + destFileName;
                 final String targetNodePathName = targetPathName.substring(JCR_ROOT.length() - 1);
@@ -868,7 +909,56 @@
         return fileMap;
     }
 
+    /**
+     * Establishes a session-shareable workDirectory lookup map for the given pluginContext.
+     *
+     * @param pluginContext a Map retrieved from {@link MavenSession#getPluginContext(PluginDescriptor, MavenProject)}.
+     * @return a lookup Map. The key is {@link Artifact#getId()} and value is {@link AbstractPackageMojo#workDirectory}.
+     */
+    @SuppressWarnings("unchecked")
+    static Map<String, File> getArtifactWorkDirectoryLookup(final Map pluginContext) {
+        final String workDirectoryLookupKey = "workDirectoryLookup";
+        if (!pluginContext.containsKey(workDirectoryLookupKey)) {
+            pluginContext.put(workDirectoryLookupKey, new ConcurrentHashMap<String, File>());
+        }
+        return (Map<String, File>) pluginContext.get(workDirectoryLookupKey);
+    }
+
+    /**
+     * Find the other project which produces the provided artifact.
+     *
+     * @param artifact the dependency artifact needle
+     * @return another project that is a dependency of thisProject
+     */
+    MavenProject findModuleForArtifact(final Artifact artifact) {
+        for (MavenProject otherProject : session.getProjects()) {
+            if (otherProject != this.project) {
+                final Artifact otherArtifact = otherProject.getArtifact();
+                if (getModuleArtifactKey(artifact).equals(getModuleArtifactKey(otherArtifact))) {
+                    return otherProject;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Construct a handler-independent artifact disambiguation key. This helps with the issue
+     * of matching dependency artifacts, which cannot reliably reference their original artifact handler to match the
+     * correct packaging type, to multimodule artifacts, which include the packaging type in their getId() result.
+     *
+     * I.E. {@link Artifact#getId()} contains either ":content-package:" or ":zip:" depending on whether it comes from
+     * this.session.getProjects() -> MavenProject#getArtifact() or from this.project.getArtifacts().
+     *
+     * @param artifact the module artifact ({@link MavenProject#getArtifact()}) to identify
+     * @return a handler-independent artifact disambiguation key
+     */
+    String getModuleArtifactKey(final Artifact artifact) {
+        return this.embedArtifactLayout.pathOf(artifact);
+    }
+
     private Map<String, File> getSubPackages() throws MojoFailureException {
+        final String propsRelPath = "META-INF/vault/properties.xml";
         Map<String, File> fileMap = new HashMap<>();
         for (SubPackage pack : subPackages) {
             final Collection<Artifact> artifacts = pack.getMatchingArtifacts(project);
@@ -880,22 +970,47 @@
             // get the package path
             getLog().info("Embedding --- " + pack + " ---");
             for (Artifact artifact : artifacts) {
-                final File source = artifact.getFile();
+                final Properties props = new Properties();
 
-                // load properties
-                InputStream in = null;
-                Properties props = new Properties();
-                try (ZipFile zip = new ZipFile(source, ZipFile.OPEN_READ)){
-                    ZipEntry e = zip.getEntry("META-INF/vault/properties.xml");
-                    if (e == null) {
-                        throw new IOException("Package does not contain 'META-INF/vault/properties.xml'");
+                final File source = artifact.getFile();
+                if (source.isDirectory()) {
+                    File otherWorkDirectory = null;
+                    final MavenProject otherProject = findModuleForArtifact(artifact);
+                    if (otherProject != null) {
+                        final PluginDescriptor pluginDescriptor = (PluginDescriptor) this.getPluginContext().get("pluginDescriptor");
+                        if (pluginDescriptor != null) {
+                            Map<String, Object> otherContext = this.session.getPluginContext(pluginDescriptor, otherProject);
+                            otherWorkDirectory = getArtifactWorkDirectoryLookup(otherContext).get(getModuleArtifactKey(artifact));
+                        }
                     }
-                    in = zip.getInputStream(e);
-                    props.loadFromXML(in);
-                } catch (IOException e) {
-                    throw new MojoFailureException("Could not open subpackage '" + source + "' to extract metadata: " + e.getMessage(), e);
-                } finally {
-                    IOUtil.close(in);
+
+                    // if not identifiable as a filevault content-package dependency, assume a generic archive layout.
+                    if (otherWorkDirectory == null) {
+                        otherWorkDirectory = source;
+                    }
+
+                    final File propsXml = new File(otherWorkDirectory, propsRelPath);
+                    try (InputStream input = new FileInputStream(propsXml)) {
+                        props.loadFromXML(input);
+                    } catch (IOException e) {
+                        throw new MojoFailureException("Could not read META-INF/vault/properties.xml from directory '" +
+                                otherWorkDirectory + "' to extract metadata: " + e.getMessage(), e);
+                    }
+                } else {
+                    // load properties
+                    InputStream in = null;
+                    try (ZipFile zip = new ZipFile(source, ZipFile.OPEN_READ)) {
+                        ZipEntry e = zip.getEntry(propsRelPath);
+                        if (e == null) {
+                            throw new IOException("Package does not contain 'META-INF/vault/properties.xml'");
+                        }
+                        in = zip.getInputStream(e);
+                        props.loadFromXML(in);
+                    } catch (IOException e) {
+                        throw new MojoFailureException("Could not open subpackage '" + source + "' to extract metadata: " + e.getMessage(), e);
+                    } finally {
+                        IOUtil.close(in);
+                    }
                 }
                 PackageId pid = new PackageId(
                         props.getProperty("group"),
diff --git a/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/it/GenerateMetadataMultiModuleIT.java b/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/it/GenerateMetadataMultiModuleIT.java
new file mode 100644
index 0000000..73bed6e
--- /dev/null
+++ b/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/it/GenerateMetadataMultiModuleIT.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.filevault.maven.packaging.it;
+
+import org.junit.Test;
+
+public class GenerateMetadataMultiModuleIT {
+
+    /**
+     * Tests that the generate-manifest goal generates the expected filter.xml when run on
+     * inter-module dependencies in a multi-module setup for clean + test goals.
+     */
+    @Test
+    public void multi_module_build_clean_test() throws Exception {
+        new ProjectBuilder()
+                .setTestProjectDir("/generate-metadata-multimodule")
+                .setTestGoals("clean", "test")
+                .setVerifyPackageContents(false)
+                .build()
+                .verifyExpectedFilterInWorkDirectory("container/target/vault-work");
+    }
+
+    /**
+     * Tests that the generate-manifest goal generates the expected filter.xml when run on
+     * inter-module dependencies in a multi-module setup for clean + test goals.
+     */
+    @Test
+    public void multi_module_build_clean_package() throws Exception {
+        new ProjectBuilder()
+                .setTestProjectDir("/generate-metadata-multimodule")
+                .setTestPackageFile("container/" + ProjectBuilder.TEST_PACKAGE_DEFAULT_NAME)
+                .setTestGoals("clean", "package")
+                .setVerifyPackageContents(false)
+                .build()
+                .verifyExpectedFilter();
+    }
+}
diff --git a/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/it/ProjectBuilder.java b/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/it/ProjectBuilder.java
index 07b3781..dedadce 100644
--- a/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/it/ProjectBuilder.java
+++ b/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/it/ProjectBuilder.java
@@ -169,6 +169,11 @@
         return this;
     }
 
+    public ProjectBuilder setTestPackageFile(String testPackageFileName) {
+        this.setTestPackageFile(new File(testProjectDir, testPackageFileName));
+        return this;
+    }
+
     public File getTestPackageFile() {
         return testPackageFile;
     }
@@ -334,6 +339,20 @@
         return this;
     }
 
+    public ProjectBuilder verifyExpectedFilterInWorkDirectory(final String workDirectory) throws IOException {
+        if (buildExpectedToFail) {
+            return this;
+        }
+        File workDirFile = new File(testProjectDir, workDirectory);
+        assertTrue("workDirectory should exist: " + workDirFile.toString(), workDirFile.isDirectory());
+        File filterFile = new File(workDirFile, "META-INF/vault/filter.xml");
+        assertTrue("filterFile should exist: " + filterFile.toString(), filterFile.isFile());
+        String result = FileUtils.fileRead(filterFile);
+        String expected = FileUtils.fileRead(expectedFilterFile);
+        assertEquals("filter.xml is incorrect", normalizeWhitespace(expected), normalizeWhitespace(result));
+        return this;
+    }
+
     public List<String> getBuildOutput() throws IOException {
         return Files.readAllLines(logTxtFile.toPath(), StandardCharsets.UTF_8);
     }
diff --git a/src/test/resources/test-projects/generate-metadata-multimodule/bundle1/pom.xml b/src/test/resources/test-projects/generate-metadata-multimodule/bundle1/pom.xml
new file mode 100644
index 0000000..da37eae
--- /dev/null
+++ b/src/test/resources/test-projects/generate-metadata-multimodule/bundle1/pom.xml
@@ -0,0 +1,41 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+  -->
+<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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.jackrabbit.filevault</groupId>
+        <artifactId>package-plugin-test-pkg-parent</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>package-plugin-test-pkg-bundle1</artifactId>
+    <packaging>bundle</packaging>
+
+    <name>Packaging test bundle artifact</name>
+
+    <build>
+        <finalName>bundle1</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <version>3.5.0</version>
+                <extensions>true</extensions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/src/test/resources/test-projects/generate-metadata-multimodule/bundle1/src/main/java/bundle1/SomeClass.java b/src/test/resources/test-projects/generate-metadata-multimodule/bundle1/src/main/java/bundle1/SomeClass.java
new file mode 100644
index 0000000..899b824
--- /dev/null
+++ b/src/test/resources/test-projects/generate-metadata-multimodule/bundle1/src/main/java/bundle1/SomeClass.java
@@ -0,0 +1,19 @@
+package bundle1;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+public class SomeClass {}
\ No newline at end of file
diff --git a/src/test/resources/test-projects/generate-metadata-multimodule/bundle2/pom.xml b/src/test/resources/test-projects/generate-metadata-multimodule/bundle2/pom.xml
new file mode 100644
index 0000000..46772c4
--- /dev/null
+++ b/src/test/resources/test-projects/generate-metadata-multimodule/bundle2/pom.xml
@@ -0,0 +1,40 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+  -->
+<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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.jackrabbit.filevault</groupId>
+        <artifactId>package-plugin-test-pkg-parent</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>package-plugin-test-pkg-bundle2</artifactId>
+    <packaging>bundle</packaging>
+
+    <name>Packaging test bundle artifact</name>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <version>3.5.0</version>
+                <extensions>true</extensions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/src/test/resources/test-projects/generate-metadata-multimodule/bundle2/src/main/java/bundle2/SomeClass.java b/src/test/resources/test-projects/generate-metadata-multimodule/bundle2/src/main/java/bundle2/SomeClass.java
new file mode 100644
index 0000000..4525ce5
--- /dev/null
+++ b/src/test/resources/test-projects/generate-metadata-multimodule/bundle2/src/main/java/bundle2/SomeClass.java
@@ -0,0 +1,19 @@
+package bundle2;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+public class SomeClass {}
\ No newline at end of file
diff --git a/src/test/resources/test-projects/generate-metadata-multimodule/bundle3/pom.xml b/src/test/resources/test-projects/generate-metadata-multimodule/bundle3/pom.xml
new file mode 100644
index 0000000..3e2de10
--- /dev/null
+++ b/src/test/resources/test-projects/generate-metadata-multimodule/bundle3/pom.xml
@@ -0,0 +1,40 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+  -->
+<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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.jackrabbit.filevault</groupId>
+        <artifactId>package-plugin-test-pkg-parent</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>package-plugin-test-pkg-bundle3</artifactId>
+    <packaging>bundle</packaging>
+
+    <name>Packaging test bundle artifact</name>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <version>3.5.0</version>
+                <extensions>true</extensions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/src/test/resources/test-projects/generate-metadata-multimodule/bundle3/src/main/java/bundle3/SomeClass.java b/src/test/resources/test-projects/generate-metadata-multimodule/bundle3/src/main/java/bundle3/SomeClass.java
new file mode 100644
index 0000000..d65b27e
--- /dev/null
+++ b/src/test/resources/test-projects/generate-metadata-multimodule/bundle3/src/main/java/bundle3/SomeClass.java
@@ -0,0 +1,19 @@
+package bundle3;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+public class SomeClass {}
\ No newline at end of file
diff --git a/src/test/resources/test-projects/generate-metadata-multimodule/container/pom.xml b/src/test/resources/test-projects/generate-metadata-multimodule/container/pom.xml
new file mode 100755
index 0000000..b8dbe51
--- /dev/null
+++ b/src/test/resources/test-projects/generate-metadata-multimodule/container/pom.xml
@@ -0,0 +1,101 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+  -->
+<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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.jackrabbit.filevault</groupId>
+        <artifactId>package-plugin-test-pkg-parent</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>package-plugin-test-pkg</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <packaging>content-package</packaging>
+    <name>Packaging test</name>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.jackrabbit</groupId>
+                <artifactId>filevault-package-maven-plugin</artifactId>
+                <version>${plugin.version}</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <embeddeds>
+                        <embedded>
+                            <groupId>${project.groupId}</groupId>
+                            <artifactId>package-plugin-test-pkg-bundle1</artifactId>
+                            <filter>true</filter>
+                        </embedded>
+                        <embedded>
+                            <groupId>${project.groupId}</groupId>
+                            <artifactId>package-plugin-test-pkg-bundle2</artifactId>
+                            <filter>true</filter>
+                        </embedded>
+                        <embedded>
+                            <groupId>${project.groupId}</groupId>
+                            <artifactId>package-plugin-test-pkg-bundle3</artifactId>
+                            <filter>true</filter>
+                            <destFileName>bundle3-core.jar</destFileName>
+                        </embedded>
+                    </embeddeds>
+                    <subPackages>
+                        <subPackage>
+                            <groupId>${project.groupId}</groupId>
+                            <artifactId>package-plugin-test-pkg-sub1</artifactId>
+                            <filter>true</filter>
+                        </subPackage>
+                        <subPackage>
+                            <groupId>${project.groupId}</groupId>
+                            <artifactId>package-plugin-test-pkg-sub2</artifactId>
+                            <filter>true</filter>
+                        </subPackage>
+                    </subPackages>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>package-plugin-test-pkg-bundle1</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>package-plugin-test-pkg-bundle2</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>package-plugin-test-pkg-bundle3</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>package-plugin-test-pkg-sub1</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+            <type>zip</type>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>package-plugin-test-pkg-sub2</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+            <type>zip</type>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/src/test/resources/test-projects/generate-metadata-multimodule/expected-filter.xml b/src/test/resources/test-projects/generate-metadata-multimodule/expected-filter.xml
new file mode 100644
index 0000000..f045c99
--- /dev/null
+++ b/src/test/resources/test-projects/generate-metadata-multimodule/expected-filter.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<workspaceFilter version="1.0">
+    <filter root="/apps/bundles/install/bundle1.jar"/>
+    <filter root="/apps/bundles/install/package-plugin-test-pkg-bundle2-1.0.0-SNAPSHOT.jar"/>
+    <filter root="/apps/bundles/install/bundle3-core.jar"/>
+    <filter root="/etc/packages/test/package-plugin-test-pkg-sub1-1.0.0-SNAPSHOT.zip"/>
+    <filter root="/etc/packages/test/package-plugin-test-pkg-sub2-1.0.0-SNAPSHOT.zip"/>
+</workspaceFilter>
diff --git a/src/test/resources/test-projects/generate-metadata-multimodule/pom.xml b/src/test/resources/test-projects/generate-metadata-multimodule/pom.xml
new file mode 100644
index 0000000..1c82e1d
--- /dev/null
+++ b/src/test/resources/test-projects/generate-metadata-multimodule/pom.xml
@@ -0,0 +1,36 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+  -->
+<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">
+    <modelVersion>4.0.0</modelVersion>
+
+    <!-- ====================================================================== -->
+    <!-- P R O J E C T  D E S C R I P T I O N                                   -->
+    <!-- ====================================================================== -->
+    <groupId>org.apache.jackrabbit.filevault</groupId>
+    <artifactId>package-plugin-test-pkg-parent</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <packaging>pom</packaging>
+    <name>Parent</name>
+    <modules>
+        <module>bundle1</module>
+        <module>bundle2</module>
+        <module>bundle3</module>
+        <module>sub1</module>
+        <module>sub2</module>
+        <module>container</module>
+    </modules>
+</project>
diff --git a/src/test/resources/test-projects/generate-metadata-multimodule/sub1/pom.xml b/src/test/resources/test-projects/generate-metadata-multimodule/sub1/pom.xml
new file mode 100755
index 0000000..a295dc9
--- /dev/null
+++ b/src/test/resources/test-projects/generate-metadata-multimodule/sub1/pom.xml
@@ -0,0 +1,52 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+  -->
+<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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.jackrabbit.filevault</groupId>
+        <artifactId>package-plugin-test-pkg-parent</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>package-plugin-test-pkg-sub1</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <packaging>content-package</packaging>
+    <name>Packaging test</name>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>src/main/content/META-INF</directory>
+                <targetPath>${project.build.directory}/vault-work/META-INF</targetPath>
+            </resource>
+            <resource>
+                <directory>src/main/content/jcr_root</directory>
+                <targetPath>.</targetPath>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.jackrabbit</groupId>
+                <artifactId>filevault-package-maven-plugin</artifactId>
+                <version>${plugin.version}</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <group>test</group>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/src/test/resources/test-projects/generate-metadata-multimodule/sub1/src/main/content/META-INF/vault/filter.xml b/src/test/resources/test-projects/generate-metadata-multimodule/sub1/src/main/content/META-INF/vault/filter.xml
new file mode 100644
index 0000000..f7ae6f2
--- /dev/null
+++ b/src/test/resources/test-projects/generate-metadata-multimodule/sub1/src/main/content/META-INF/vault/filter.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+  -->
+<workspaceFilter version="1.0">
+    <filter root="/sub1"/>
+</workspaceFilter>
diff --git a/src/test/resources/test-projects/generate-metadata-multimodule/sub1/src/main/content/jcr_root/sub1/.content.xml b/src/test/resources/test-projects/generate-metadata-multimodule/sub1/src/main/content/jcr_root/sub1/.content.xml
new file mode 100755
index 0000000..dcf03f8
--- /dev/null
+++ b/src/test/resources/test-projects/generate-metadata-multimodule/sub1/src/main/content/jcr_root/sub1/.content.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+  -->
+
+<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
+    jcr:primaryType="sling:Folder"/>
diff --git a/src/test/resources/test-projects/generate-metadata-multimodule/sub2/pom.xml b/src/test/resources/test-projects/generate-metadata-multimodule/sub2/pom.xml
new file mode 100755
index 0000000..18f83cf
--- /dev/null
+++ b/src/test/resources/test-projects/generate-metadata-multimodule/sub2/pom.xml
@@ -0,0 +1,52 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+  -->
+<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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.jackrabbit.filevault</groupId>
+        <artifactId>package-plugin-test-pkg-parent</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>package-plugin-test-pkg-sub2</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <packaging>content-package</packaging>
+    <name>Packaging test</name>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>src/main/content/META-INF</directory>
+                <targetPath>${project.build.directory}/vault-work/META-INF</targetPath>
+            </resource>
+            <resource>
+                <directory>src/main/content/jcr_root</directory>
+                <targetPath>.</targetPath>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.jackrabbit</groupId>
+                <artifactId>filevault-package-maven-plugin</artifactId>
+                <version>${plugin.version}</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <group>test</group>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/src/test/resources/test-projects/generate-metadata-multimodule/sub2/src/main/content/META-INF/vault/filter.xml b/src/test/resources/test-projects/generate-metadata-multimodule/sub2/src/main/content/META-INF/vault/filter.xml
new file mode 100644
index 0000000..1d511b4
--- /dev/null
+++ b/src/test/resources/test-projects/generate-metadata-multimodule/sub2/src/main/content/META-INF/vault/filter.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+  -->
+<workspaceFilter version="1.0">
+    <filter root="/sub2"/>
+</workspaceFilter>
diff --git a/src/test/resources/test-projects/generate-metadata-multimodule/sub2/src/main/content/jcr_root/sub2/.content.xml b/src/test/resources/test-projects/generate-metadata-multimodule/sub2/src/main/content/jcr_root/sub2/.content.xml
new file mode 100755
index 0000000..dcf03f8
--- /dev/null
+++ b/src/test/resources/test-projects/generate-metadata-multimodule/sub2/src/main/content/jcr_root/sub2/.content.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+  -->
+
+<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
+    jcr:primaryType="sling:Folder"/>