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-org-apache-sling-repoinit-filevault-validator.git

commit c55ab69f270a8624b07d18839b2185b8c8c02d35
Author: Konrad Windszus <[email protected]>
AuthorDate: Thu Dec 22 19:45:19 2022 +0100

    SLING-11729 initial commit of a FileVault validator for RepoInit
---
 .gitignore                                         |   4 +
 README.md                                          |  46 +++++
 pom.xml                                            | 105 +++++++++++
 src/it/project1/META-INF/vault/filter.xml          |  20 +++
 src/it/project1/invoker.properties                 |  17 ++
 src/it/project1/pom.xml                            |  75 ++++++++
 ...jcr.repoinit.RepositoryInitializer~test1.config |  17 ++
 src/it/project1/verify.groovy                      |  23 +++
 .../filevault/validator/RepoInitValidator.java     | 192 +++++++++++++++++++++
 .../validator/RepoInitValidatorFactory.java        |  45 +++++
 10 files changed, 544 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0f6424b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+/target/
+/.settings
+/.project
+/.classpath
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5f0a420
--- /dev/null
+++ b/README.md
@@ -0,0 +1,46 @@
+[![Apache 
Sling](https://sling.apache.org/res/logos/sling.png)](https://sling.apache.org)
+
+[![Build 
Status](https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apache-sling-repoinit-filevault-validator/job/master/badge/icon)](https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apache-sling-repoinit-filevault-validator/job/master/)
+[![Test 
Status](https://img.shields.io/jenkins/tests.svg?jobUrl=https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apache-sling-repoinit-filevault-validator/job/master/)](https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apache-sling-repoinit-filevault-validator/job/master/test/?width=800&height=600)
+[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=apache_sling-org-apache-sling-repoinit-filevault-validator&metric=coverage)](https://sonarcloud.io/dashboard?id=apache_sling-org-apache-sling-repoinit-filevault-validator)
+[![Sonarcloud 
Status](https://sonarcloud.io/api/project_badges/measure?project=apache_sling-org-apache-sling-repoinit-filevault-validator&metric=alert_status)](https://sonarcloud.io/dashboard?id=apache_sling-org-apache-sling-repoinit-filevault-validator)
+[![JavaDoc](https://www.javadoc.io/badge/org.apache.sling/org.apache.sling.repoinit.filevault.validator.svg)](https://www.javadoc.io/doc/org.apache.sling/org.apache.sling.repoinit.filevault.validator)
+[![Maven 
Central](https://maven-badges.herokuapp.com/maven-central/org.apache.sling/org.apache.sling.repoinit.filevault.validator/badge.svg)](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.apache.sling%22%20a%3A%22org.apache.sling.repoinit.filevault.validator%22)
+[![repoinit](https://sling.apache.org/badges/group-repoinit.svg)](https://github.com/apache/sling-aggregator/blob/master/docs/groups/repoinit.md)
+[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
+
+# 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][4] 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>
+    <!-- use the following dependency to optionally overwrite the used 
repoinit parser version (default=1.8.0) -->
+    <dependency>
+      <groupId>org.apache.sling</groupId>
+      <artifactId>org.apache.sling.repoinit.parser</artifactId>
+      <version><version of the parser in the runtime where my config is 
deployed to></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
+[4]: 
https://sling.apache.org/documentation/bundles/repository-initialization.html
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..58f7db0
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,105 @@
+<!--
+ 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.8.0</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>
+                            <debug>true</debug>
+                            <!-- 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/src/it/project1/META-INF/vault/filter.xml 
b/src/it/project1/META-INF/vault/filter.xml
new file mode 100644
index 0000000..c180df7
--- /dev/null
+++ b/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/src/it/project1/invoker.properties 
b/src/it/project1/invoker.properties
new file mode 100644
index 0000000..021186e
--- /dev/null
+++ b/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/src/it/project1/pom.xml b/src/it/project1/pom.xml
new file mode 100644
index 0000000..dd1bcc6
--- /dev/null
+++ b/src/it/project1/pom.xml
@@ -0,0 +1,75 @@
+<?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>
+                        <!-- override the parser dependency (explicit 
downgrade from 1.8.0) -->
+                        <dependency>
+                            <groupId>org.apache.sling</groupId>
+                            
<artifactId>org.apache.sling.repoinit.parser</artifactId>
+                            <version>1.6.14</version>
+                            <scope>compile</scope>
+                        </dependency>
+                        <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/src/it/project1/src/main/jcr_root/apps/test/config/org.apache.sling.jcr.repoinit.RepositoryInitializer~test1.config
 
b/src/it/project1/src/main/jcr_root/apps/test/config/org.apache.sling.jcr.repoinit.RepositoryInitializer~test1.config
new file mode 100644
index 0000000..6d596ba
--- /dev/null
+++ 
b/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/src/it/project1/verify.groovy b/src/it/project1/verify.groovy
new file mode 100644
index 0000000..4957ba6
--- /dev/null
+++ b/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/src/main/java/org/apache/sling/repoinit/filevault/validator/RepoInitValidator.java
 
b/src/main/java/org/apache/sling/repoinit/filevault/validator/RepoInitValidator.java
new file mode 100644
index 0000000..28f6e73
--- /dev/null
+++ 
b/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/src/main/java/org/apache/sling/repoinit/filevault/validator/RepoInitValidatorFactory.java
 
b/src/main/java/org/apache/sling/repoinit/filevault/validator/RepoInitValidatorFactory.java
new file mode 100644
index 0000000..f471750
--- /dev/null
+++ 
b/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;
+    }
+
+}

Reply via email to