You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by kw...@apache.org on 2022/03/09 17:55:18 UTC
[jackrabbit-filevault-package-maven-plugin] branch master updated: JCRVLT-558 add mojo for generating a CND (#69)
This is an automated email from the ASF dual-hosted git repository.
kwin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jackrabbit-filevault-package-maven-plugin.git
The following commit(s) were added to refs/heads/master by this push:
new 386895f JCRVLT-558 add mojo for generating a CND (#69)
386895f is described below
commit 386895f166b9215dbae21d8e9fd0cc131bff6853
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Wed Mar 9 18:55:15 2022 +0100
JCRVLT-558 add mojo for generating a CND (#69)
---
pom.xml | 4 +-
.../AbstractSourceAndMetadataPackageMojo.java | 5 +-
.../filevault/maven/packaging/GenerateCndMojo.java | 274 +++++++++++++++++++++
.../maven/packaging/GenerateCndMojoTest.java | 88 +++++++
.../maven/packaging/GenerateMetadataMojoTest.java | 2 -
.../maven/packaging/it/CndGenerationIT.java | 63 +++++
.../maven/packaging/it/util/ProjectBuilder.java | 14 ++
.../filevault/maven/packaging/cq-nodetypes.cnd | 91 +++++++
.../apps/test/base/components/.content.xml | 19 ++
.../assetselection/clientlib/.content.xml | 24 ++
.../test/base/components/container/.content.xml | 22 ++
.../jcr_root/apps/test/base/components/test.txt | 17 ++
.../apps/test/base/components/.content.xml | 19 ++
.../jcr_root/apps/test/base/components/test.txt | 17 ++
.../test-projects/cnd-generation/simple/pom.xml | 52 ++++
15 files changed, 705 insertions(+), 6 deletions(-)
diff --git a/pom.xml b/pom.xml
index d5eb8e5..def122b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,7 +24,7 @@
<groupId>org.apache.jackrabbit.vault</groupId>
<artifactId>parent</artifactId>
<relativePath />
- <version>3.5.9-SNAPSHOT</version>
+ <version>3.6.0</version>
</parent>
<!-- ====================================================================== -->
@@ -32,7 +32,7 @@
<!-- ====================================================================== -->
<groupId>org.apache.jackrabbit</groupId>
<artifactId>filevault-package-maven-plugin</artifactId>
- <version>1.2.3-SNAPSHOT</version>
+ <version>1.3.0-SNAPSHOT</version>
<packaging>maven-plugin</packaging>
<name>Apache Jackrabbit FileVault - Package Maven Plugin</name>
diff --git a/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/AbstractSourceAndMetadataPackageMojo.java b/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/AbstractSourceAndMetadataPackageMojo.java
index 25bae89..f66a10d 100644
--- a/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/AbstractSourceAndMetadataPackageMojo.java
+++ b/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/AbstractSourceAndMetadataPackageMojo.java
@@ -22,6 +22,7 @@ import java.util.Set;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.shared.utils.io.DirectoryScanner;
+import org.jetbrains.annotations.Nullable;
/**
* Commons ancestor for all mojos dealing with package source files and meta data files
@@ -90,11 +91,11 @@ public abstract class AbstractSourceAndMetadataPackageMojo extends AbstractMetad
@Parameter(defaultValue = "true")
protected boolean addDefaultExcludes;
- protected File getJcrSourceDirectory() {
+ protected @Nullable File getJcrSourceDirectory() {
return getJcrSourceDirectory(jcrRootSourceDirectory, builtContentDirectory, getLog());
}
- protected static File getJcrSourceDirectory(File[] jcrRootSourceDirectory, File builtContentDirectory, Log log) {
+ protected static @Nullable File getJcrSourceDirectory(File[] jcrRootSourceDirectory, File builtContentDirectory, Log log) {
final File jcrSourceDirectory;
if (builtContentDirectory != null) {
log.warn("The 'builtContentDirectory' is deprecated. Please use the new 'jcrRootSourceDirectory' instead.");
diff --git a/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/GenerateCndMojo.java b/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/GenerateCndMojo.java
new file mode 100644
index 0000000..0d0f738
--- /dev/null
+++ b/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/GenerateCndMojo.java
@@ -0,0 +1,274 @@
+/*
+ * 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;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.jcr.NamespaceException;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.commons.cnd.ParseException;
+import org.apache.jackrabbit.jcr2spi.nodetype.NodeTypeDefinitionProvider;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.QNodeDefinition;
+import org.apache.jackrabbit.spi.QNodeTypeDefinition;
+import org.apache.jackrabbit.spi.commons.conversion.IllegalNameException;
+import org.apache.jackrabbit.spi.commons.conversion.NameResolver;
+import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
+import org.apache.jackrabbit.spi.commons.nodetype.compact.CompactNodeTypeDefWriter;
+import org.apache.jackrabbit.vault.fs.io.DocViewParser;
+import org.apache.jackrabbit.vault.fs.io.DocViewParser.XmlParseException;
+import org.apache.jackrabbit.vault.fs.io.DocViewParserHandler;
+import org.apache.jackrabbit.vault.util.Constants;
+import org.apache.jackrabbit.vault.util.DocViewNode2;
+import org.apache.jackrabbit.vault.util.StandaloneManagerProvider;
+import org.apache.jackrabbit.vault.validation.spi.util.classloaderurl.CndUtil;
+import org.apache.jackrabbit.vault.validation.spi.util.classloaderurl.URLFactory;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+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.jetbrains.annotations.NotNull;
+import org.xml.sax.InputSource;
+
+/**
+ * Generates a <a href="https://jackrabbit.apache.org/jcr/node-type-notation.html">CND file</a> containing all
+ * used node types and namespaces. It uses the <a href="https://s.apache.org/jcr-2.0-spec/3_Repository_Model.html#3.7.11%20Standard%20Application%20Node%20Types">default namespaces and node types</a>
+ * and in addition some provided ones as source node type and namespace registry.
+ * From those only the ones being really leveraged in the package end up in the generated CND.
+ * The generated CND will end up in the <a href="https://jackrabbit.apache.org/filevault/nodetypes.html">package metadata</a> and
+ * all contained namespaces and node types are automatically registered during installation (in case they are not yet registered).
+ * @since 1.3.0
+ */
+@Mojo(
+ name = "generate-cnd",
+ defaultPhase = LifecyclePhase.GENERATE_SOURCES,
+ requiresDependencyResolution = ResolutionScope.COMPILE,
+ threadSafe = true
+)
+public class GenerateCndMojo extends AbstractSourceAndMetadataPackageMojo {
+ /**
+ * List of URLs pointing to a <a href="https://jackrabbit.apache.org/jcr/node-type-notation.html">CND</a>
+ * which define the additional namespaces and node types potentially used in this package apart from the
+ * <a href="https://s.apache.org/jcr-2.0-spec/3_Repository_Model.html#3.7.11%20Standard%20Application%20Node%20Types">default ones defined in JCR 2.0</a>.
+ * If a URI is pointing to a JAR it will leverage all the node types being mentioned in the
+ * <a href="https://sling.apache.org/documentation/bundles/content-loading-jcr-contentloader.html#declared-node-type-registration">{@code Sling-Nodetypes} manifest header</a>.
+ * part from the <a href="https://docs.oracle.com/javase/8/docs/api/java/net/URL.html#URL-java.lang.String-java.lang.String-int-java.lang.String-"standard protocols</a> the scheme {@code tccl}
+ * can be used to reference names from the <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#getContextClassLoader--">Thread's context class loader</a>.
+ * In the Maven plugin context this is the <a href="http://maven.apache.org/guides/mini/guide-maven-classloading.html?ref=driverlayer.com/web#3-plugin-classloaders">plugin classloader.
+ */
+ @Parameter(property = "vault.inputCndUrls")
+ List<String> additionalInputCndUrls = new LinkedList<>();
+
+ public GenerateCndMojo() {
+
+ }
+
+ @Override
+ public void execute() throws MojoExecutionException, MojoFailureException {
+ getLog().info("Retrieving used node types and namespaces...");
+ try {
+ StandaloneManagerProvider managerProvider = new StandaloneManagerProvider();
+ URLFactory.processUrlStreams(CndUtil.resolveJarUrls(additionalInputCndUrls), t -> {
+ try {
+ managerProvider.registerNodeTypes(t);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ catch (ParseException | RepositoryException e) {
+ throw new IllegalArgumentException(e);
+ }
+ });
+ File cndOutputFile = new File(getGeneratedVaultDir(true), Constants.NODETYPES_CND);
+ File jcrSourceDirectory = getJcrSourceDirectory();
+ if (jcrSourceDirectory == null) {
+ getLog().warn("None of the configured jcrRootSourceDirectory directories exists, skipping generating the CND!");
+ } else {
+ int numWrittenNodeTypes = generateCnd(managerProvider, cndOutputFile.toPath(), getJcrSourceDirectory().toPath());
+ getLog().info("Written " + numWrittenNodeTypes + " node types to CND file " + getProjectRelativeFilePath(cndOutputFile));
+ }
+ } catch (IOException | RepositoryException | ParseException | IllegalStateException e) {
+ throw new MojoExecutionException("Error while writing CND: " + e.toString(), e);
+ }
+ }
+
+ public int generateCnd(StandaloneManagerProvider managerProvider, Path cndOutputFile, Path jcrSourceDirectory) throws IOException, RepositoryException, ParseException {
+ DocViewParser docViewParser = new DocViewParser(managerProvider.getNamespaceResolver());
+ // traverse relevant package files
+ final Set<String> nodeTypes;
+ try {
+ nodeTypes = collectNodeTypes(jcrSourceDirectory, docViewParser);
+ } catch (UncheckedIOException e) {
+ throw e.getCause();
+ }
+ getLog().info("Found " + nodeTypes.size() + " unique node types" );
+ Collection<? extends QNodeTypeDefinition> ntDefinitons = resolveNodeTypes(nodeTypes, managerProvider.getNameResolver(),
+ managerProvider.getNodeTypeDefinitionProvider());
+
+ // make sure parent directories exist
+ Files.createDirectories(cndOutputFile.getParent());
+ // writes the CND into the given file
+ try (Writer writer = Files.newBufferedWriter(cndOutputFile, StandardCharsets.US_ASCII)) {
+ return writeCnd(ntDefinitons, managerProvider.getNodeTypeDefinitionProvider(), managerProvider.getNamespaceResolver(), writer);
+ }
+ }
+
+ private Set<String> collectNodeTypes(Path jcrRootPath, DocViewParser docViewParser) throws IOException {
+ Set<String> nodeTypes = new HashSet<>();
+ // add default ones for simple file aggregates: https://jackrabbit.apache.org/filevault/vaultfs.html#simple-file-aggregates
+ nodeTypes.add(JcrConstants.NT_FILE);
+ nodeTypes.add(JcrConstants.NT_FOLDER);
+
+ NodeTypeCollectorHandler nodeTypeCollectorHandler = new NodeTypeCollectorHandler(nodeTypes);
+ // extract types from docview files
+ try (Stream<Path> filePaths = Files.find(jcrRootPath, 50, (path, attributes) -> !attributes.isDirectory() && path.getFileName().toString().endsWith(".xml"))) {
+ filePaths.forEach(p -> {
+ nodeTypeCollectorHandler.setFile(p);
+ getNodeTypes(jcrRootPath, p, docViewParser, nodeTypeCollectorHandler);
+ });
+ }
+ return nodeTypes;
+ }
+
+ private void getNodeTypes(Path jcrRootPath, Path docViewFile, DocViewParser docViewParser, NodeTypeCollectorHandler nodeTypeCollectorHandler) {
+ try (InputStream is = Files.newInputStream(docViewFile);
+ BufferedInputStream bufferedIs = new BufferedInputStream(is)) {
+ String documentViewXmlRootNodePath = DocViewParser.getDocumentViewXmlRootNodePath(bufferedIs, jcrRootPath.relativize(docViewFile));
+ if (documentViewXmlRootNodePath == null) {
+ return;
+ }
+ InputSource inputSource = new InputSource(bufferedIs);
+ docViewParser.parse(documentViewXmlRootNodePath, inputSource, nodeTypeCollectorHandler);
+ } catch (XmlParseException|IOException e) {
+ getLog().warn("Could not parse " + docViewFile + ". Ignore for node type definition generation!", e);
+ }
+ }
+
+ final class NodeTypeCollectorHandler implements DocViewParserHandler {
+ private final Set<String> nodeTypes;
+ private Path file;
+ public NodeTypeCollectorHandler(Set<String> nodeTypes) {
+ this.nodeTypes = nodeTypes;
+ }
+
+ public void setFile(Path file) {
+ this.file = file;
+ }
+
+ @Override
+ public void startDocViewNode(@NotNull String nodePath, @NotNull DocViewNode2 docViewNode, @NotNull Optional<DocViewNode2> parentDocViewNode, int line, int column)
+ throws IOException, RepositoryException {
+ Optional<String> primaryType = docViewNode.getPrimaryType();
+ if (primaryType.isPresent()) {
+ if (nodeTypes.add(primaryType.get())) {
+ getLog().debug("Found primary type " + primaryType.get() + " in node '" + nodePath + "' contained in file " + getProjectRelativeFilePath(file.toFile()));
+ }
+ }
+ if (nodeTypes.addAll(docViewNode.getMixinTypes())) {
+ getLog().debug("Found mixin types " + docViewNode.getMixinTypes() + " in node '" + nodePath + "' contained in file " + getProjectRelativeFilePath(file.toFile()));
+ }
+ }
+
+ @Override
+ public void endDocViewNode(@NotNull String nodePath, @NotNull DocViewNode2 docViewNode, @NotNull Optional<DocViewNode2> parentDocViewNode, int line, int column)
+ throws IOException, RepositoryException {
+ // do nothing
+ }
+ }
+
+ // resolve from cnd file
+ private Collection<QNodeTypeDefinition> resolveNodeTypes(Set<String> nodeTypeNames, NameResolver nameResolver, NodeTypeDefinitionProvider ntDefinitionProvider) throws IOException, RepositoryException, ParseException {
+ return resolveNodeTypesFromNames(nodeTypeNames.stream()
+ .map(name -> {
+ try {
+ return nameResolver.getQName(name);
+ } catch (IllegalNameException|NamespaceException e) {
+ throw new IllegalStateException("Cannot get expanded name for type " + name, e);
+ }
+ })
+ .collect(Collectors.toSet()),
+ ntDefinitionProvider);
+ }
+
+ static Collection<QNodeTypeDefinition> resolveNodeTypesFromNames(Set<Name> nodeTypeNames, NodeTypeDefinitionProvider ntDefinitionProvider) throws IOException, RepositoryException, ParseException {
+ return nodeTypeNames.stream().map(name -> {
+ try {
+ return ntDefinitionProvider.getNodeTypeDefinition(name);
+ } catch (RepositoryException e) {
+ throw new IllegalStateException("RepositoryException ", e);
+ }
+ }).collect(Collectors.toList());
+ }
+
+ private int writeCnd(Collection<? extends QNodeTypeDefinition> nodeTypeDefinitions, NodeTypeDefinitionProvider ntDefinitionProvider, NamespaceResolver nsResolver, Writer writer) throws IOException, RepositoryException {
+ CompactNodeTypeDefWriter cndWriter = new CompactNodeTypeDefWriter(writer, nsResolver, true);
+ Set<Name> written = new HashSet<>();
+ int numWrittenNodeTypes = 0;
+ for (QNodeTypeDefinition nodeTypeDefinition : nodeTypeDefinitions) {
+ numWrittenNodeTypes += writeNodeType(nodeTypeDefinition, cndWriter, written, ntDefinitionProvider);
+ }
+ cndWriter.close();
+ return numWrittenNodeTypes;
+ }
+
+ private int writeNodeType(Name nodeType, CompactNodeTypeDefWriter cndWriter, Set<Name> written, NodeTypeDefinitionProvider ntDefinitionProvider) throws IOException, RepositoryException {
+ if (nodeType == null || written.contains(nodeType)) {
+ return 0;
+ }
+ QNodeTypeDefinition ntDefinition = ntDefinitionProvider.getNodeTypeDefinition(nodeType);
+ return writeNodeType(ntDefinition, cndWriter, written, ntDefinitionProvider);
+ }
+
+ private int writeNodeType(QNodeTypeDefinition ntDefinition, CompactNodeTypeDefWriter cndWriter, Set<Name> written, NodeTypeDefinitionProvider ntDefinitionProvider)
+ throws IOException, RepositoryException {
+ int numWrittenNodeTypes = 1;
+ cndWriter.write(ntDefinition);
+ written.add(ntDefinition.getName());
+ // also write all referenced node types
+ for (Name superType: ntDefinition.getSupertypes()) {
+ numWrittenNodeTypes += writeNodeType(superType, cndWriter, written, ntDefinitionProvider);
+ }
+ for (QNodeDefinition cntDefinition: ntDefinition.getChildNodeDefs()) {
+ writeNodeType(cntDefinition.getDefaultPrimaryType(), cndWriter, written, ntDefinitionProvider);
+ if (cntDefinition.getRequiredPrimaryTypes() != null) {
+ for (Name name: cntDefinition.getRequiredPrimaryTypes()) {
+ numWrittenNodeTypes += writeNodeType(name, cndWriter, written, ntDefinitionProvider);
+ }
+ }
+ }
+ return numWrittenNodeTypes;
+ }
+}
diff --git a/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/GenerateCndMojoTest.java b/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/GenerateCndMojoTest.java
new file mode 100644
index 0000000..0fb7628
--- /dev/null
+++ b/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/GenerateCndMojoTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.Objects;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.commons.cnd.ParseException;
+import org.apache.jackrabbit.vault.util.StandaloneManagerProvider;
+import org.apache.maven.plugin.logging.SystemStreamLog;
+import org.apache.maven.project.MavenProject;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+class GenerateCndMojoTest {
+
+ @Test
+ void testGenerateCndMojo(@TempDir Path tmpFolder) throws IOException, RepositoryException, ParseException {
+ GenerateCndMojo mojo = new GenerateCndMojo();
+ mojo.setLog(new SystemStreamLog());
+ mojo.project = new MavenProject();
+ mojo.project.setFile(new File(getFile("/test-packages/generate-cnd", tmpFolder).toFile(), "pom.xml"));
+ StandaloneManagerProvider managerProvider = new StandaloneManagerProvider();
+ Path cndFile = tmpFolder.resolve("nodetypes.cnd");
+
+ Exception e = assertThrows(IllegalStateException.class, () -> mojo.generateCnd(managerProvider, cndFile, getFile("/test-packages/generate-cnd/jcr_root", tmpFolder)));
+ assertEquals("Cannot get expanded name for type cq:ClientLibraryFolder", e.getMessage());
+
+ // now register additional CND file and try again
+ try (InputStream input = getStream("cq-nodetypes.cnd");
+ Reader reader = new InputStreamReader(input, StandardCharsets.UTF_8)) {
+ managerProvider.registerNodeTypes(reader);
+ }
+ assertEquals(11, mojo.generateCnd(managerProvider, cndFile, getFile("/test-packages/generate-cnd/jcr_root", tmpFolder)));
+ }
+
+ public Path getFile(String name, Path tmpFolder) throws IOException {
+ URI uri;
+ try {
+ uri = Objects.requireNonNull(getClass().getResource(name), "Could not find class resource with name '" + name + "'").toURI();
+ } catch (URISyntaxException e) {
+ throw new IOException("Could not convert class resource URL to URI", e);
+ }
+ if (uri.isOpaque()) { // non hierarchical URIs (for resources in a JAR) can not use classical file operations
+ Path tmpFile = Files.createTempFile(tmpFolder, null, null);
+ try (InputStream in = getStream(name)) {
+ Files.copy(in, tmpFile, StandardCopyOption.REPLACE_EXISTING);
+ }
+ return tmpFile;
+ } else {
+ return Paths.get(uri);
+ }
+ }
+
+ public InputStream getStream(String name) {
+ return Objects.requireNonNull(getClass().getResourceAsStream(name), "Could not find class resource with name '" + name + "'");
+ }
+}
diff --git a/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/GenerateMetadataMojoTest.java b/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/GenerateMetadataMojoTest.java
index a836397..c77807c 100644
--- a/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/GenerateMetadataMojoTest.java
+++ b/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/GenerateMetadataMojoTest.java
@@ -171,6 +171,4 @@ public class GenerateMetadataMojoTest {
private static String unescapeContinuations(String value) {
return value.replaceAll("\r ", "");
}
-
-
}
diff --git a/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/it/CndGenerationIT.java b/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/it/CndGenerationIT.java
new file mode 100644
index 0000000..6a3eafa
--- /dev/null
+++ b/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/it/CndGenerationIT.java
@@ -0,0 +1,63 @@
+/*
+ * 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 java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.jackrabbit.filevault.maven.packaging.it.util.ProjectBuilder;
+import org.apache.jackrabbit.filevault.maven.packaging.it.util.ProjectBuilderExtension;
+import org.apache.maven.it.VerificationException;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(ProjectBuilderExtension.class)
+class CndGenerationIT {
+
+ @Test
+ void testCndGeneration(ProjectBuilder projectBuilder) throws IOException, VerificationException {
+ String expectedCnd = "<'nt'='http://www.jcp.org/jcr/nt/1.0'>\n"
+ + "<'jcr'='http://www.jcp.org/jcr/1.0'>\n"
+ + "<'mix'='http://www.jcp.org/jcr/mix/1.0'>\n"
+ + "\n"
+ + "[nt:file] > nt:hierarchyNode\n"
+ + " primaryitem jcr:content\n"
+ + " + jcr:content (nt:base) mandatory\n"
+ + "\n"
+ + "[nt:hierarchyNode] > mix:created\n"
+ + " abstract\n"
+ + "\n"
+ + "[mix:created]\n"
+ + " mixin\n"
+ + " - jcr:createdBy (string) autocreated protected\n"
+ + " - jcr:created (date) autocreated protected\n"
+ + "\n"
+ + "[nt:base]\n"
+ + " abstract\n"
+ + " - jcr:mixinTypes (name) protected multiple compute\n"
+ + " - jcr:primaryType (name) mandatory autocreated protected compute\n"
+ + "\n"
+ + "[nt:folder] > nt:hierarchyNode\n"
+ + " + * (nt:hierarchyNode) version\n"
+ + "\n";
+
+ projectBuilder
+ .setTestProjectDir("cnd-generation/simple")
+ .build()
+ .verifyExpectedFileContent("META-INF/vault/nodetypes.cnd", StandardCharsets.US_ASCII, expectedCnd);
+ }
+}
diff --git a/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/it/util/ProjectBuilder.java b/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/it/util/ProjectBuilder.java
index a375a5a..5c07465 100644
--- a/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/it/util/ProjectBuilder.java
+++ b/src/test/java/org/apache/jackrabbit/filevault/maven/packaging/it/util/ProjectBuilder.java
@@ -26,8 +26,10 @@ import static org.junit.jupiter.api.Assertions.fail;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
+import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
@@ -385,6 +387,18 @@ public class ProjectBuilder implements AutoCloseable {
return this;
}
+ public ProjectBuilder verifyExpectedFileContent(String name, Charset cs, String expectedContent) throws IOException {
+ openPackage();
+ JarEntry entry = contentPackageZip.getJarEntry(name);
+ if (entry == null) {
+ fail("Could not find entry with name " + name + " in package " + testPackageFile);
+ }
+ try (InputStream actualInput = contentPackageZip.getInputStream(entry)) {
+ assertEquals(expectedContent, IOUtils.toString(actualInput, cs));
+ }
+ return this;
+ }
+
private final static class JarEntryMatcher extends TypeSafeMatcher<JarEntry> {
private final String name;
diff --git a/src/test/resources/org/apache/jackrabbit/filevault/maven/packaging/cq-nodetypes.cnd b/src/test/resources/org/apache/jackrabbit/filevault/maven/packaging/cq-nodetypes.cnd
new file mode 100644
index 0000000..7d2a8ef
--- /dev/null
+++ b/src/test/resources/org/apache/jackrabbit/filevault/maven/packaging/cq-nodetypes.cnd
@@ -0,0 +1,91 @@
+/*
+ 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.
+*/
+
+<'sling'='http://sling.apache.org/jcr/sling/1.0'>
+<'cq'='http://www.day.com/jcr/cq/1.0'>
+
+[sling:Folder] > nt:folder
+ - * (undefined)
+ - * (undefined) multiple
+ + * (nt:base) = sling:Folder version
+
+[cq:ClientLibraryFolder] > sling:Folder
+ - channels (string) multiple
+ - categories (string) multiple
+ - dependencies (string) multiple
+ - embed (string) multiple
+
+[cq:Component] > mix:title, nt:folder, sling:ResourceSuperType
+ - * (undefined)
+ - allowedChildren (string) multiple
+ - dialogPath (string)
+ - * (undefined) multiple
+ - cq:isContainer (boolean)
+ - cq:noDecoration (boolean)
+ - cq:cellName (string)
+ - allowedParents (string) multiple
+ - componentGroup (string)
+ + cq:childEditConfig (cq:EditConfig) = cq:EditConfig
+ + design_dialog (nt:base) = nt:unstructured
+ + * (nt:base) = nt:base multiple version
+ + cq:infoProviders (nt:base) = nt:unstructured
+ + virtual (nt:base) = sling:Folder
+ + dialog (nt:base) = nt:unstructured
+ + cq:htmlTag (nt:base) = nt:unstructured
+ + cq:editConfig (cq:EditConfig) = cq:EditConfig
+ + icon.png (nt:file)
+ + thumbnail.png (nt:file)
+
+[sling:ResourceSuperType]
+ mixin
+ - sling:resourceSuperType (string)
+
+[mix:title]
+ mixin
+ - jcr:description (string)
+ - jcr:title (string)
+
+[cq:EditConfig] > nt:hierarchyNode, nt:unstructured
+ orderable
+ - cq:dialogMode (string) < 'auto', 'floating', 'inline'
+ - cq:layout (string) < 'editbar', 'rollover', 'auto'
+ - cq:actions (string) multiple
+ - cq:emptyText (string)
+ - cq:inherit (boolean)
+ + cq:formParameters (nt:base) = nt:unstructured
+ + cq:actionConfigs (nt:base) = nt:unstructured
+ + cq:dropTargets (nt:base) = nt:unstructured
+ + cq:inplaceEditing (cq:InplaceEditingConfig) = cq:InplaceEditingConfig
+ + cq:listeners (nt:base) = cq:EditListenersConfig
+
+[cq:InplaceEditingConfig] > nt:unstructured
+ orderable
+ - editorType (string)
+ - configPath (string)
+ - active (boolean)
+ + config (nt:unstructured) = nt:unstructured
+
+[cq:EditListenersConfig]
+ - afteredit (string)
+ - aftermove (string)
+ - afterinsert (string)
+ - * (undefined) multiple
+ - aftercreate (string)
+ - afterremove (string)
+ - * (undefined)
+ - afterdelete (string)
+ + * (nt:base) = nt:base multiple version
\ No newline at end of file
diff --git a/src/test/resources/test-packages/generate-cnd/jcr_root/apps/test/base/components/.content.xml b/src/test/resources/test-packages/generate-cnd/jcr_root/apps/test/base/components/.content.xml
new file mode 100644
index 0000000..ce03add
--- /dev/null
+++ b/src/test/resources/test-packages/generate-cnd/jcr_root/apps/test/base/components/.content.xml
@@ -0,0 +1,19 @@
+<?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:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
+ jcr:primaryType="sling:Folder"/>
diff --git a/src/test/resources/test-packages/generate-cnd/jcr_root/apps/test/base/components/assetselection/clientlib/.content.xml b/src/test/resources/test-packages/generate-cnd/jcr_root/apps/test/base/components/assetselection/clientlib/.content.xml
new file mode 100644
index 0000000..2f6e264
--- /dev/null
+++ b/src/test/resources/test-packages/generate-cnd/jcr_root/apps/test/base/components/assetselection/clientlib/.content.xml
@@ -0,0 +1,24 @@
+<?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:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
+ jcr:primaryType="cq:ClientLibraryFolder"
+ allowProxy="{Boolean}true"
+ categories="[test]"
+ dependencies="[cq.shared]"
+ embed="[cq.authoring.messaging]"
+ jsProcessor="[default:gcc;languageIn=STABLE;compilationLevel=whitespace,min:gcc;languageIn=STABLE;compilationLevel=whitespace]"/>
diff --git a/src/test/resources/test-packages/generate-cnd/jcr_root/apps/test/base/components/container/.content.xml b/src/test/resources/test-packages/generate-cnd/jcr_root/apps/test/base/components/container/.content.xml
new file mode 100644
index 0000000..a2537ee
--- /dev/null
+++ b/src/test/resources/test-packages/generate-cnd/jcr_root/apps/test/base/components/container/.content.xml
@@ -0,0 +1,22 @@
+<?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:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
+ cq:isContainer="{Boolean}true"
+ jcr:primaryType="cq:Component"
+ jcr:title="Container"
+ sling:resourceSuperType="core/wcm/components/container/v1/container"/>
diff --git a/src/test/resources/test-packages/generate-cnd/jcr_root/apps/test/base/components/test.txt b/src/test/resources/test-packages/generate-cnd/jcr_root/apps/test/base/components/test.txt
new file mode 100644
index 0000000..6a57763
--- /dev/null
+++ b/src/test/resources/test-packages/generate-cnd/jcr_root/apps/test/base/components/test.txt
@@ -0,0 +1,17 @@
+<!--
+ 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.
+ -->
+Some empty file
\ No newline at end of file
diff --git a/src/test/resources/test-projects/cnd-generation/simple/jcr_root/apps/test/base/components/.content.xml b/src/test/resources/test-projects/cnd-generation/simple/jcr_root/apps/test/base/components/.content.xml
new file mode 100644
index 0000000..e10c257
--- /dev/null
+++ b/src/test/resources/test-projects/cnd-generation/simple/jcr_root/apps/test/base/components/.content.xml
@@ -0,0 +1,19 @@
+<?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:jcr="http://www.jcp.org/jcr/1.0"
+ jcr:primaryType="nt:folder"/>
diff --git a/src/test/resources/test-projects/cnd-generation/simple/jcr_root/apps/test/base/components/test.txt b/src/test/resources/test-projects/cnd-generation/simple/jcr_root/apps/test/base/components/test.txt
new file mode 100644
index 0000000..6a57763
--- /dev/null
+++ b/src/test/resources/test-projects/cnd-generation/simple/jcr_root/apps/test/base/components/test.txt
@@ -0,0 +1,17 @@
+<!--
+ 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.
+ -->
+Some empty file
\ No newline at end of file
diff --git a/src/test/resources/test-projects/cnd-generation/simple/pom.xml b/src/test/resources/test-projects/cnd-generation/simple/pom.xml
new file mode 100644
index 0000000..5e41e58
--- /dev/null
+++ b/src/test/resources/test-projects/cnd-generation/simple/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>
+ <groupId>org.apache.jackrabbit.filevault</groupId>
+ <artifactId>package-plugin-test-pkg</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>content-package</packaging>
+ <name>Example Default Project</name>
+ <description>This is an Example Project to verify that CND generation is correct.</description>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.jackrabbit</groupId>
+ <artifactId>filevault-package-maven-plugin</artifactId>
+ <version>${plugin.version}</version>
+ <extensions>true</extensions>
+ <configuration>
+ <group>my/test/group</group>
+ <filters>
+ <filter>
+ <root>/apps/test</root>
+ </filter>
+ </filters>
+ </configuration>
+ <executions>
+ <execution>
+ <id>generate-cnd</id>
+ <goals>
+ <goal>generate-cnd</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>