diff --git a/changes.xml b/changes.xml index 6979831..35d921d 100644 --- a/changes.xml +++ b/changes.xml @@ -24,6 +24,9 @@ + + Add JsonOsgiConfigPostProcessor to support reading a combined set of OSGi configuration for run modes from .osgiconfig.json files. + ProvisioningOsgiConfigPostProcessor: Write OSGi configurations as .cfg.json files instead of .config files. diff --git a/conga-sling-plugin/pom.xml b/conga-sling-plugin/pom.xml index 3a9cab8..9b7917d 100644 --- a/conga-sling-plugin/pom.xml +++ b/conga-sling-plugin/pom.xml @@ -79,6 +79,13 @@ compile + + com.fasterxml.jackson.core + jackson-databind + 2.16.1 + compile + + org.apache.felix org.apache.felix.cm.json diff --git a/conga-sling-plugin/src/main/java/io/wcm/devops/conga/plugins/sling/postprocessor/JsonOsgiConfigPostProcessor.java b/conga-sling-plugin/src/main/java/io/wcm/devops/conga/plugins/sling/postprocessor/JsonOsgiConfigPostProcessor.java new file mode 100644 index 0000000..46c5aaa --- /dev/null +++ b/conga-sling-plugin/src/main/java/io/wcm/devops/conga/plugins/sling/postprocessor/JsonOsgiConfigPostProcessor.java @@ -0,0 +1,85 @@ +/* + * #%L + * wcm.io + * %% + * Copyright (C) 2024 wcm.io + * %% + * Licensed 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. + * #L% + */ +package io.wcm.devops.conga.plugins.sling.postprocessor; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.apache.sling.provisioning.model.Model; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import io.wcm.devops.conga.generator.GeneratorException; +import io.wcm.devops.conga.generator.spi.PostProcessorPlugin; +import io.wcm.devops.conga.generator.spi.context.FileContext; +import io.wcm.devops.conga.generator.spi.context.PostProcessorContext; +import io.wcm.devops.conga.plugins.sling.util.JsonOsgiConfigUtil; +import io.wcm.devops.conga.plugins.sling.util.ProvisioningUtil; + +/** + * Transforms a combined JSON file containing OSGi configurations into individual OSGi configuration files. + */ +public class JsonOsgiConfigPostProcessor implements PostProcessorPlugin { + + /** + * Plugin name + */ + public static final String NAME = "sling-json-osgiconfig"; + + /** + * File extension + */ + public static final String FILE_EXTENSION = ".osgiconfig.json"; + + @Override + public String getName() { + return NAME; + } + + @Override + public boolean accepts(FileContext file, PostProcessorContext context) { + return StringUtils.endsWith(file.getFile().getName(), FILE_EXTENSION); + } + + @Override + @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_BAD_PRACTICE") + public List apply(FileContext fileContext, PostProcessorContext context) { + File file = fileContext.getFile(); + try { + // read JSON file with combined configurations + Model model = JsonOsgiConfigUtil.readToProvisioningModel(file); + + // generate OSGi configurations + List files = ProvisioningUtil.generateOsgiConfigurations(model, file.getParentFile(), context); + + // delete provisioning file after transformation + Files.delete(file.toPath()); + + // return list of generated osgi configuration files + return files; + } + catch (IOException ex) { + throw new GeneratorException("Unable to parse JSON file with OSGi configurations.", ex); + } + } + +} diff --git a/conga-sling-plugin/src/main/java/io/wcm/devops/conga/plugins/sling/postprocessor/ProvisioningOsgiConfigPostProcessor.java b/conga-sling-plugin/src/main/java/io/wcm/devops/conga/plugins/sling/postprocessor/ProvisioningOsgiConfigPostProcessor.java index 9a759e7..9f855bf 100644 --- a/conga-sling-plugin/src/main/java/io/wcm/devops/conga/plugins/sling/postprocessor/ProvisioningOsgiConfigPostProcessor.java +++ b/conga-sling-plugin/src/main/java/io/wcm/devops/conga/plugins/sling/postprocessor/ProvisioningOsgiConfigPostProcessor.java @@ -20,10 +20,8 @@ package io.wcm.devops.conga.plugins.sling.postprocessor; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Dictionary; +import java.nio.file.Files; import java.util.List; import org.apache.sling.provisioning.model.Model; @@ -33,12 +31,11 @@ import io.wcm.devops.conga.generator.spi.PostProcessorPlugin; import io.wcm.devops.conga.generator.spi.context.FileContext; import io.wcm.devops.conga.generator.spi.context.PostProcessorContext; -import io.wcm.devops.conga.plugins.sling.util.ConfigConsumer; -import io.wcm.devops.conga.plugins.sling.util.OsgiConfigUtil; import io.wcm.devops.conga.plugins.sling.util.ProvisioningUtil; /** - * Transforms a Sling Provisioning file into OSGi configurations (ignoring all other provisioning contents). + * Transforms a Sling Provisioning file into OSGi configuration files (.cfg.json). + * Repoinit statements are supported as well, all other provisioning contents are ignored */ public class ProvisioningOsgiConfigPostProcessor implements PostProcessorPlugin { @@ -64,41 +61,17 @@ public List apply(FileContext fileContext, PostProcessorContext con try { // generate OSGi configurations Model model = ProvisioningUtil.getModel(fileContext); - List files = generateOsgiConfigurations(model, file.getParentFile(), context); + List files = ProvisioningUtil.generateOsgiConfigurations(model, file.getParentFile(), context); // delete provisioning file after transformation - file.delete(); + Files.delete(file.toPath()); // return list of generated osgi configuration files return files; } catch (IOException ex) { - throw new GeneratorException("Unable to post-process sling provisioning OSGi configurations.", ex); + throw new GeneratorException("Unable to post-process Sling Provisioning OSGi configurations.", ex); } } - /** - * Generate OSGi configuration for all feature and run modes. - * @param model Provisioning Model - * @param dir Target directory - * @param context Post processor context - */ - private List generateOsgiConfigurations(Model model, File dir, PostProcessorContext context) throws IOException { - return ProvisioningUtil.visitOsgiConfigurations(model, new ConfigConsumer() { - @Override - @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_BAD_PRACTICE") - public FileContext accept(String path, Dictionary properties) throws IOException { - context.getLogger().info(" Generate {}", path); - - File confFile = new File(dir, path); - confFile.getParentFile().mkdirs(); - try (FileOutputStream os = new FileOutputStream(confFile)) { - OsgiConfigUtil.write(os, properties); - } - - return new FileContext().file(confFile).charset(StandardCharsets.UTF_8); - } - }); - } - } diff --git a/conga-sling-plugin/src/main/java/io/wcm/devops/conga/plugins/sling/util/JsonOsgiConfigUtil.java b/conga-sling-plugin/src/main/java/io/wcm/devops/conga/plugins/sling/util/JsonOsgiConfigUtil.java new file mode 100644 index 0000000..b358e4e --- /dev/null +++ b/conga-sling-plugin/src/main/java/io/wcm/devops/conga/plugins/sling/util/JsonOsgiConfigUtil.java @@ -0,0 +1,158 @@ +/* + * #%L + * wcm.io + * %% + * Copyright (C) 2024 wcm.io + * %% + * Licensed 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. + * #L% + */ +package io.wcm.devops.conga.plugins.sling.util; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Dictionary; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.sling.provisioning.model.Configuration; +import org.apache.sling.provisioning.model.Feature; +import org.apache.sling.provisioning.model.Model; +import org.apache.sling.provisioning.model.RunMode; +import org.apache.sling.provisioning.model.Section; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.wcm.devops.conga.plugins.sling.postprocessor.JsonOsgiConfigPostProcessor; + +/** + * Helper class for reading JSON files. + */ +public final class JsonOsgiConfigUtil { + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private static final TypeReference> MAP_TYPE_REFERENCE = new TypeReference>() { + // default implementation + }; + + private static final Pattern KEY_PATTERN_CONFIGURATIONS = Pattern.compile("^configurations(:(.*))?$"); + private static final Pattern KEY_PATTERN_REPOINIT = Pattern.compile("^repoinit(:(.*))?$"); + private static final int RUNMODES_INDEX = 2; + + private JsonOsgiConfigUtil() { + // static methods only + } + + /** + * Read JSON file content to a map. + * @param file JSON file + * @return Map containing JSON content + * @throws IOException I/O exception + */ + static Map readToMap(File file) throws IOException { + String jsonString = FileUtils.readFileToString(file, StandardCharsets.UTF_8); + return OBJECT_MAPPER.readValue(jsonString, MAP_TYPE_REFERENCE); + } + + /** + * Read JSON file content to a map. + * @param file JSON file + * @return Map containing JSON content + * @throws IOException I/O exception + */ + public static Model readToProvisioningModel(File file) throws IOException { + Model model = new Model(); + String featureName = StringUtils.substringBeforeLast(file.getName(), JsonOsgiConfigPostProcessor.FILE_EXTENSION); + Feature feature = model.getOrCreateFeature(featureName); + + Map data = readToMap(file); + for (Map.Entry entry : data.entrySet()) { + processEntry(feature, entry.getKey(), entry.getValue()); + } + + return model; + } + + @SuppressWarnings("unchecked") + private static void processEntry(Feature feature, String key, Object value) throws IOException { + Matcher configurationsKeyMatcher = KEY_PATTERN_CONFIGURATIONS.matcher(key); + if (configurationsKeyMatcher.matches()) { + if (value instanceof Map) { + String[] runModes = toRunModes(configurationsKeyMatcher.group(RUNMODES_INDEX)); + processOsgiConfiguration(feature, runModes, (Map)value); + } + else { + throw new IOException("Unexpected data for key " + key + ": " + value.getClass().getName()); + } + } + else { + Matcher repoinitKeyMatcher = KEY_PATTERN_REPOINIT.matcher(key); + if (repoinitKeyMatcher.matches()) { + if (value instanceof Collection) { + String[] runModes = toRunModes(repoinitKeyMatcher.group(RUNMODES_INDEX)); + processRepoInit(feature, runModes, (Collection)value); + } + else { + throw new IOException("Unexpected data for key " + key + ": " + value.getClass().getName()); + } + } + else { + throw new IOException("Invalid toplevel key in JSON file: " + key); + } + } + } + + private static String[] toRunModes(String runModesString) { + if (StringUtils.isBlank(runModesString)) { + return null; + } + return StringUtils.split(runModesString, ","); + } + + + @SuppressWarnings("unchecked") + private static void processOsgiConfiguration(Feature feature, String[] runModes, Map configurations) throws IOException { + RunMode runMode = feature.getOrCreateRunMode(runModes); + for (Map.Entry entry : configurations.entrySet()) { + String pid = entry.getKey(); + Object value = entry.getValue(); + if (value instanceof Map) { + Map configProperties = (Map)value; + Configuration config = runMode.getOrCreateConfiguration(pid, null); + Dictionary properties = config.getProperties(); + for (Map.Entry configProperty : configProperties.entrySet()) { + properties.put(configProperty.getKey(), configProperty.getValue()); + } + } + else { + throw new IOException("Unexpected configurations data for " + pid + ": " + value.getClass().getName()); + } + } + } + + private static void processRepoInit(Feature feature, String[] runModes, Collection repoinits) { + Section section = new Section(ProvisioningUtil.REPOINIT_SECTION); + feature.getAdditionalSections().add(section); + if (runModes != null) { + section.getAttributes().put(ProvisioningUtil.REPOINIT_PROPERTY_RUNMODES, StringUtils.join(runModes, ",")); + } + section.setContents(StringUtils.join(repoinits, "\n")); + } + +} diff --git a/conga-sling-plugin/src/main/java/io/wcm/devops/conga/plugins/sling/util/ProvisioningUtil.java b/conga-sling-plugin/src/main/java/io/wcm/devops/conga/plugins/sling/util/ProvisioningUtil.java index 80aeb32..0ddb467 100644 --- a/conga-sling-plugin/src/main/java/io/wcm/devops/conga/plugins/sling/util/ProvisioningUtil.java +++ b/conga-sling-plugin/src/main/java/io/wcm/devops/conga/plugins/sling/util/ProvisioningUtil.java @@ -20,13 +20,17 @@ package io.wcm.devops.conga.plugins.sling.util; import java.io.BufferedInputStream; +import java.io.File; import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; +import java.util.Dictionary; import java.util.List; import java.util.Objects; import java.util.SortedSet; @@ -43,7 +47,9 @@ import org.apache.sling.provisioning.model.Section; import org.apache.sling.provisioning.model.io.ModelReader; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.wcm.devops.conga.generator.spi.context.FileContext; +import io.wcm.devops.conga.generator.spi.context.PostProcessorContext; import io.wcm.devops.conga.generator.util.FileUtil; /** @@ -61,7 +67,8 @@ public final class ProvisioningUtil { */ public static final String TEXT_FILE_EXTENSION = "txt"; - private static final String REPOINIT_SECTION = "repoinit"; + static final String REPOINIT_SECTION = "repoinit"; + static final String REPOINIT_PROPERTY_RUNMODES = "runModes"; private static final String REPOINIT_PID = "org.apache.sling.jcr.repoinit.RepositoryInitializer"; private ProvisioningUtil() { @@ -135,7 +142,7 @@ public static List visitOsgiConfigurations(Model model, ConfigConsumer } // associated run modes - String runModesString = section.getAttributes().get("runModes"); + String runModesString = section.getAttributes().get(REPOINIT_PROPERTY_RUNMODES); RunMode runMode; if (runModesString != null) { runMode = new RunMode(StringUtils.split(runModesString, ",")); @@ -184,4 +191,31 @@ private static String getPathForConfiguration(Configuration configuration, RunMo return path.toString(); } + /** + * Generate OSGi configuration for all feature and run modes. + * @param model Provisioning Model + * @param dir Target directory + * @param context Post processor context + * @return Generated files + * @throws IOException I/O exception + */ + public static List generateOsgiConfigurations(Model model, File dir, PostProcessorContext context) throws IOException { + return ProvisioningUtil.visitOsgiConfigurations(model, new ConfigConsumer() { + + @Override + @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_BAD_PRACTICE") + public FileContext accept(String path, Dictionary properties) throws IOException { + context.getLogger().info(" Generate {}", path); + + File confFile = new File(dir, path); + confFile.getParentFile().mkdirs(); + try (FileOutputStream os = new FileOutputStream(confFile)) { + OsgiConfigUtil.write(os, properties); + } + + return new FileContext().file(confFile).charset(StandardCharsets.UTF_8); + } + }); + } + } diff --git a/conga-sling-plugin/src/main/resources/META-INF/services/io.wcm.devops.conga.generator.spi.PostProcessorPlugin b/conga-sling-plugin/src/main/resources/META-INF/services/io.wcm.devops.conga.generator.spi.PostProcessorPlugin index 6d33cbb..890b2dd 100644 --- a/conga-sling-plugin/src/main/resources/META-INF/services/io.wcm.devops.conga.generator.spi.PostProcessorPlugin +++ b/conga-sling-plugin/src/main/resources/META-INF/services/io.wcm.devops.conga.generator.spi.PostProcessorPlugin @@ -1 +1,2 @@ +io.wcm.devops.conga.plugins.sling.postprocessor.JsonOsgiConfigPostProcessor io.wcm.devops.conga.plugins.sling.postprocessor.ProvisioningOsgiConfigPostProcessor \ No newline at end of file diff --git a/conga-sling-plugin/src/test/java/io/wcm/devops/conga/plugins/sling/postprocessor/JsonOsgiConfigPostProcessorTest.java b/conga-sling-plugin/src/test/java/io/wcm/devops/conga/plugins/sling/postprocessor/JsonOsgiConfigPostProcessorTest.java new file mode 100644 index 0000000..0f57164 --- /dev/null +++ b/conga-sling-plugin/src/test/java/io/wcm/devops/conga/plugins/sling/postprocessor/JsonOsgiConfigPostProcessorTest.java @@ -0,0 +1,127 @@ +/* + * #%L + * wcm.io + * %% + * Copyright (C) 2024 wcm.io + * %% + * Licensed 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. + * #L% + */ +package io.wcm.devops.conga.plugins.sling.postprocessor; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Dictionary; +import java.util.List; + +import org.apache.commons.io.FileUtils; +import org.apache.felix.cm.json.io.Configurations; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.slf4j.LoggerFactory; + +import io.wcm.devops.conga.generator.spi.PostProcessorPlugin; +import io.wcm.devops.conga.generator.spi.context.FileContext; +import io.wcm.devops.conga.generator.spi.context.PluginContextOptions; +import io.wcm.devops.conga.generator.spi.context.PostProcessorContext; +import io.wcm.devops.conga.generator.util.PluginManagerImpl; + +class JsonOsgiConfigPostProcessorTest { + + private PostProcessorPlugin underTest; + + private File targetDir; + + @BeforeEach + void setUp(TestInfo testInfo) throws IOException { + underTest = new PluginManagerImpl().get(JsonOsgiConfigPostProcessor.NAME, PostProcessorPlugin.class); + + // prepare target dirctory + targetDir = new File("target/JsonOsgiConfigPostProcessorTest_" + testInfo.getDisplayName()); + if (targetDir.exists()) { + FileUtils.deleteDirectory(targetDir); + } + } + + @Test + void testJsonFile() throws Exception { + + // post process example valid provisioning file + File provisioningFile = new File(targetDir, "sample.osgiconfig.json"); + FileUtils.copyFile(new File(getClass().getResource("/osgi-config-json/sample.osgiconfig.json").toURI()), provisioningFile); + postProcess(provisioningFile); + + // validate generated configs + Dictionary config = readConfig("my.pid.cfg.json"); + assertEquals("value1", config.get("stringProperty")); + assertEquals(List.of("v1", "v2", "v3"), config.get("stringArrayProperty")); + assertEquals(true, config.get("booleanProperty")); + assertEquals(999999999999L, config.get("longProperty")); + + assertExists("my.factory-my.pid.cfg.json"); + assertExists("mode1/my.factory-my.pid2.cfg.json"); + assertExists("mode2/my.pid2.cfg.json"); + assertExists("publish.prod/my.pid2.cfg.json"); + + // validate repoinit statements + config = readConfig("org.apache.sling.jcr.repoinit.RepositoryInitializer-sample.cfg.json"); + assertArrayEquals(new String[] { "create path /repoinit/test1\n" + + "create path /repoinit/test2" }, (String[])config.get("scripts")); + + config = readConfig("mode1/org.apache.sling.jcr.repoinit.RepositoryInitializer-sample-mode1.cfg.json"); + assertArrayEquals(new String[] { "create service user mode1" }, (String[])config.get("scripts")); + + config = readConfig("mode1.mode2/org.apache.sling.jcr.repoinit.RepositoryInitializer-sample-mode1-mode2.cfg.json"); + assertArrayEquals(new String[] { "create service user mode1_mode2" }, (String[])config.get("scripts")); + } + + private void postProcess(File provisioningFile) { + // post-process + FileContext fileContext = new FileContext() + .file(provisioningFile) + .charset(StandardCharsets.UTF_8); + PluginContextOptions pluginContextOptions = new PluginContextOptions() + .pluginManager(new PluginManagerImpl()) + .logger(LoggerFactory.getLogger(ProvisioningOsgiConfigPostProcessor.class)); + PostProcessorContext context = new PostProcessorContext() + .pluginContextOptions(pluginContextOptions); + + assertTrue(underTest.accepts(fileContext, context)); + underTest.apply(fileContext, context); + + // validate + assertFalse(provisioningFile.exists(), "Combined JSON file deleted"); + } + + private Dictionary readConfig(String fileName) throws IOException { + assertExists(fileName); + File file = new File(targetDir, fileName); + try (FileReader reader = new FileReader(file, StandardCharsets.UTF_8)) { + return Configurations.buildReader().build(reader).readConfiguration(); + } + } + + private void assertExists(String fileName) throws IOException { + File file = new File(targetDir, fileName); + assertTrue(file.exists(), "Config file found: " + file.getCanonicalPath()); + } + +} diff --git a/conga-sling-plugin/src/test/java/io/wcm/devops/conga/plugins/sling/postprocessor/ProvisioningOsgiConfigPostProcessorTest.java b/conga-sling-plugin/src/test/java/io/wcm/devops/conga/plugins/sling/postprocessor/ProvisioningOsgiConfigPostProcessorTest.java index 1c0b609..f090573 100644 --- a/conga-sling-plugin/src/test/java/io/wcm/devops/conga/plugins/sling/postprocessor/ProvisioningOsgiConfigPostProcessorTest.java +++ b/conga-sling-plugin/src/test/java/io/wcm/devops/conga/plugins/sling/postprocessor/ProvisioningOsgiConfigPostProcessorTest.java @@ -55,7 +55,7 @@ void setUp(TestInfo testInfo) throws IOException { underTest = new PluginManagerImpl().get(ProvisioningOsgiConfigPostProcessor.NAME, PostProcessorPlugin.class); // prepare target dirctory - targetDir = new File("target/postprocessor-test_" + testInfo.getDisplayName()); + targetDir = new File("target/ProvisioningOsgiConfigPostProcessorTest_" + testInfo.getDisplayName()); if (targetDir.exists()) { FileUtils.deleteDirectory(targetDir); } diff --git a/conga-sling-plugin/src/test/java/io/wcm/devops/conga/plugins/sling/util/JsonOsgiConfigUtilTest.java b/conga-sling-plugin/src/test/java/io/wcm/devops/conga/plugins/sling/util/JsonOsgiConfigUtilTest.java new file mode 100644 index 0000000..68ca53b --- /dev/null +++ b/conga-sling-plugin/src/test/java/io/wcm/devops/conga/plugins/sling/util/JsonOsgiConfigUtilTest.java @@ -0,0 +1,39 @@ +/* + * #%L + * wcm.io + * %% + * Copyright (C) 2024 wcm.io + * %% + * Licensed 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. + * #L% + */ +package io.wcm.devops.conga.plugins.sling.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +class JsonOsgiConfigUtilTest { + + @Test + void testReadToMap() throws IOException { + Map content = JsonOsgiConfigUtil.readToMap(new File("src/test/resources/osgi-config-json/sample.osgiconfig.json")); + assertEquals(List.of("create service user mode1"), content.get("repoinit:mode1")); + } + +}