diff --git a/deltaspike/checkstyle-rules/pom.xml b/deltaspike/checkstyle-rules/pom.xml index 53901484c..ad977f820 100644 --- a/deltaspike/checkstyle-rules/pom.xml +++ b/deltaspike/checkstyle-rules/pom.xml @@ -53,4 +53,3 @@ Apache DeltaSpike CheckStyle-rules - diff --git a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/util/MapUtils.java b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/util/MapUtils.java new file mode 100644 index 000000000..a35e68611 --- /dev/null +++ b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/util/MapUtils.java @@ -0,0 +1,191 @@ +/* + * 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.deltaspike.core.util; + +import jakarta.enterprise.inject.Typed; +import java.util.HashMap; +import java.util.Map; +import java.util.StringJoiner; + +/** + * Utility to flatten nested Maps into a single level key:value pair set of properties. + * + * @since 2.0.1 + */ +@Typed +public abstract class MapUtils +{ + /** + * Don't construct this class. Only use the static methods. + */ + private MapUtils() + { + // Do nothing + } + + /** + * Calls {@link #flattenMapProperties(Map, boolean)} with indexed=false. + * + * @param input Map of properties that may contain nested Maps. + * @param Type of values the {@link Map} contains. + * @return Map of all properties indexed by their fully qualified names. + * @see #flattenMapProperties(Map, boolean) + */ + public static Map flattenMapProperties(final Map input) + { + return flattenMapProperties(input, false); + } + + /** + * Converts a {@link Map} of objects to a flattened {@link Map} of {@link String} values. + * + *

For example, with the given input:

+ * + *

+     * Map<String, Object> application = Map.of(
+     *     "name", "My App",
+     *     "prefixes", List.of(">", "$")
+     * );
+     *
+     * Map<String, Object> map = Map.of("application", application);
+     * Map<String, String> result = MapUtils.flattenMapProperties(map);
+     * 
+ * + * Will result in the following properties, assuming indexed is false: + * + *

+     * application.name=My App
+     * application.prefixes=>,$
+     * 
+ * + * If indexed is true, the result would be: + * + *

+     * application.name=My App
+     * application.prefixes[0]=>
+     * application.prefixes[1]=$
+     * 
+ * + * + * @param input Map of properties that may contain nested Maps. + * @param indexed If arrays are converted to multiple properties, or a comma separated list. + * @param Type of values the {@link Map} contains. + * @return Map of all properties indexed by their fully qualified names. + */ + public static Map flattenMapProperties(final Map input, final boolean indexed) + { + final Map result = new HashMap<>(); + flattenMapProperties(input, result, indexed); + return result; + } + + /** + * Calls {@link #flattenMapProperties(Map, Map, boolean, String)} with parameter prefix as null, since when we begin + * flattening the map, there is no prefix by default. + * + * @param input Map of properties that may contain nested Maps. + * @param output Map that all properties are added to. + * @param indexed If arrays are converted to multiple properties, or a comma separated list. + * @param Type of values the {@link Map} contains. + * @see #flattenMapProperties(Map, Map, boolean, String) + */ + private static void flattenMapProperties(final Map input, + final Map output, + final boolean indexed) + { + flattenMapProperties(input, output, indexed, null); + } + + /** + * @param input Map of properties that may contain nested Maps. + * @param output Map that all properties are added to. + * @param indexed If arrays are converted to multiple properties, or a comma separated list. + * @param prefix Name to prefix to any properties found on this level. + * @param Type of values the {@link Map} contains. + */ + private static void flattenMapProperties(final Map input, + final Map output, + final boolean indexed, + final String prefix) + { + input.forEach((key, value) -> + { + if (value == null) + { + return; + } + + final String k = (prefix == null) ? key : (prefix + '.' + key); + + if (value instanceof Map) + { + flattenMapProperties((Map) value, output, indexed, k); + } + else if (value instanceof Iterable) + { + addIterable((Iterable) value, k, output, indexed); + } + else + { + output.put(k, (output.containsKey(k)) ? output.get(k) + "," + value : value.toString()); + } + }); + } + + /** + * @param value Array of values that needs to be flattened. + * @param key Property name for this value. + * @param output Map that all properties are added to. + * @param indexed If arrays are converted to multiple properties, or a comma separated list. + * @param Type of values the {@link Map} contains. + */ + private static void addIterable(final Iterable value, + final String key, + final Map output, + final boolean indexed) + { + final StringJoiner joiner = new StringJoiner(","); + int index = 0; + + for (final Object o : value) + { + if (o instanceof Map) + { + final Map map = (Map) o; + + if (map.isEmpty()) + { + continue; + } + + final String keyPrefix = (indexed) ? key + "[" + index++ + "]" : key; + flattenMapProperties((Map) map, output, indexed, keyPrefix); + } + else + { + joiner.add(o.toString()); + } + } + + if (joiner.length() > 0) + { + output.put(key, joiner.toString()); + } + } +} diff --git a/deltaspike/core/api/src/test/java/org/apache/deltaspike/core/util/MapUtilsTest.java b/deltaspike/core/api/src/test/java/org/apache/deltaspike/core/util/MapUtilsTest.java new file mode 100644 index 000000000..867abfb4f --- /dev/null +++ b/deltaspike/core/api/src/test/java/org/apache/deltaspike/core/util/MapUtilsTest.java @@ -0,0 +1,332 @@ +/* + * 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.deltaspike.core.util; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class MapUtilsTest +{ + /** + *

+     * application:
+     *   name: MyApp
+     * database:
+     *   host: 127.0.0.1
+     *   username: seth
+     *   password: password
+     * 
+ */ + @Test + public void testFlattenMap() + { + Map application = new HashMap<>(); + application.put("name", "MyApp"); + + Map database = new HashMap<>(); + database.put("host", "127.0.0.1"); + database.put("username", "username"); + database.put("password", "password"); + + Map map = new HashMap<>(); + map.put("application", application); + map.put("database", database); + + Map result = MapUtils.flattenMapProperties(map); + + Assert.assertEquals(4, result.size()); + Assert.assertEquals("MyApp", result.get("application.name")); + Assert.assertEquals("127.0.0.1", result.get("database.host")); + Assert.assertEquals("username", result.get("database.username")); + Assert.assertEquals("password", result.get("database.password")); + } + + /** + *

+     * application:
+     *   name: Another App
+     *   database:
+     *     host: localhost
+     *     username: username
+     *     password: password
+     * 
+ */ + @Test + public void testFlattenNestedMap() + { + Map database = new HashMap<>(); + database.put("host", "localhost"); + database.put("username", "username"); + database.put("password", "password"); + + Map application = new HashMap<>(); + application.put("name", "Another App"); + application.put("database", database); + + Map map = new HashMap<>(); + map.put("application", application); + + Map result = MapUtils.flattenMapProperties(map); + + Assert.assertEquals(4, result.size()); + Assert.assertEquals("Another App", result.get("application.name")); + Assert.assertEquals("localhost", result.get("application.database.host")); + Assert.assertEquals("username", result.get("application.database.username")); + Assert.assertEquals("password", result.get("application.database.password")); + } + + /** + *

+     * application:
+     *   name: Yet Another App
+     *   prefixes:
+     *     - >
+     *     - $
+     * 
+ */ + @Test + public void testFlattenWithList() + { + List prefixes = new ArrayList<>(); + prefixes.add(">"); + prefixes.add("$"); + + Map application = new HashMap<>(); + application.put("name", "Yet Another App"); + application.put("prefixes", prefixes); + + Map map = new HashMap<>(); + map.put("application", application); + + Map result = MapUtils.flattenMapProperties(map); + + Assert.assertEquals(2, result.size()); + Assert.assertEquals("Yet Another App", result.get("application.name")); + Assert.assertEquals(">,$", result.get("application.prefixes")); + } + + /** + *

+     * application:
+     *   name: More Apps
+     *   messages:
+     *     - source: one
+     *       target: two
+     *     - source: three
+     *       target: four
+     * 
+ */ + @Test + public void testFlattenWithObjectArrayLists() + { + Map message1 = new HashMap<>(); + message1.put("source", "one"); + message1.put("target", "two"); + + Map message2 = new HashMap<>(); + message2.put("source", "three"); + message2.put("target", "four"); + + List> messages = new ArrayList<>(); + messages.add(message1); + messages.add(message2); + + Map application = new HashMap<>(); + application.put("name", "More Apps"); + application.put("messages", messages); + + Map map = new HashMap<>(); + map.put("application", application); + + Map result = MapUtils.flattenMapProperties(map); + + Assert.assertEquals(3, result.size()); + Assert.assertEquals("More Apps", result.get("application.name")); + Assert.assertEquals("one,three", result.get("application.messages.source")); + Assert.assertEquals("two,four", result.get("application.messages.target")); + } + + /** + * Ignore nested null values. + * + *

+     * application:
+     *   name: More Apps
+     *   messages:
+     *     - source: one
+     *       target: two
+     *     - source: null
+     *       target: null
+     *     - source: three
+     *       target: four
+     * 
+ */ + @Test + public void testFlattenWithObjectArrayListsWithNull() + { + Map message1 = new HashMap<>(); + message1.put("source", "one"); + message1.put("target", "two"); + + Map message2 = new HashMap<>(); + message2.put("source", null); + message2.put("target", null); + + Map message3 = new HashMap<>(); + message2.put("source", "three"); + message2.put("target", "four"); + + List> messages = new ArrayList<>(); + messages.add(message1); + messages.add(message2); + messages.add(message3); + + Map application = new HashMap<>(); + application.put("name", "More Apps"); + application.put("messages", messages); + + Map map = new HashMap<>(); + map.put("application", application); + + Map result = MapUtils.flattenMapProperties(map); + + Assert.assertEquals(3, result.size()); + Assert.assertEquals("More Apps", result.get("application.name")); + Assert.assertEquals("one,three", result.get("application.messages.source")); + Assert.assertEquals("two,four", result.get("application.messages.target")); + } + + /** + *

+     * application:
+     *   name: More Apps
+     *   messages:
+     *     - source: one
+     *       target: two
+     *     - source: three
+     *       target: four
+     * 
+ */ + @Test + public void testFlattenWithObjectArrayIndexed() + { + Map message1 = new HashMap<>(); + message1.put("source", "one"); + message1.put("target", "two"); + + Map message2 = new HashMap<>(); + message2.put("source", "three"); + message2.put("target", "four"); + + List> messages = new ArrayList<>(); + messages.add(message1); + messages.add(message2); + + Map application = new HashMap<>(); + application.put("name", "More Apps"); + application.put("messages", messages); + + Map map = new HashMap<>(); + map.put("application", application); + + Map result = MapUtils.flattenMapProperties(map, true); + + Assert.assertEquals(5, result.size()); + Assert.assertEquals("More Apps", result.get("application.name")); + Assert.assertEquals("one", result.get("application.messages[0].source")); + Assert.assertEquals("two", result.get("application.messages[0].target")); + Assert.assertEquals("three", result.get("application.messages[1].source")); + Assert.assertEquals("four", result.get("application.messages[1].target")); + } + + /** + * Ignore nested null values. + * + *

+     * application:
+     *   name: More Apps
+     *   messages:
+     *     - source: one
+     *       target: two
+     *     - source: null
+     *       target: null
+     *     - source: three
+     *       target: four
+     * 
+ */ + @Test + public void testFlattenWithObjectArrayIndexedWithNull() + { + Map message1 = new HashMap<>(); + message1.put("source", "one"); + message1.put("target", "two"); + + Map message2 = new HashMap<>(); + message2.put("source", "three"); + message2.put("target", "four"); + + List> messages = new ArrayList<>(); + messages.add(message1); + messages.add(message2); + + Map application = new HashMap<>(); + application.put("name", "More Apps"); + application.put("messages", messages); + + Map map = new HashMap<>(); + map.put("application", application); + + Map result = MapUtils.flattenMapProperties(map, true); + + Assert.assertEquals(5, result.size()); + Assert.assertEquals("More Apps", result.get("application.name")); + Assert.assertEquals("one", result.get("application.messages[0].source")); + Assert.assertEquals("two", result.get("application.messages[0].target")); + Assert.assertEquals("three", result.get("application.messages[1].source")); + Assert.assertEquals("four", result.get("application.messages[1].target")); + } + + /** + *

+     * application:
+     *   name: Null App
+     *   prefixes:
+     * 
+ */ + @Test + public void testWithNull() + { + Map application = new HashMap<>(); + application.put("name", "Null App"); + application.put("prefixes", null); + + Map map = new HashMap<>(); + map.put("application", application); + + Map result = MapUtils.flattenMapProperties(map); + + Assert.assertEquals(1, result.size()); + Assert.assertEquals("Null App", result.get("application.name")); + } +} diff --git a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/EnvironmentPropertyConfigSource.java b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/EnvironmentPropertyConfigSource.java index c16e8f438..c0b549a9f 100644 --- a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/EnvironmentPropertyConfigSource.java +++ b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/EnvironmentPropertyConfigSource.java @@ -18,8 +18,6 @@ */ package org.apache.deltaspike.core.impl.config; - - /** * {@link org.apache.deltaspike.core.spi.config.ConfigSource} * which uses {@link System#getenv()} @@ -36,7 +34,6 @@ class EnvironmentPropertyConfigSource extends MapConfigSource initOrdinal(300); } - /** * {@inheritDoc} */ diff --git a/deltaspike/modules/pom.xml b/deltaspike/modules/pom.xml index 2f8430ece..60a6fba3f 100644 --- a/deltaspike/modules/pom.xml +++ b/deltaspike/modules/pom.xml @@ -43,5 +43,6 @@ data scheduler test-control + yaml diff --git a/deltaspike/modules/yaml/api/pom.xml b/deltaspike/modules/yaml/api/pom.xml new file mode 100644 index 000000000..abbb6b59b --- /dev/null +++ b/deltaspike/modules/yaml/api/pom.xml @@ -0,0 +1,42 @@ + + + + 4.0.0 + + + org.apache.deltaspike.modules + yaml-module-project + 2.0.1-SNAPSHOT + + + deltaspike-yaml-module-api + + Apache DeltaSpike YAML-Module API + + + + org.apache.deltaspike.core + deltaspike-core-api + ${project.version} + + + + diff --git a/deltaspike/modules/yaml/impl/pom.xml b/deltaspike/modules/yaml/impl/pom.xml new file mode 100644 index 000000000..769182864 --- /dev/null +++ b/deltaspike/modules/yaml/impl/pom.xml @@ -0,0 +1,59 @@ + + + + 4.0.0 + + + org.apache.deltaspike.modules + yaml-module-project + 2.0.1-SNAPSHOT + + + deltaspike-yaml-module-impl + jar + Apache DeltaSpike YAML-Module Impl + + org.apache.deltaspike.yaml.impl.* + !org.apache.deltaspike.yaml.impl.*,* + META-INF/beans.xml + + + + org.apache.deltaspike.core + deltaspike-core-api + ${project.version} + + + + org.apache.deltaspike.core + deltaspike-core-impl + ${project.version} + + + + org.yaml + snakeyaml + 2.4 + compile + + + + diff --git a/deltaspike/modules/yaml/impl/src/main/java/org/apache/deltaspike/yaml/impl/YamlConfigSource.java b/deltaspike/modules/yaml/impl/src/main/java/org/apache/deltaspike/yaml/impl/YamlConfigSource.java new file mode 100644 index 000000000..c26dbf878 --- /dev/null +++ b/deltaspike/modules/yaml/impl/src/main/java/org/apache/deltaspike/yaml/impl/YamlConfigSource.java @@ -0,0 +1,164 @@ +/* + * 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.deltaspike.yaml.impl; + +import org.apache.deltaspike.core.impl.config.MapConfigSource; +import org.apache.deltaspike.core.util.MapUtils; + +import java.io.InputStream; +import java.util.Map; +import java.util.Objects; + +/** + * You can create a configuration with a custom name by extending this class, and calling super() with the new configuration name. + * + *

+ * public class CustomYamlConfigSource extends YamlConfigSource
+ * {
+ *     public CustomYamlConfigSource()
+ *     {
+ *         super("custom_application.yml");
+ *     }
+ * }
+ * 
+ * + *

This will seek out my_application.yml, instead of application.yml.

+ * + * @since 2.0.1 + */ +public class YamlConfigSource extends MapConfigSource +{ + /** Default configuration name. Already available if this class is in your classpath. */ + private static final String DEFAULT_FILE_PATH = "application.yml"; + + /** Configuration file/stream name that the {@link YamlConfigSource} is for. */ + private final String configName; + + /** + * @see #isIndexed() + */ + private final boolean indexed; + + /** Constructs {@link YamlConfigSource} with {@link #DEFAULT_FILE_PATH}. */ + public YamlConfigSource() + { + this(DEFAULT_FILE_PATH); + } + + /** + * Calls {@link #YamlConfigSource(String, boolean)} with the parameter indexed set to false. + * + * @param configPath The file path relative to the classpath of the configuration. + * @throws NullPointerException If configPath is null. + * @see #YamlConfigSource(String, boolean) + */ + public YamlConfigSource(String configPath) + { + this(configPath, false); + } + + /** + * @param configPath File relative to the classpath of the configuration. + * @param indexed If this configuration should used indexed keys, or lists. + * @throws NullPointerException If configPath is null. + */ + public YamlConfigSource(String configPath, boolean indexed) + { + this(new YamlStringFunction().apply(configPath), configPath, indexed); + } + + /** + * @param inputStream Input stream to read the configuration from. + */ + public YamlConfigSource(InputStream inputStream) + { + this(inputStream, false); + } + + /** + * @param inputStream Input stream to read the configuration from. + * @param indexed If this configuration should used indexed keys, or lists. + */ + public YamlConfigSource(InputStream inputStream, boolean indexed) + { + this(inputStream, "input-stream", indexed); + } + + /** + * @param inputStream Input stream to read the configuration from. + * @param configName File path relative to the classpath of the configuration. + * @param indexed If this configuration should used indexed keys, or lists. + * @throws NullPointerException If configName is null. + */ + public YamlConfigSource(InputStream inputStream, String configName, boolean indexed) + { + this(new YamlInputStreamFunction().apply(inputStream), configName, indexed); + } + + /** + * @param map {@link Map}, which may include nested maps, of configuration properties. + * @param configName File path relative to the classpath of the configuration. + * @param indexed If this configuration should used indexed keys, or lists. + */ + private YamlConfigSource(Map map, String configName, boolean indexed) + { + super(MapUtils.flattenMapProperties(map, indexed)); + this.configName = Objects.requireNonNull(configName); + this.indexed = indexed; + } + + @Override + public String getConfigName() + { + return "yaml " + configName; + } + + /** + * If indexed is true, when we get arrays of objects such as: + * + *

+     * messages:
+     *   - source: one
+     *     target: two
+     *   - source: three
+     *     target: four
+     * 
+ * + * It will return: + *

+     * messages[0].source=one
+     * messages[0].target=two
+     * messages[1].source=three
+     * messages[2].target=four
+     * 
+ * + *

While if this is false (default), it would return:

+ * + *

+     * messages.source=one,three
+     * messages.target=two,four
+     * 
+ * + * @return If this {@link org.apache.deltaspike.core.spi.config.ConfigSource} uses indexed arrays, or comma separated values. + */ + public boolean isIndexed() + { + return indexed; + } +} diff --git a/deltaspike/modules/yaml/impl/src/main/java/org/apache/deltaspike/yaml/impl/YamlInputStreamFunction.java b/deltaspike/modules/yaml/impl/src/main/java/org/apache/deltaspike/yaml/impl/YamlInputStreamFunction.java new file mode 100644 index 000000000..33e3846df --- /dev/null +++ b/deltaspike/modules/yaml/impl/src/main/java/org/apache/deltaspike/yaml/impl/YamlInputStreamFunction.java @@ -0,0 +1,54 @@ +/* + * 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.deltaspike.yaml.impl; + +import org.yaml.snakeyaml.Yaml; + +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Java requires super be the first call of a method; we use this {@link Function} to perform more methods calls while calling super. + * + * @since 2.0.1 + */ +public class YamlInputStreamFunction implements Function> +{ + private final Logger log = Logger.getLogger(YamlInputStreamFunction.class.getName()); + + /** + * @param inputStream Input stream to read the YAML configuration from. + * @return Nested map representing all YAML properties. + */ + @Override + public Map apply(InputStream inputStream) + { + if (inputStream != null) + { + return new Yaml().load(inputStream); + } + + log.log(Level.WARNING, "Using YamlConfigSource, but the stream was null."); + return new HashMap<>(); + } +} diff --git a/deltaspike/modules/yaml/impl/src/main/java/org/apache/deltaspike/yaml/impl/YamlStringFunction.java b/deltaspike/modules/yaml/impl/src/main/java/org/apache/deltaspike/yaml/impl/YamlStringFunction.java new file mode 100644 index 000000000..47be4e450 --- /dev/null +++ b/deltaspike/modules/yaml/impl/src/main/java/org/apache/deltaspike/yaml/impl/YamlStringFunction.java @@ -0,0 +1,62 @@ +/* + * 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.deltaspike.yaml.impl; + +import org.yaml.snakeyaml.Yaml; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Java requires super be the first call of a method; we use this {@link Function} to perform more methods calls while calling super. + * + * @since 2.0.1 + */ +public class YamlStringFunction implements Function> +{ + private final Logger log = Logger.getLogger(YamlStringFunction.class.getName()); + + /** + * @param configPath Path to the configuration file. + * @return Nested map representing all YAML properties. + */ + @Override + public Map apply(String configPath) + { + try (InputStream inputStream = YamlConfigSource.class.getClassLoader().getResourceAsStream(configPath)) + { + if (inputStream != null) + { + return new Yaml().load(inputStream); + } + } + catch (IOException ex) + { + log.log(Level.SEVERE, "IOException occurred while reading YAML.", ex); + } + + log.log(Level.WARNING, "Using YamlConfigSource, but {0} was not found on the classpath.", configPath); + return new HashMap<>(); + } +} diff --git a/deltaspike/modules/yaml/impl/src/main/resources/META-INF/services/org.apache.deltaspike.core.spi.config.ConfigSource b/deltaspike/modules/yaml/impl/src/main/resources/META-INF/services/org.apache.deltaspike.core.spi.config.ConfigSource new file mode 100644 index 000000000..cb42e6aac --- /dev/null +++ b/deltaspike/modules/yaml/impl/src/main/resources/META-INF/services/org.apache.deltaspike.core.spi.config.ConfigSource @@ -0,0 +1,20 @@ +##################################################################################### +# 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. +##################################################################################### + +org.apache.deltaspike.yaml.impl.YamlConfigSource diff --git a/deltaspike/modules/yaml/impl/src/test/java/org/apache/deltaspike/yaml/impl/YamlConfigSourceTest.java b/deltaspike/modules/yaml/impl/src/test/java/org/apache/deltaspike/yaml/impl/YamlConfigSourceTest.java new file mode 100644 index 000000000..ac29ce6e9 --- /dev/null +++ b/deltaspike/modules/yaml/impl/src/test/java/org/apache/deltaspike/yaml/impl/YamlConfigSourceTest.java @@ -0,0 +1,93 @@ +/* + * 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.deltaspike.yaml.impl; + +import org.junit.Assert; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +public class YamlConfigSourceTest +{ + /** + * It should not throw an error in the case this file does not exist. + */ + @Test + public void exceptionNotThrownOnDefaultConfiguration() + { + try + { + new YamlConfigSource(); + } + catch (Exception ex) + { + Assert.fail(); + } + } + + @Test + public void testThatInputStreamWorks() throws IOException + { + String yaml = + "application:\n" + + " name: Testing"; + + try (InputStream stream = new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8))) + { + YamlConfigSource config = new YamlConfigSource(stream); + + Assert.assertEquals("yaml input-stream", config.getConfigName()); + Assert.assertFalse(config.isIndexed()); + Assert.assertEquals("Testing", config.getPropertyValue("application.name")); + } + } + + @Test + public void testInputStreamWithCustomName() throws IOException + { + String yaml = + "application:\n" + + " name: Testing"; + + try (InputStream stream = new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8))) + { + YamlConfigSource config = new YamlConfigSource(stream, "custom-stream", true); + + Assert.assertEquals("yaml custom-stream", config.getConfigName()); + Assert.assertTrue(config.isIndexed()); + Assert.assertEquals("Testing", config.getPropertyValue("application.name")); + } + } + + @Test + public void testWithNullInputStream() + { + try + { + new YamlConfigSource((InputStream)null); + } + catch (Exception ex) + { + Assert.fail(); + } + } +} diff --git a/deltaspike/modules/yaml/pom.xml b/deltaspike/modules/yaml/pom.xml new file mode 100644 index 000000000..ca5ab0cb6 --- /dev/null +++ b/deltaspike/modules/yaml/pom.xml @@ -0,0 +1,41 @@ + + + + 4.0.0 + + + org.apache.deltaspike.modules + modules-project + 2.0.1-SNAPSHOT + + + org.apache.deltaspike.modules + yaml-module-project + 2.0.1-SNAPSHOT + pom + + Apache DeltaSpike YAML-Module + + + api + impl + +