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"/>