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/sling-whiteboard.git
The following commit(s) were added to refs/heads/master by this push:
new ddfd07ef SLING-11729 add FileVault validator for repoinit OSGi
configurations
ddfd07ef is described below
commit ddfd07ef33d07a1d1fad0b19d6e12075f2bfb60e
Author: Konrad Windszus <[email protected]>
AuthorDate: Tue Dec 13 17:11:56 2022 +0100
SLING-11729 add FileVault validator for repoinit OSGi configurations
---
.../README.md | 32 ++++
.../pom.xml | 104 +++++++++++
.../src/it/project1/META-INF/vault/filter.xml | 20 +++
.../src/it/project1/invoker.properties | 17 ++
.../src/it/project1/pom.xml | 68 ++++++++
...jcr.repoinit.RepositoryInitializer~test1.config | 17 ++
.../src/it/project1/verify.groovy | 23 +++
.../filevault/validator/RepoInitValidator.java | 192 +++++++++++++++++++++
.../validator/RepoInitValidatorFactory.java | 45 +++++
9 files changed, 518 insertions(+)
diff --git a/org.apache.sling.repoinit.filevault.validator/README.md
b/org.apache.sling.repoinit.filevault.validator/README.md
new file mode 100644
index 00000000..1d034d75
--- /dev/null
+++ b/org.apache.sling.repoinit.filevault.validator/README.md
@@ -0,0 +1,32 @@
+[](https://sling.apache.org)
+
+ [](https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apache-sling-repoinit-parser/job/master/) [](https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apac
[...]
+
+# Apache Sling Repo Init FileVault Validator
+
+This module is part of the [Apache Sling](https://sling.apache.org) project.
+
+It implements a [FileVault validator][1] for the [Repository Initialization
language](https://sling.apache.org/documentation/bundles/repository-initialization.html)
used in [serialized OSGi configurations][3].
+It emits validation error messages for invalid repoinit statements.
+
+# Usage with Maven
+
+You can use this validator with the [FileVault Package Maven Plugin][2] in
version 1.3.0 or higher like this
+
+```
+<plugin>
+ <groupId>org.apache.jackrabbit</groupId>
+ <artifactId>filevault-package-maven-plugin</artifactId>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.repoinit.filevault.validator</artifactId>
+ <version><latestversion></version>
+ </dependency>
+ </dependencies>
+</plugin>
+```
+
+[1]: https://jackrabbit.apache.org/filevault/validation.html
+[2]: https://jackrabbit.apache.org/filevault-package-maven-plugin/index.html
+[3]:
https://sling.apache.org/documentation/bundles/configuration-installer-factory.html#configuration-serialization-formats
\ No newline at end of file
diff --git a/org.apache.sling.repoinit.filevault.validator/pom.xml
b/org.apache.sling.repoinit.filevault.validator/pom.xml
new file mode 100644
index 00000000..a16aa8ed
--- /dev/null
+++ b/org.apache.sling.repoinit.filevault.validator/pom.xml
@@ -0,0 +1,104 @@
+<!--
+ 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
https://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.repoinit.filevault.validator</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+
+ <parent>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>sling</artifactId>
+ <version>49</version>
+ </parent>
+
+ <properties>
+ <sling.java.version>8</sling.java.version>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.jackrabbit.vault</groupId>
+ <artifactId>vault-validation</artifactId>
+ <version>3.6.0</version>
+ <scope>compile</scope>
+ </dependency>
+ <!-- We use a class from the config admin implementation to read
config files -->
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.configadmin</artifactId>
+ <version>1.8.12</version>
+ <scope>compile</scope>
+ </dependency>
+ <!-- JSON Configurations -->
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.cm.json</artifactId>
+ <version>1.0.6</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-json_1.1_spec</artifactId>
+ <version>1.2</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.repoinit.parser</artifactId>
+ <version>1.6.10</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jetbrains</groupId>
+ <artifactId>annotations</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <!-- http://metainf-services.kohsuke.org/index.html -->
+ <dependency>
+ <groupId>org.kohsuke.metainf-services</groupId>
+ <artifactId>metainf-services</artifactId>
+ <version>1.8</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-invoker-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>install</goal>
+ <goal>run</goal>
+ </goals>
+ <configuration>
+
<cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
+
<postBuildHookScript>verify.groovy</postBuildHookScript>
+ <streamLogsOnFailures>true</streamLogsOnFailures>
+ <!-- reuse global repo for speeding up builds
+
<localRepositoryPath>target/it-repo</localRepositoryPath> -->
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git
a/org.apache.sling.repoinit.filevault.validator/src/it/project1/META-INF/vault/filter.xml
b/org.apache.sling.repoinit.filevault.validator/src/it/project1/META-INF/vault/filter.xml
new file mode 100644
index 00000000..c180df79
--- /dev/null
+++
b/org.apache.sling.repoinit.filevault.validator/src/it/project1/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="/apps/test"/>
+</workspaceFilter>
\ No newline at end of file
diff --git
a/org.apache.sling.repoinit.filevault.validator/src/it/project1/invoker.properties
b/org.apache.sling.repoinit.filevault.validator/src/it/project1/invoker.properties
new file mode 100644
index 00000000..021186ee
--- /dev/null
+++
b/org.apache.sling.repoinit.filevault.validator/src/it/project1/invoker.properties
@@ -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.
+invoker.buildResult = failure
\ No newline at end of file
diff --git
a/org.apache.sling.repoinit.filevault.validator/src/it/project1/pom.xml
b/org.apache.sling.repoinit.filevault.validator/src/it/project1/pom.xml
new file mode 100644
index 00000000..872d6910
--- /dev/null
+++ b/org.apache.sling.repoinit.filevault.validator/src/it/project1/pom.xml
@@ -0,0 +1,68 @@
+<?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.
+-->
+<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/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.apache.sling</groupId>
+
<artifactId>org.apache.sling.repoinit.filevault.validator.project1</artifactId>
+ <packaging>content-package</packaging>
+ <version>1.0.0-SNAPSHOT</version>
+
+ <parent>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>sling</artifactId>
+ <version>49</version>
+ </parent>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.rat</groupId>
+ <artifactId>apache-rat-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.jackrabbit</groupId>
+ <artifactId>filevault-package-maven-plugin</artifactId>
+ <version>1.3.0</version>
+ <extensions>true</extensions>
+ <configuration>
+ <packageType>container</packageType>
+ <validatorsSettings>
+ <jackrabbit-filter>
+ <!-- define additional valid roots which are
always provided in Sling -->
+ <options>
+ <validRoots>/,/libs,/apps</validRoots>
+ </options>
+ </jackrabbit-filter>
+ </validatorsSettings>
+ </configuration>
+ <dependencies>
+ <dependency>
+ <groupId>@project.groupId@</groupId>
+ <artifactId>@project.artifactId@</artifactId>
+ <version>@project.version@</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git
a/org.apache.sling.repoinit.filevault.validator/src/it/project1/src/main/jcr_root/apps/test/config/org.apache.sling.jcr.repoinit.RepositoryInitializer~test1.config
b/org.apache.sling.repoinit.filevault.validator/src/it/project1/src/main/jcr_root/apps/test/config/org.apache.sling.jcr.repoinit.RepositoryInitializer~test1.config
new file mode 100644
index 00000000..6d596ba6
--- /dev/null
+++
b/org.apache.sling.repoinit.filevault.validator/src/it/project1/src/main/jcr_root/apps/test/config/org.apache.sling.jcr.repoinit.RepositoryInitializer~test1.config
@@ -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.
+scripts=invalid
\ No newline at end of file
diff --git
a/org.apache.sling.repoinit.filevault.validator/src/it/project1/verify.groovy
b/org.apache.sling.repoinit.filevault.validator/src/it/project1/verify.groovy
new file mode 100644
index 00000000..4957ba68
--- /dev/null
+++
b/org.apache.sling.repoinit.filevault.validator/src/it/project1/verify.groovy
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+String buildLog = new File(basedir, 'build.log').text
+
+assert buildLog.contains('''[ERROR] ValidationViolation: "sling-repoinit:
Invalid repoinit statement(s) detected: Encountered " <STRING> "invalid "" at
line 1, column 1.
+Was expecting:
+ <EOF>
+ ", filePath=''' +
"src${File.separator}main${File.separator}jcr_root${File.separator}apps${File.separator}test${File.separator}config${File.separator}org.apache.sling.jcr.repoinit.RepositoryInitializer~test1.config")
diff --git
a/org.apache.sling.repoinit.filevault.validator/src/main/java/org/apache/sling/repoinit/filevault/validator/RepoInitValidator.java
b/org.apache.sling.repoinit.filevault.validator/src/main/java/org/apache/sling/repoinit/filevault/validator/RepoInitValidator.java
new file mode 100644
index 00000000..28f6e731
--- /dev/null
+++
b/org.apache.sling.repoinit.filevault.validator/src/main/java/org/apache/sling/repoinit/filevault/validator/RepoInitValidator.java
@@ -0,0 +1,192 @@
+/*
+ * 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.sling.repoinit.filevault.validator;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.function.Function;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import org.apache.felix.cm.json.ConfigurationReader;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
+import org.apache.jackrabbit.util.Text;
+import org.apache.jackrabbit.vault.util.DocViewNode2;
+import org.apache.jackrabbit.vault.util.DocViewProperty2;
+import org.apache.jackrabbit.vault.validation.spi.DocumentViewXmlValidator;
+import org.apache.jackrabbit.vault.validation.spi.GenericJcrDataValidator;
+import org.apache.jackrabbit.vault.validation.spi.NodeContext;
+import org.apache.jackrabbit.vault.validation.spi.ValidationMessage;
+import org.apache.jackrabbit.vault.validation.spi.ValidationMessageSeverity;
+import org.apache.sling.repoinit.parser.RepoInitParsingException;
+import org.apache.sling.repoinit.parser.impl.RepoInitParserService;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class RepoInitValidator implements DocumentViewXmlValidator,
GenericJcrDataValidator {
+
+ private final RepoInitParserService parser;
+
+ public RepoInitValidator() {
+ parser = new RepoInitParserService();
+ }
+ enum OsgiConfigurationSerialization {
+ CFG,
+ CFG_JSON,
+ CONFIG
+ }
+
+ private static final String OSGI_CONFIG_NAME =
"org\\.apache\\.sling\\.jcr\\.repoinit\\.RepositoryInitializer(~|-).*";
+
+ private static final Pattern OSGI_CONFIG_NODE_NAME_PATTERN =
Pattern.compile(OSGI_CONFIG_NAME);
+
+ /**
+ *
https://sling.apache.org/documentation/bundles/configuration-installer-factory.html#configuration-serialization-formats
+ */
+ private static final Pattern OSGI_CONFIG_FILE_NAME_PATTERN =
Pattern.compile(OSGI_CONFIG_NAME + "\\.(config|cfg\\.json|cfg)");
+
+ @Nullable
+ public Collection<ValidationMessage> done() {
+ return null;
+ }
+
+ @Override
+ public @Nullable Collection<ValidationMessage> validate(@NotNull
DocViewNode2 node, @NotNull NodeContext nodeContext,
+ boolean isRoot) {
+ if ("sling:OsgiConfig".equals(node.getPrimaryType().orElse("")) &&
OSGI_CONFIG_NODE_NAME_PATTERN.matcher(Text.getName(nodeContext.getNodePath())).matches())
{
+ Optional<DocViewProperty2> scriptsProperty =
node.getProperty(NameFactoryImpl.getInstance().create(Name.NS_DEFAULT_URI,
"scripts"));
+ if (scriptsProperty.isPresent()) {
+ try {
+ return
validateStatements(scriptsProperty.get().getStringValues());
+ } catch (IOException e) {
+ return Collections.singleton(new
ValidationMessage(ValidationMessageSeverity.ERROR, "IOException while parsing "
+ nodeContext.getFilePath() +" : " + e.getMessage(), e));
+ }
+ }
+ }
+ return null;
+ }
+
+
+ @Override
+ @Nullable
+ public Collection<ValidationMessage> validateJcrData(@NotNull InputStream
input, @NotNull Path filePath, @NotNull Path basePath,
+ @NotNull Map<String, Integer> nodePathsAndLineNumbers) throws
IOException {
+ Map<String, Object> config =
deserializeOsgiConfiguration(getType(filePath.getFileName().toString()), input);
+ return validateConfig(config);
+ }
+
+ public boolean shouldValidateJcrData(@NotNull Path filePath, @NotNull Path
basePath) {
+ return isOsgiConfig(filePath);
+ }
+
+ private OsgiConfigurationSerialization getType(String fileName) {
+ if (fileName.endsWith(".cfg.json")) {
+ return OsgiConfigurationSerialization.CFG_JSON;
+ } else if (fileName.endsWith(".config")) {
+ return OsgiConfigurationSerialization.CONFIG;
+ } else if (fileName.endsWith(".cfg")) {
+ return OsgiConfigurationSerialization.CONFIG;
+ } else {
+ throw new IllegalArgumentException("Given file name " + fileName +
" does not represent a known OSGi configuration serialization");
+ }
+ }
+
+ private boolean isOsgiConfig(@NotNull Path filePath) {
+ // TODO: check depth of config node
+ String fileName = filePath.getFileName().toString();
+ return OSGI_CONFIG_FILE_NAME_PATTERN.matcher(fileName).matches();
+ }
+
+ Map<String, Object> deserializeOsgiConfiguration(@NotNull
OsgiConfigurationSerialization serializationType, @NotNull InputStream input)
throws IOException {
+ switch(serializationType) {
+ case CONFIG:
+ Properties properties = new Properties();
+ properties.load(input);
+ return convertToMap(properties);
+ case CFG:
+ return
convertToMap(org.apache.felix.cm.file.ConfigurationHandler.read(input));
+ case CFG_JSON:
+ Reader reader = new InputStreamReader(input,
StandardCharsets.UTF_8);
+ ConfigurationReader configReader =
org.apache.felix.cm.json.Configurations.buildReader().build(reader);
+ return convertToMap(configReader.readConfiguration());
+ }
+ return null;
+ }
+
+ private Collection<ValidationMessage> validateConfig(Map<String, Object>
config) throws IOException {
+ //
https://sling.apache.org/documentation/bundles/repository-initialization.html#providing-repoinit-statements-from-osgi-factory-configurations
+ // only evaluate scripts for now, references might have unresolvable
URLs at the time of building
(https://sling.apache.org/documentation/bundles/repository-initialization.html#references-to-urls-providing-raw-repoinit-statements)
+ Object scripts = config.get("scripts");
+ if (scripts == null) {
+ return null;
+ }
+ if (scripts instanceof String[]) {
+ return validateStatements(Arrays.asList((String[])scripts));
+ } else if (scripts instanceof String) {
+ return
validateStatements((String)scripts).map(Collections::singletonList).orElse(null);
+ } else {
+ return Collections.singletonList(new
ValidationMessage(ValidationMessageSeverity.ERROR, "OSGi config property
'scripts' must be of type String or String[]"));
+ }
+ }
+
+ private Collection<ValidationMessage>
validateStatements(Collection<String> scripts) throws IOException {
+ List<ValidationMessage> validationMsgs = new ArrayList<>();
+ for (String statements : scripts) {
+ validateStatements(statements).ifPresent(validationMsgs::add);
+ }
+ return validationMsgs;
+ }
+
+ private Optional<ValidationMessage> validateStatements(String statements)
throws IOException {
+ try (Reader reader = new StringReader(statements)) {
+ parser.parse(reader);
+ } catch (RepoInitParsingException e) {
+ return Optional.of(new
ValidationMessage(ValidationMessageSeverity.ERROR, "Invalid repoinit
statement(s) detected: " + e.getMessage(), e));
+ }
+ return Optional.empty();
+ }
+
+ static Map<String, Object> convertToMap(Dictionary<String, ?> dictionary) {
+ List<String> keys = Collections.list(dictionary.keys());
+ return keys.stream().collect(Collectors.toMap(Function.identity(),
dictionary::get));
+ }
+
+ static Map<String, Object> convertToMap(Properties properties) {
+ return properties.entrySet().stream().collect(
+ Collectors.toMap(
+ e -> String.valueOf(e.getKey()),
+ e -> String.valueOf(e.getValue()),
+ (prev, next) -> next, HashMap::new
+ ));
+ }
+}
diff --git
a/org.apache.sling.repoinit.filevault.validator/src/main/java/org/apache/sling/repoinit/filevault/validator/RepoInitValidatorFactory.java
b/org.apache.sling.repoinit.filevault.validator/src/main/java/org/apache/sling/repoinit/filevault/validator/RepoInitValidatorFactory.java
new file mode 100644
index 00000000..f4717504
--- /dev/null
+++
b/org.apache.sling.repoinit.filevault.validator/src/main/java/org/apache/sling/repoinit/filevault/validator/RepoInitValidatorFactory.java
@@ -0,0 +1,45 @@
+/*
+ * 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.sling.repoinit.filevault.validator;
+
+import org.apache.jackrabbit.vault.validation.spi.ValidationContext;
+import org.apache.jackrabbit.vault.validation.spi.Validator;
+import org.apache.jackrabbit.vault.validation.spi.ValidatorFactory;
+import org.apache.jackrabbit.vault.validation.spi.ValidatorSettings;
+import org.jetbrains.annotations.NotNull;
+import org.kohsuke.MetaInfServices;
+
+@MetaInfServices
+public class RepoInitValidatorFactory implements ValidatorFactory {
+
+ public Validator createValidator(@NotNull ValidationContext context,
@NotNull ValidatorSettings settings) {
+ return new RepoInitValidator();
+ }
+
+ public boolean shouldValidateSubpackages() {
+ return false;
+ }
+
+ public @NotNull String getId() {
+ return "sling-repoinit";
+ }
+
+ public int getServiceRanking() {
+ return 0;
+ }
+
+}