You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tamaya.apache.org by st...@apache.org on 2015/01/07 23:21:37 UTC
[4/9] incubator-tamaya git commit: TAMAYA-49 move api and core to
java8 module
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/328a4ac7/java8/api/pom.xml
----------------------------------------------------------------------
diff --git a/java8/api/pom.xml b/java8/api/pom.xml
new file mode 100644
index 0000000..6f40b45
--- /dev/null
+++ b/java8/api/pom.xml
@@ -0,0 +1,43 @@
+<!--
+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 current 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>
+
+ <parent>
+ <groupId>org.apache.tamaya</groupId>
+ <artifactId>tamaya-java8</artifactId>
+ <version>0.2-incubating-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>tamaya-api</artifactId>
+ <description>
+ The API defines a complete SE based API for reading of configuration data.
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/328a4ac7/java8/api/src/main/java/org/apache/tamaya/ConfigException.java
----------------------------------------------------------------------
diff --git a/java8/api/src/main/java/org/apache/tamaya/ConfigException.java b/java8/api/src/main/java/org/apache/tamaya/ConfigException.java
new file mode 100644
index 0000000..bac2ef4
--- /dev/null
+++ b/java8/api/src/main/java/org/apache/tamaya/ConfigException.java
@@ -0,0 +1,44 @@
+/*
+ * 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.tamaya;
+
+/**
+ * Exception class (runtime exception) for configuration issues.
+ */
+public class ConfigException extends RuntimeException{
+
+ private static final long serialVersionUID = -5886094818057522680L;
+
+ /**
+ * Creates a new configuration exception.
+ * @param message the exception message, not null.
+ */
+ public ConfigException(String message){
+ super(message);
+ }
+
+ /**
+ * Creates a new configuration exception.
+ * @param message the exception message, not null.
+ * @param t the throwable.
+ */
+ public ConfigException(String message, Throwable t){
+ super(message, t);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/328a4ac7/java8/api/src/main/java/org/apache/tamaya/Configuration.java
----------------------------------------------------------------------
diff --git a/java8/api/src/main/java/org/apache/tamaya/Configuration.java b/java8/api/src/main/java/org/apache/tamaya/Configuration.java
new file mode 100644
index 0000000..2786af7
--- /dev/null
+++ b/java8/api/src/main/java/org/apache/tamaya/Configuration.java
@@ -0,0 +1,204 @@
+/*
+ * 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.tamaya;
+
+import org.apache.tamaya.spi.PropertyConverter;
+import org.apache.tamaya.spi.ServiceContext;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.function.UnaryOperator;
+
+/**
+ * A configuration models a aggregated set current properties, identified by a unique key, but adds higher level access functions to
+ * a {@link org.apache.tamaya.spi.PropertySource}. Hereby in most cases a configuration is a wrapper around a composite
+ * {@link org.apache.tamaya.spi.PropertySource} instance, which may combine multiple child config in well defined tree like structure,
+ * where nodes define logically the rules current priority, filtering, combination and overriding.
+ * <br/>
+ * <h3>Implementation Requirements</h3>
+ * Implementations current this interface must be
+ * <ul>
+ * <li>Thread safe.
+ * <li>Immutable
+ * </ul>
+ * It is not recommended that implementations also are serializable, since the any configuration can be <i>freezed</i>
+ * by reading out its complete configuration map into a serializable and remotable structure. This helps significantly
+ * simplifying the development current this interface, e.g. for being backed up by systems and stores that are not part current
+ * this library at all.
+ */
+public interface Configuration {
+
+ /**
+ * Access a property.
+ *
+ * @param key the property's key, not null.
+ * @return the property's keys.
+ */
+ Optional<String> get(String key);
+
+ /**
+ * Access all current known Configuration properties as a full {@code Map<String,String>}.
+ * Be aware that entries from non scannable parts of the registered {@link org.apache.tamaya.spi.PropertySource}
+ * instances may not be contained in the result, but nevertheless be accessible calling one of the
+ * {@code get(...)} methods.
+ */
+ Map<String,String> getProperties();
+
+ /**
+ * Get the property keys as type T. This will implicitly require a corresponding {@link
+ * org.apache.tamaya.spi.PropertyConverter} to be available that is capable current providing type T
+ * fromMap the given String keys.
+ *
+ * @param key the property's absolute, or relative path, e.g. @code
+ * a/b/c/d.myProperty}.
+ * @param type The target type required, not null.
+ * @return the property value, never null..
+ * @throws ConfigException if the keys could not be converted to the required target
+ * type.
+ */
+ <T> Optional<T> get(String key, Class<T> type);
+
+ /**
+ * Get the property keys as type {@code Class<T>}.
+ * <p>
+ * If {@code Class<T>} is not one current
+ * {@code Boolean, Short, Integer, Long, Float, Double, BigInteger,
+ * BigDecimal, String} , an according converter must be
+ * available to perform the conversion fromMap {@link String} to
+ * {@code Class<T>}.
+ *
+ * @param key the property's absolute, or relative path, e.g. @code
+ * a/b/c/d.myProperty}.
+ * @param converter the PropertyConverter to perform the conversion fromMap
+ * {@link String} to {@code Class<T>}, not {@code null}.
+ * @return the property's keys.
+ * @throws ConfigException if the keys could not be converted to the required target
+ * type, or no such property exists.
+ */
+ default <T> Optional<T> get(String key, PropertyConverter<T> converter) {
+ Optional<String> value = get(key);
+ if (value.isPresent()) {
+ return Optional.ofNullable(converter.convert(value.get()));
+ }
+ return Optional.empty();
+ }
+
+
+ /**
+ * Get the property keys as {@link Boolean}.
+ *
+ * @param key the property's absolute, or relative path, e.g. {@code
+ * a/b/c/d.myProperty}.
+ * @return the property's keys.
+ * @throws ConfigException if the configured value could not be converted to the target type.
+ */
+ default Boolean getBoolean(String key) {
+ Optional<Boolean> val = get(key, Boolean.class);
+ if (val.isPresent()) {
+ return val.get();
+ }
+ return null;
+ }
+
+ /**
+ * Get the property keys as {@link Integer}.
+ *
+ * @param key the property's absolute, or relative path, e.g. @code
+ * a/b/c/d.myProperty}.
+ * @return the property's keys.
+ * @throws ConfigException if the configured value could not be converted to the target type.
+ */
+ default OptionalInt getInteger(String key) {
+ Optional<Integer> val = get(key, Integer.class);
+ if (val.isPresent()){
+ return OptionalInt.of(val.get());
+ }
+ return OptionalInt.empty();
+ }
+
+
+ /**
+ * Get the property keys as {@link Long}.
+ *
+ * @param key the property's absolute, or relative path, e.g. @code
+ * a/b/c/d.myProperty}.
+ * @return the property's keys.
+ * @throws ConfigException if the configured value could not be converted to the target type.
+ */
+ default OptionalLong getLong(String key) {
+ Optional<Long> val = get(key, Long.class);
+ if (val.isPresent()){
+ return OptionalLong.of(val.get());
+ }
+ return OptionalLong.empty();
+ }
+
+
+ /**
+ * Get the property keys as {@link Double}.
+ *
+ * @param key the property's absolute, or relative path, e.g. @code
+ * a/b/c/d.myProperty}.
+ * @return the property's keys.
+ * @throws ConfigException if the configured value could not be converted to the target type.
+ */
+ default OptionalDouble getDouble(String key) {
+
+ Optional<Double> val = get(key, Double.class);
+ if (val.isPresent()){
+ return OptionalDouble.of(val.get());
+ }
+ return OptionalDouble.empty();
+ }
+
+
+ /**
+ * Extension point for adjusting configuration.
+ *
+ * @param operator A configuration operator, e.g. a filter, or an adjuster
+ * combining configurations.
+ * @return the new adjusted configuration, never {@code null}.
+ */
+ default Configuration with(UnaryOperator<Configuration> operator) {
+ return operator.apply(this);
+ }
+
+
+ /**
+ * Query a configuration.
+ *
+ * @param query the query, never {@code null}.
+ * @return the result
+ */
+ default <T> T query(Function<Configuration,T> query) {
+ return query.apply(this);
+ }
+
+
+ /**
+ * Access a configuration.
+ *
+ * @return the corresponding Configuration instance, never null.
+ * @throws ConfigException if no such configuration is defined.
+ */
+ public static Configuration current(){
+ return ServiceContext.getInstance().getService(Configuration.class).get();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/328a4ac7/java8/api/src/main/java/org/apache/tamaya/spi/ConfigurationContext.java
----------------------------------------------------------------------
diff --git a/java8/api/src/main/java/org/apache/tamaya/spi/ConfigurationContext.java b/java8/api/src/main/java/org/apache/tamaya/spi/ConfigurationContext.java
new file mode 100644
index 0000000..7b51717
--- /dev/null
+++ b/java8/api/src/main/java/org/apache/tamaya/spi/ConfigurationContext.java
@@ -0,0 +1,145 @@
+/*
+ * 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.tamaya.spi;
+
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Central SPI for programmatically dealing with the setup of the configuration system.
+ * This includes adding and enlisting {@link org.apache.tamaya.spi.PropertySource}s,
+ * managing {@link org.apache.tamaya.spi.PropertyConverter}s, ConfigFilters, etc.
+ */
+public interface ConfigurationContext {
+
+ /**
+ * This method can be used for programmatically adding {@link PropertySource}s.
+ * It is not needed for normal 'usage' by end users, but only for Extension Developers!
+ *
+ * @param propertySourcesToAdd the PropertySources to add
+ */
+ void addPropertySources(PropertySource... propertySourcesToAdd);
+
+ /**
+ * This method returns the current list of registered PropertySources ordered via their ordinal.
+ * PropertySources with a lower ordinal come last. The PropertySource with the
+ * highest ordinal comes first.
+ * If two PropertySources have the same ordinal number they will get sorted
+ * using their class name just to ensure the user at least gets the same ordering
+ * after a JVM restart, hereby names before are added last.
+ * PropertySources are loaded when this method is called the first time, which basically is
+ * when the first time configuration is accessed.
+ *
+ * @return a sorted list of registered PropertySources. The returned list need not be modifiable
+ */
+ List<PropertySource> getPropertySources();
+
+
+ /**
+ * This method can be used for programmatically adding {@link PropertyConverter}s.
+ * It is not needed for normal 'usage' by end users, but only for Extension Developers!
+ *
+ * @param typeToConvert the type which the converter is for
+ * @param propertyConverter the PropertyConverters to add for this type
+ */
+ <T> void addPropertyConverter(Class<T> typeToConvert, PropertyConverter<T> propertyConverter);
+
+ /**
+ * <p>
+ * This method returns the Map of registered PropertyConverters
+ * per type.
+ * The List for each type is ordered via their {@link javax.annotation.Priority} and
+ * cladd name. Refer also to {@link #getPropertyConverters()}.
+ * </p>
+ * <p>
+ * A simplified scenario could be like:
+ * <pre>
+ * {
+ * Date.class -> {StandardDateConverter, TimezoneDateConverter, MyCustomDateConverter }
+ * Boolean.class -> {StandardBooleanConverter, FrenchBooleanConverter}
+ * Integer.class -> {DynamicDefaultConverter}
+ * }
+ * </pre>
+ * </p>
+ *
+ * @return map with sorted list of registered PropertySources per type.
+ */
+ Map<Class<?>, List<PropertyConverter<?>>> getPropertyConverters();
+
+ /**
+ * <p>
+ * This method returns the registered PropertyConverters for a given type.
+ * The List for each type is ordered via their {@link javax.annotation.Priority}.
+ * </p>
+ *
+ * <p>
+ * PropertyConverters with a higher Priority come first. The PropertyConverter with the
+ * lowest Priority comes last.
+ * If two PropertyConverter have the same ordinal number they will get sorted
+ * using their class name just to ensure the user at least gets the same ordering
+ * after a JVM restart.
+ * </p>
+ *
+ * <p>
+ * Additionally if a PropertyProvider is accessed, which is not registered the implementation
+ * should try to figure out, if there could be a default implementation as follows:
+ * <ol>
+ * <le>Look for static factory methods: {@code of(String), valueOf(String), getInstance(String),
+ * instanceOf(String), fomr(String)}</le>
+ * <le>Look for a matching constructor: {@code T(String)}.</le>
+ * </ol>
+ * If a correspoding factory method or constructor could be found, a corresponding
+ * PropertyConverter should be created and registered automatically for the given
+ * type.
+ * </p>
+ *
+ * <p>
+ * The scenario could be like:
+ * <pre>
+ * {
+ * Date.class -> {MyCustomDateConverter,StandardDateConverter, TimezoneDateConverter}
+ * Boolean.class -> {StandardBooleanConverter, FrenchBooleanConverter}
+ * Integer.class -> {DynamicDefaultConverter}
+ * }
+ * </pre>
+ * </p>
+ *
+ * <p>
+ * The converters returned for a type should be used as a chain, whereas the result of the
+ * first converter that is able to convert the configured value, is taken as the chain's result.
+ * No more converters are called after a converter has successfully converted the input into
+ * the required target type.
+ * </p>
+ *
+ * @return a sorted list of registered PropertySources per type.
+ */
+ <T> List<PropertyConverter<T>> getPropertyConverters(Class<T> type);
+
+ /**
+ * Access the current PropertyFilter instances.
+ * @return the list of registered PropertyFilters, never null.
+ */
+ List<PropertyFilter> getPropertyFilters();
+
+ public static ConfigurationContext context(){
+ return ServiceContext.getInstance().getService(ConfigurationContext.class).get();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/328a4ac7/java8/api/src/main/java/org/apache/tamaya/spi/PropertyConverter.java
----------------------------------------------------------------------
diff --git a/java8/api/src/main/java/org/apache/tamaya/spi/PropertyConverter.java b/java8/api/src/main/java/org/apache/tamaya/spi/PropertyConverter.java
new file mode 100644
index 0000000..9c41983
--- /dev/null
+++ b/java8/api/src/main/java/org/apache/tamaya/spi/PropertyConverter.java
@@ -0,0 +1,42 @@
+/*
+ * 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.tamaya.spi;
+
+
+/**
+ * Interface for an property that converts a configured String into something else.
+ * This is used for implementing type conversion from a property (String) to a certain target
+ * type. Hereby the target type can be multivalued (eg collections) or complex if needed.
+ */
+@FunctionalInterface
+public interface PropertyConverter<T>{
+
+ /**
+ * Convert the given configuration keys from it' String representation into the required target type.
+ * @param value the configuration keys
+ * @return converted keys
+ */
+ T convert(String value);
+
+ //X TODO probably add some diagnostic info which explains what kind of
+ //X format(s) is supported.
+ //X This could be useful if e.g. no converter in the chain felt responsible
+ //X because a wrongly formatted configuration string had been used.
+ //X This could probably also be handled via an additional Annotation on the converter.
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/328a4ac7/java8/api/src/main/java/org/apache/tamaya/spi/PropertyFilter.java
----------------------------------------------------------------------
diff --git a/java8/api/src/main/java/org/apache/tamaya/spi/PropertyFilter.java b/java8/api/src/main/java/org/apache/tamaya/spi/PropertyFilter.java
new file mode 100644
index 0000000..50a03dd
--- /dev/null
+++ b/java8/api/src/main/java/org/apache/tamaya/spi/PropertyFilter.java
@@ -0,0 +1,49 @@
+/*
+ * 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.tamaya.spi;
+
+
+/**
+ * <p>Interface for filtering the current map of properties during the evaluation of the chain of PropertySources.
+ * Filters can be registered using the {@link org.apache.tamaya.spi.ServiceContext}. The ordinal
+ * hereby is defined by the corresponding {@code @Priority} annotation.</p>
+ * <p>Filters </p>
+ */
+public interface PropertyFilter{
+
+ /**
+ * <p>Maps the current {@code valueToBeFiltered} value to a new value. The resulting value will be used as the result
+ * passed to the user.</p>
+ * <p>If a filter is currently not available, it should just pass the input map to the method's
+ * output.</p>
+ * <p>Returning {@code null} will remove the entry and Optional.empty() will be returned to the user.</p>
+ * <h3>Implementation specification</h3>
+ * Implementations of this class must be
+ * <ul>
+ * <li>reentrant</li>
+ * <li>thread-safe</li>
+ * </ul>
+ *
+ * @param key the key accessed, not null.
+ * @param valueToBeFiltered the value to be filtered, not null.
+ * @return the filtered map, never null.
+ */
+ String filterProperty(String key, String valueToBeFiltered);
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/328a4ac7/java8/api/src/main/java/org/apache/tamaya/spi/PropertySource.java
----------------------------------------------------------------------
diff --git a/java8/api/src/main/java/org/apache/tamaya/spi/PropertySource.java b/java8/api/src/main/java/org/apache/tamaya/spi/PropertySource.java
new file mode 100644
index 0000000..ae80159
--- /dev/null
+++ b/java8/api/src/main/java/org/apache/tamaya/spi/PropertySource.java
@@ -0,0 +1,128 @@
+/*
+* 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.tamaya.spi;
+
+
+import java.util.Map;
+import java.util.Optional;
+
+
+/**
+ * This interface models a provider that serves configuration properties. The contained
+ * properties may be read fromMap single or several sources (composite).<br/>
+ * PropertySources are the building blocks of the final configuration.
+ * <p/>
+ * <h3>Implementation Requirements</h3>
+ * <p></p>Implementations current this interface must be
+ * <ul>
+ * <li>Thread safe.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * <p>A PropertySourceProvider will get picked up via the
+ * {@link java.util.ServiceLoader} mechanism and can be registered via
+ * META-INF/services/org.apache.tamaya.spi.PropertySource
+ * </p>
+ * <p>
+ * If you like to register multiple PropertySources at the same time
+ * you can use the {@link org.apache.tamaya.spi.PropertySourceProvider}
+ * interface.
+ * </p>
+ */
+public interface PropertySource {
+
+ /**
+ * property name to override default tamaya ordinals
+ */
+ static final String TAMAYA_ORDINAL = "tamaya.ordinal";
+
+
+ /**
+ * Lookup order:
+ * TODO rethink whole default PropertySources and ordering:
+ * TODO introduce default values or constants for ordinals
+ * <ol>
+ * <li>System properties (ordinal 400)</li>
+ * <li>Environment properties (ordinal 300)</li>
+ * <li>JNDI values (ordinal 200)</li>
+ * <li>Properties file values (/META-INF/applicationConfiguration.properties) (ordinal 100)</li>
+ * </ol>
+ * <p/>
+ * <p><b>Important Hints for custom implementations</b>:</p>
+ * <p>
+ * If a custom implementation should be invoked <b>before</b> the default implementations, use a value > 400
+ * </p>
+ * <p>
+ * If a custom implementation should be invoked <b>after</b> the default implementations, use a value < 100
+ * </p>
+ * <p/>
+ * <p>Reordering of the default order of the config-sources:</p>
+ * <p>Example: If the properties file/s should be used <b>before</b> the other implementations,
+ * you have to configure an ordinal > 400. That means, you have to add e.g. deltaspike_ordinal=401 to
+ * /META-INF/apache-deltaspike.properties . Hint: In case of property files every file is handled as independent
+ * config-source, but all of them have ordinal 400 by default (and can be reordered in a fine-grained manner.</p>
+ *
+ * @return the 'importance' aka ordinal of the configured values. The higher, the more important.
+ * //X TODO think about making this a default method which returns default priority
+ */
+ int getOrdinal();
+
+
+ /**
+ * Get the name of the property source. The name should be unique for the type of source, whereas the id is used
+ * to ensure unique identity, either locally or remotely.
+ * @return the configuration's name, never null.
+ */
+ String getName();
+
+ /**
+ * Access a property.
+ *
+ * //X TODO discuss if the key can be null
+ * @param key the property's key, not null.
+ * @return the property's keys.
+ */
+ Optional<String> get(String key);
+
+ /**
+ * Access the current properties as Map. The resulting Map may not return all items accessible, e.g.
+ * when the underlying storage does not support iteration of its entries.
+ *
+ * @return the a corresponding map, never null.
+ * //X TODO or should we just do getPropertyKeys()? Think about security (key) vs easier merging (full map)?
+ */
+ Map<String,String> getProperties();
+
+ /**
+ * Determines if this config source could be scanned for its list of properties.
+ *
+ * <p>
+ * PropertySources which are not scannable might not be able to find all the
+ * configured values to provide via {@link #getProperties()}. This can e.g. happen
+ * if the underlying storage doesn't support listing.
+ * </p>
+ *
+ * @return {@code true} if this PropertySource could be scanned for its list of properties,
+ * {@code false} if it should not be scanned.
+ */
+ default boolean isScannable(){
+ return true;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/328a4ac7/java8/api/src/main/java/org/apache/tamaya/spi/PropertySourceProvider.java
----------------------------------------------------------------------
diff --git a/java8/api/src/main/java/org/apache/tamaya/spi/PropertySourceProvider.java b/java8/api/src/main/java/org/apache/tamaya/spi/PropertySourceProvider.java
new file mode 100644
index 0000000..42e3b4d
--- /dev/null
+++ b/java8/api/src/main/java/org/apache/tamaya/spi/PropertySourceProvider.java
@@ -0,0 +1,43 @@
+/*
+ * 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.tamaya.spi;
+
+import java.util.Collection;
+
+/**
+ * <p>Implement this interfaces to provide a PropertySource provider which
+ * is able to register multiple PropertySources. This is e.g. needed if
+ * there are multiple property files of a given config file name.</p>
+ *
+ * <p>If a PropertySource like JNDI only exists once, then there is no need
+ * to implement it via the PropertySourceProvider but should directly
+ * expose a {@link PropertySource}.</p>
+ *
+ * <p>A PropertySourceProvider will get picked up via the
+ * {@link java.util.ServiceLoader} mechanism and must get registered via
+ * META-INF/services/org.apache.tamaya.spi.PropertySourceProvider</p>
+ */
+public interface PropertySourceProvider {
+
+ /**
+ * @return For each e.g. property file, we return a single PropertySource
+ * or an empty list if no PropertySource exists.
+ */
+ Collection<PropertySource> getPropertySources();
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/328a4ac7/java8/api/src/main/java/org/apache/tamaya/spi/ServiceContext.java
----------------------------------------------------------------------
diff --git a/java8/api/src/main/java/org/apache/tamaya/spi/ServiceContext.java b/java8/api/src/main/java/org/apache/tamaya/spi/ServiceContext.java
new file mode 100644
index 0000000..f7817e7
--- /dev/null
+++ b/java8/api/src/main/java/org/apache/tamaya/spi/ServiceContext.java
@@ -0,0 +1,68 @@
+/*
+ * 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.tamaya.spi;
+
+import java.util.List;
+import java.util.Optional;
+
+
+/**
+ * This class models the component that is managing the lifecycle current the
+ * services used by the Configuration API.
+ */
+public interface ServiceContext {
+
+ /**
+ * @return ordinal of the ServiceContext. The one with the highest ordinal will be taken.
+ */
+ default int ordinal() {
+ return 1;
+ }
+
+ /**
+ * Access a service singleton via its type.
+ * If multiple implementations for the very serviceType exist then
+ * the one with the highest {@link javax.annotation.Priority} will be used.
+ *
+ * @param serviceType the service type.
+ * @return The instance to be used, never {@code null}
+ * @throws org.apache.tamaya.ConfigException if there are multiple service implementations with the maximum priority.
+ */
+ <T> Optional<T> getService(Class<T> serviceType);
+
+ /**
+ * Access a list current services, given its type. The bootstrap mechanism should
+ * order the instance for precedence, hereby the most significant should be
+ * first in order.
+ *
+ * @param serviceType
+ * the service type.
+ * @return The instance to be used, never {@code null}
+ */
+ <T> List<T> getServices(Class<T> serviceType);
+
+ /**
+ * Get the current {@link ServiceContext}. If necessary the {@link ServiceContext} will be laziliy loaded.
+ *
+ * @return the {@link ServiceContext} to be used.
+ */
+ public static ServiceContext getInstance(){
+ return ServiceContextManager.getServiceContext();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/328a4ac7/java8/api/src/main/java/org/apache/tamaya/spi/ServiceContextManager.java
----------------------------------------------------------------------
diff --git a/java8/api/src/main/java/org/apache/tamaya/spi/ServiceContextManager.java b/java8/api/src/main/java/org/apache/tamaya/spi/ServiceContextManager.java
new file mode 100644
index 0000000..68e7552
--- /dev/null
+++ b/java8/api/src/main/java/org/apache/tamaya/spi/ServiceContextManager.java
@@ -0,0 +1,109 @@
+/*
+ * 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.tamaya.spi;
+
+import java.util.Objects;
+import java.util.ServiceLoader;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.tamaya.ConfigException;
+
+
+/**
+ * This singleton provides access to the services available in the current {@link ServiceContext}. The
+ * behaviour can be adapted, by calling {@link ServiceContextManager#set(ServiceContext)} before accessing any
+ * services.
+ */
+final class ServiceContextManager {
+ /**
+ * The ServiceProvider used.
+ */
+ private static volatile ServiceContext serviceContextProviderDelegate;
+
+ /**
+ * Private singletons constructor.
+ */
+ private ServiceContextManager() {
+ }
+
+ /**
+ * Load the {@link ServiceContext} to be used.
+ *
+ * @return {@link ServiceContext} to be used for loading the services.
+ */
+ private static ServiceContext loadDefaultServiceProvider() {
+ ServiceContext highestServiceContext = null;
+ try {
+ int highestOrdinal = 0;
+ for (ServiceContext serviceContext : ServiceLoader.load(ServiceContext.class)) {
+ if (serviceContext.ordinal() > highestOrdinal) {
+ highestServiceContext = serviceContext;
+ }
+ }
+ } catch (Exception e) {
+ throw new ConfigException("ServiceContext not loadable", e);
+ }
+ if(highestServiceContext==null){
+ throw new ConfigException("No ServiceContext found");
+ }
+ return highestServiceContext;
+ }
+
+ /**
+ * Replace the current {@link ServiceContext} in use.
+ *
+ * @param serviceContextProvider the new {@link ServiceContext}, not null.
+ */
+ public static ServiceContext set(ServiceContext serviceContextProvider) {
+ ServiceContext currentContext = ServiceContextManager.serviceContextProviderDelegate;
+ Objects.requireNonNull(serviceContextProvider);
+ synchronized (ServiceContextManager.class) {
+ if (ServiceContextManager.serviceContextProviderDelegate == null) {
+ ServiceContextManager.serviceContextProviderDelegate = serviceContextProvider;
+ Logger.getLogger(ServiceContextManager.class.getName())
+ .log(Level.INFO, "Using ServiceProvider: " + serviceContextProvider.getClass().getName());
+ } else {
+ Logger.getLogger(ServiceContextManager.class.getName())
+ .log(Level.WARNING, "Replacing ServiceProvider " +
+ ServiceContextManager.serviceContextProviderDelegate.getClass().getName() +
+ " with: " + serviceContextProvider.getClass().getName());
+ ServiceContextManager.serviceContextProviderDelegate = serviceContextProvider;
+ }
+ }
+ return currentContext;
+ }
+
+ /**
+ * Ge {@link ServiceContext}. If necessary the {@link ServiceContext} will be laziliy loaded.
+ *
+ * @return the {@link ServiceContext} used.
+ */
+ public static ServiceContext getServiceContext() {
+ if (serviceContextProviderDelegate == null) {
+ synchronized (ServiceContextManager.class) {
+ if (serviceContextProviderDelegate == null) {
+ serviceContextProviderDelegate = loadDefaultServiceProvider();
+ }
+ }
+ }
+ return serviceContextProviderDelegate;
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/328a4ac7/java8/api/src/test/java/org/apache/tamaya/ConfigurationTest.java
----------------------------------------------------------------------
diff --git a/java8/api/src/test/java/org/apache/tamaya/ConfigurationTest.java b/java8/api/src/test/java/org/apache/tamaya/ConfigurationTest.java
new file mode 100644
index 0000000..0e62205
--- /dev/null
+++ b/java8/api/src/test/java/org/apache/tamaya/ConfigurationTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.tamaya;
+
+import static org.junit.Assert.*;
+
+/**
+ * Test class that tests the default methods implemented on {@link org.apache.tamaya.Configuration}. The provided
+ * {@link org.apache.tamaya.TestConfiguration} is implemeted with maximal use of the default methods.
+ */
+public class ConfigurationTest {
+
+ @org.junit.Test
+ public void testget() throws Exception {
+ assertEquals(Boolean.TRUE, Configuration.current().get("booleanTrue", (s) -> Boolean.valueOf(s)).get());
+ assertEquals(Boolean.FALSE, Configuration.current().get("booleanFalse", (s) -> Boolean.valueOf(s)).get());
+ assertEquals((int)Byte.MAX_VALUE, (int)Configuration.current().get("byte", (s) -> Byte.valueOf(s)).get());
+ assertEquals((int)Integer.MAX_VALUE, (int)Configuration.current().get("int", (s) -> Integer.valueOf(s)).get());
+ assertEquals((long)Long.MAX_VALUE, (long)Configuration.current().get("long", (s) -> Long.valueOf(s)).get());
+ assertEquals((double)Float.MAX_VALUE, (double)Configuration.current().get("float", (s) -> Float.valueOf(s)).get(), 0.0d);
+ assertEquals((double)Double.MAX_VALUE, (double)Configuration.current().get("double", (s) -> Double.valueOf(s)).get(), 0.0d);
+ }
+
+ @org.junit.Test
+ public void testGetBoolean() throws Exception {
+ assertTrue(Configuration.current().getBoolean("booleanTrue"));
+ assertFalse(Configuration.current().getBoolean("booleanFalse"));
+ assertFalse(Configuration.current().getBoolean("foorBar"));
+ }
+
+ @org.junit.Test
+ public void testGetInteger() throws Exception {
+ assertEquals(Integer.MAX_VALUE,Configuration.current().getInteger("int").getAsInt());
+ }
+
+ @org.junit.Test
+ public void testGetLong() throws Exception {
+ assertEquals(Long.MAX_VALUE,Configuration.current().getLong("long").getAsLong());
+ }
+
+ @org.junit.Test
+ public void testGetDouble() throws Exception {
+ assertEquals(Double.MAX_VALUE,Configuration.current().getDouble("double").getAsDouble(), 0.0d);
+ }
+
+ @org.junit.Test
+ public void testWith() throws Exception {
+ assertEquals(Configuration.current(), Configuration.current().with(c-> c));
+ }
+
+ @org.junit.Test
+ public void testQuery() throws Exception {
+ assertEquals("myFooResult", Configuration.current().query(c -> "myFooResult"));
+ }
+
+ @org.junit.Test
+ public void testGetAdapted() throws Exception {
+ assertEquals("yes", Configuration.current().get("booleanTrue", (v) -> Boolean.parseBoolean(v)?"yes":"no").get());
+ assertEquals("no", Configuration.current().get("booleanFalse", (v) -> Boolean.parseBoolean(v)?"yes":"no").get());
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/328a4ac7/java8/api/src/test/java/org/apache/tamaya/TestConfiguration.java
----------------------------------------------------------------------
diff --git a/java8/api/src/test/java/org/apache/tamaya/TestConfiguration.java b/java8/api/src/test/java/org/apache/tamaya/TestConfiguration.java
new file mode 100644
index 0000000..cdcb0e9
--- /dev/null
+++ b/java8/api/src/test/java/org/apache/tamaya/TestConfiguration.java
@@ -0,0 +1,87 @@
+/*
+ * 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.tamaya;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Test Configuration class, that is used to testdata the default methods provided by the API.
+ */
+public class TestConfiguration implements Configuration{
+
+ private static final Map<String, String> VALUES;
+ static {
+ VALUES = new HashMap<String, String>();
+ VALUES.put("long", String.valueOf(Long.MAX_VALUE));
+ VALUES.put("int", String.valueOf(Integer.MAX_VALUE));
+ VALUES.put("double", String.valueOf(Double.MAX_VALUE));
+ VALUES.put("float", String.valueOf(Float.MAX_VALUE));
+ VALUES.put("short", String.valueOf(Short.MAX_VALUE));
+ VALUES.put("byte", String.valueOf(Byte.MAX_VALUE));
+ VALUES.put("booleanTrue", "true");
+ VALUES.put("booleanFalse", "false");
+ VALUES.put("String", "aStringValue");
+ }
+
+ @Override
+ public Optional<String> get(String key) {
+ return Optional.ofNullable(VALUES.get(key));
+ }
+
+ @Override
+ public <T> Optional<T> get(String key, Class<T> type) {
+ if(type.equals(Long.class)){
+ return Optional.class.cast(Optional.ofNullable(Long.MAX_VALUE));
+ }
+ else if(type.equals(Integer.class)){
+ return Optional.class.cast(Optional.ofNullable(Integer.MAX_VALUE));
+ }
+ else if(type.equals(Double.class)){
+ return Optional.class.cast(Optional.ofNullable(Double.MAX_VALUE));
+ }
+ else if(type.equals(Float.class)){
+ return Optional.class.cast(Optional.ofNullable(Float.MAX_VALUE));
+ }
+ else if(type.equals(Short.class)){
+ return Optional.class.cast(Optional.ofNullable(Short.MAX_VALUE));
+ }
+ else if(type.equals(Byte.class)){
+ return Optional.class.cast(Optional.ofNullable(Byte.MAX_VALUE));
+ }
+ else if(type.equals(Boolean.class)){
+ if("booleanTrue".equals(key)) {
+ return Optional.class.cast(Optional.ofNullable(Boolean.TRUE));
+ }
+ else{
+ return Optional.class.cast(Optional.ofNullable(Boolean.FALSE));
+ }
+ }
+ else if(type.equals(String.class)){
+ return Optional.class.cast(Optional.ofNullable("aStringValue"));
+ }
+ throw new ConfigException("No such property: " + key);
+ }
+
+ @Override
+ public Map<String, String> getProperties() {
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/328a4ac7/java8/api/src/test/java/org/apache/tamaya/spi/ServiceContextManagerTest.java
----------------------------------------------------------------------
diff --git a/java8/api/src/test/java/org/apache/tamaya/spi/ServiceContextManagerTest.java b/java8/api/src/test/java/org/apache/tamaya/spi/ServiceContextManagerTest.java
new file mode 100644
index 0000000..0309c6c
--- /dev/null
+++ b/java8/api/src/test/java/org/apache/tamaya/spi/ServiceContextManagerTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.tamaya.spi;
+
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Optional;
+
+public class ServiceContextManagerTest {
+
+ private static URLClassLoader classLoader;
+ private static Field delegateField;
+
+ @BeforeClass
+ public static void init() throws Exception {
+
+ // setup the environment for our ugly hacks
+
+ // replace classloader with our own
+ classLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(new UglyHackClassLoader(classLoader));
+
+ // clear the caching field
+ delegateField = ServiceContextManager.class.getDeclaredField("serviceContextProviderDelegate");
+ delegateField.setAccessible(true);
+
+ delegateField.set(null, null);
+ }
+
+ @AfterClass
+ public static void clean() throws Exception {
+
+ // clean our hacks
+
+ delegateField.set(null, null);
+ Thread.currentThread().setContextClassLoader(classLoader);
+ }
+
+ @Test
+ public void testGetServiceContext() {
+
+ ServiceContext context = ServiceContextManager.getServiceContext();
+ Assert.assertEquals(100, context.ordinal());
+
+ }
+
+
+ // has to be public because ServiceLoader won't find it otherwise
+ public static class ServiceContextWithOrdinal implements ServiceContext {
+
+ @Override
+ public int ordinal() {
+ return 100;
+ }
+
+ @Override
+ public <T> Optional<T> getService(Class<T> serviceType) {
+ return null;
+ }
+
+ @Override
+ public <T> List<T> getServices(Class<T> serviceType) {
+ return null;
+ }
+ }
+
+ // to override the getResources method to use our own 'ServiceLoader'-file we have to this ugly hack
+ private static class UglyHackClassLoader extends URLClassLoader {
+
+ private UglyHackClassLoader(URLClassLoader urlClassLoader) {
+ super(urlClassLoader.getURLs());
+ }
+
+
+ @Override
+ public Enumeration<URL> getResources(String name) throws IOException {
+ if ("META-INF/services/org.apache.tamaya.spi.ServiceContext".equals(name)) {
+ return super.getResources("ServiceContextWithOrdinal");
+ }
+
+ return super.getResources(name);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/328a4ac7/java8/api/src/test/java/org/apache/tamaya/spi/ServiceContextTest.java
----------------------------------------------------------------------
diff --git a/java8/api/src/test/java/org/apache/tamaya/spi/ServiceContextTest.java b/java8/api/src/test/java/org/apache/tamaya/spi/ServiceContextTest.java
new file mode 100644
index 0000000..5ebfc19
--- /dev/null
+++ b/java8/api/src/test/java/org/apache/tamaya/spi/ServiceContextTest.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.tamaya.spi;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+
+public class ServiceContextTest {
+
+ private ServiceContext serviceContext = new ServiceContext(){
+
+ @Override
+ public <T> Optional<T> getService(Class<T> serviceType) {
+ if(String.class.equals(serviceType)){
+ return Optional.of(serviceType.cast("ServiceContextTest"));
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public <T> List<T> getServices(Class<T> serviceType) {
+ if(String.class.equals(serviceType)){
+ List<String> list = new ArrayList<>();
+ list.add("ServiceContextTest");
+ return List.class.cast(list);
+ }
+ return Collections.EMPTY_LIST;
+ }
+ };
+
+ @Test
+ public void testOrdinal() throws Exception {
+ assertEquals(1, serviceContext.ordinal());
+ }
+
+ @Test
+ public void testgetService() throws Exception {
+ assertEquals("ServiceContextTest", serviceContext.getService(String.class).get());
+ assertFalse(serviceContext.getService(Integer.class).isPresent());
+ }
+
+ @Test
+ public void testGetService() throws Exception {
+ Optional<String> service = serviceContext.getService(String.class);
+ assertNotNull(service);
+ assertTrue(service.isPresent());
+ assertEquals("ServiceContextTest", service.get());
+ Optional<Integer> intService = serviceContext.getService(Integer.class);
+ assertNotNull(intService);
+ assertFalse(intService.isPresent());
+ }
+
+ @Test
+ public void testGetServices() throws Exception {
+ Collection<String> services = serviceContext.getServices(String.class);
+ assertNotNull(services);
+ assertFalse(services.isEmpty());
+ assertEquals("ServiceContextTest", services.iterator().next());
+ Collection<Integer> intServices = serviceContext.getServices(Integer.class);
+ assertNotNull(intServices);
+ assertTrue(intServices.isEmpty());
+ }
+
+ @Test
+ public void testGetInstance() throws Exception {
+ assertNotNull(ServiceContext.getInstance());
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/328a4ac7/java8/api/src/test/java/org/apache/tamaya/spi/TestServiceContext.java
----------------------------------------------------------------------
diff --git a/java8/api/src/test/java/org/apache/tamaya/spi/TestServiceContext.java b/java8/api/src/test/java/org/apache/tamaya/spi/TestServiceContext.java
new file mode 100644
index 0000000..665c99f
--- /dev/null
+++ b/java8/api/src/test/java/org/apache/tamaya/spi/TestServiceContext.java
@@ -0,0 +1,82 @@
+/*
+ * 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.tamaya.spi;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.ServiceLoader;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class implements the (default) {@link org.apache.tamaya.spi.ServiceContext} interface and hereby uses the JDK
+ * {@link java.util.ServiceLoader} to load the services required.
+ */
+public final class TestServiceContext implements ServiceContext {
+ /** List current services loaded, per class. */
+ private final ConcurrentHashMap<Class, List<Object>> servicesLoaded = new ConcurrentHashMap<>();
+ /** Singletons. */
+ private final Map<Class, Optional<?>> singletons = new ConcurrentHashMap<>();
+
+ @Override
+ public <T> Optional<T> getService(Class<T> serviceType) {
+ Optional<T> cached = Optional.class.cast(singletons.get(serviceType));
+ if(cached==null) {
+ List<? extends T> services = getServices(serviceType);
+ if (services.isEmpty()) {
+ cached = Optional.empty();
+ }
+ else{
+ cached = Optional.of(services.get(0));
+ }
+ singletons.put(serviceType, cached);
+ }
+ return cached;
+ }
+
+ /**
+ * Loads and registers services.
+ *
+ * @param serviceType The service type.
+ * @param <T> the concrete type.
+ *
+ * @return the items found, never {@code null}.
+ */
+ @Override
+ public <T> List<T> getServices(Class<T> serviceType) {
+ try {
+ List<T> services = new ArrayList<>();
+ for (T t : ServiceLoader.load(serviceType)) {
+ services.add(t);
+ }
+ services = Collections.unmodifiableList(services);
+ final List<T> previousServices = List.class.cast(servicesLoaded.putIfAbsent(serviceType, (List<Object>)services));
+ return previousServices != null ? previousServices : services;
+ } catch (Exception e) {
+ Logger.getLogger(TestServiceContext.class.getName()).log(Level.WARNING,
+ "Error loading services current type " + serviceType, e);
+ return Collections.EMPTY_LIST;
+ }
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/328a4ac7/java8/api/src/test/resources/META-INF/services/org.apache.tamaya.Configuration
----------------------------------------------------------------------
diff --git a/java8/api/src/test/resources/META-INF/services/org.apache.tamaya.Configuration b/java8/api/src/test/resources/META-INF/services/org.apache.tamaya.Configuration
new file mode 100644
index 0000000..1f42438
--- /dev/null
+++ b/java8/api/src/test/resources/META-INF/services/org.apache.tamaya.Configuration
@@ -0,0 +1,19 @@
+#
+# 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 current 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.tamaya.TestConfiguration
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/328a4ac7/java8/api/src/test/resources/META-INF/services/org.apache.tamaya.spi.ServiceContext
----------------------------------------------------------------------
diff --git a/java8/api/src/test/resources/META-INF/services/org.apache.tamaya.spi.ServiceContext b/java8/api/src/test/resources/META-INF/services/org.apache.tamaya.spi.ServiceContext
new file mode 100644
index 0000000..0f7abe9
--- /dev/null
+++ b/java8/api/src/test/resources/META-INF/services/org.apache.tamaya.spi.ServiceContext
@@ -0,0 +1 @@
+org.apache.tamaya.spi.TestServiceContext
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/328a4ac7/java8/api/src/test/resources/ServiceContextWithOrdinal
----------------------------------------------------------------------
diff --git a/java8/api/src/test/resources/ServiceContextWithOrdinal b/java8/api/src/test/resources/ServiceContextWithOrdinal
new file mode 100644
index 0000000..4112c18
--- /dev/null
+++ b/java8/api/src/test/resources/ServiceContextWithOrdinal
@@ -0,0 +1,25 @@
+# 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.
+
+# this is just an ugly hack to test the loading of ServiceContexts
+# if we have more than one
+
+# this one is without overriding ordinal so it shall take the default
+org.apache.tamaya.spi.TestServiceContext
+
+# this one has a higher ordinal (100)
+org.apache.tamaya.spi.ServiceContextManagerTest$ServiceContextWithOrdinal
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/328a4ac7/java8/core/pom.xml
----------------------------------------------------------------------
diff --git a/java8/core/pom.xml b/java8/core/pom.xml
new file mode 100644
index 0000000..7949c3c
--- /dev/null
+++ b/java8/core/pom.xml
@@ -0,0 +1,46 @@
+<!--
+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 current 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>
+
+ <parent>
+ <groupId>org.apache.tamaya</groupId>
+ <artifactId>tamaya-java8</artifactId>
+ <version>0.2-incubating-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>tamaya-core</artifactId>
+
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.tamaya</groupId>
+ <artifactId>tamaya-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/328a4ac7/java8/core/src/main/java/org/apache/tamaya/core/internal/DefaultConfiguration.java
----------------------------------------------------------------------
diff --git a/java8/core/src/main/java/org/apache/tamaya/core/internal/DefaultConfiguration.java b/java8/core/src/main/java/org/apache/tamaya/core/internal/DefaultConfiguration.java
new file mode 100644
index 0000000..ec23e25
--- /dev/null
+++ b/java8/core/src/main/java/org/apache/tamaya/core/internal/DefaultConfiguration.java
@@ -0,0 +1,226 @@
+/*
+ * 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.tamaya.core.internal;
+
+import org.apache.tamaya.ConfigException;
+import org.apache.tamaya.Configuration;
+import org.apache.tamaya.spi.ConfigurationContext;
+import org.apache.tamaya.spi.PropertyConverter;
+import org.apache.tamaya.spi.PropertyFilter;
+import org.apache.tamaya.spi.PropertySource;
+import org.apache.tamaya.spi.ServiceContext;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+/**
+ * Implementation of the Configuration API. This class uses the current {@link ConfigurationContext} to evaluate the
+ * chain of {@link org.apache.tamaya.spi.PropertySource} and {@link org.apache.tamaya.spi.PropertyFilter}
+ * instance to evaluate the current Configuration.
+ */
+public class DefaultConfiguration implements Configuration {
+ /**
+ * The logger.
+ */
+ private static final Logger LOG = Logger.getLogger(DefaultConfiguration.class.getName());
+ /**
+ * The maximal number of filter cycles performed before aborting.
+ */
+ private static final int MAX_FILTER_LOOPS = 10;
+
+ /**
+ * The current {@link org.apache.tamaya.spi.ConfigurationContext} of the current instance.
+ */
+ private final ConfigurationContext configurationContext = ServiceContext.getInstance().getService(ConfigurationContext.class).get();
+
+ /**
+ * This method evaluates the given configuration key. Hereby if goes down the chain or PropertySource instances
+ * provided by the current {@link org.apache.tamaya.spi.ConfigurationContext}. The first non-null-value returned
+ * is taken as an intermediate value. Finally the value is filtered through the
+ * {@link org.apache.tamaya.spi.PropertyFilter} instances installed, before it is returned as the final result of
+ * this method.
+ *
+ * @param key the property's key, not null.
+ * @return the optional configuration value, never null.
+ */
+ @Override
+ public Optional<String> get(String key) {
+ List<PropertySource> propertySources = configurationContext.getPropertySources();
+ String unfilteredValue = null;
+ for (PropertySource propertySource : propertySources) {
+ Optional<String> value = propertySource.get(key);
+ if (value.isPresent()) {
+ unfilteredValue = value.get();
+ break;
+ }
+ }
+ return Optional.ofNullable(applyFilter(key, unfilteredValue));
+ }
+
+ /**
+ * Apply filters to a single property value.
+ *
+ * @param key the key, used for logging, not null.
+ * @param unfilteredValue the unfiltered property value.
+ * @return the filtered value, or null.
+ */
+ private String applyFilter(String key, String unfilteredValue) {
+ // Apply filters to values, prevent values filtered to null!
+ for (int i = 0; i < MAX_FILTER_LOOPS; i++) {
+ boolean changed = false;
+ // Apply filters to values, prevent values filtered to null!
+ for (PropertyFilter filter : configurationContext.getPropertyFilters()) {
+ String newValue = filter.filterProperty(key, unfilteredValue);
+ if (newValue != null && !newValue.equals(unfilteredValue)) {
+ changed = true;
+ if (LOG.isLoggable(Level.FINEST)) {
+ LOG.finest("Filter - " + key + ": " + unfilteredValue + " -> " + newValue + " by " + filter);
+ }
+ } else if (unfilteredValue != null && !unfilteredValue.equals(newValue)) {
+ changed = true;
+ if (LOG.isLoggable(Level.FINEST)) {
+ LOG.finest("Filter - " + key + ": " + unfilteredValue + " -> " + newValue + " by " + filter);
+ }
+ }
+ unfilteredValue = newValue;
+ }
+ if (!changed) {
+ LOG.finest(() -> "Finishing filter loop, no changes detected.");
+ break;
+ } else {
+ if (i == (MAX_FILTER_LOOPS - 1)) {
+ if (LOG.isLoggable(Level.WARNING)) {
+ LOG.warning("Maximal filter loop count reached, aborting filter evaluation after cycles: " + i);
+ }
+ } else {
+ LOG.finest(() -> "Repeating filter loop, changes detected.");
+ }
+ }
+ }
+ return unfilteredValue;
+ }
+
+ /**
+ * Get the current properties, composed by the loaded {@link org.apache.tamaya.spi.PropertySource} and filtered
+ * by registered {@link org.apache.tamaya.spi.PropertyFilter}.
+ *
+ * @return the final properties.
+ */
+ @Override
+ public Map<String, String> getProperties() {
+ List<PropertySource> propertySources = new ArrayList<>(configurationContext.getPropertySources());
+ Collections.reverse(propertySources);
+ Map<String, String> result = new HashMap<>();
+ for (PropertySource propertySource : propertySources) {
+ try {
+ int origSize = result.size();
+ Map<String, String> otherMap = propertySource.getProperties();
+ LOG.log(Level.FINEST, null, () -> "Overriding with properties from " + propertySource.getName());
+ result.putAll(otherMap);
+ LOG.log(Level.FINEST, null, () -> "Handled properties from " + propertySource.getName() + "(new: " +
+ (result.size() - origSize) + ", overrides: " + origSize + ", total: " + result.size());
+ } catch (Exception e) {
+ LOG.log(Level.SEVERE, "Error adding properties from PropertySource: " + propertySource + ", ignoring PropertySource.", e);
+ }
+ }
+ return applyFilters(result);
+ }
+
+ /**
+ * Filter a full configuration property map.
+ *
+ * @param inputMap the unfiltered map
+ * @return the filtered map.
+ */
+ private Map<String, String> applyFilters(Map<String, String> inputMap) {
+ // Apply filters to values, prevent values filtered to null!
+ for (int i = 0; i < MAX_FILTER_LOOPS; i++) {
+ AtomicInteger changes = new AtomicInteger();
+ for (PropertyFilter filter : configurationContext.getPropertyFilters()) {
+ inputMap.replaceAll((k, v) -> {
+ String newValue = filter.filterProperty(k, v);
+ if (newValue != null && !newValue.equals(v)) {
+ changes.incrementAndGet();
+ LOG.finest(() -> "Filter - " + k + ": " + v + " -> " + newValue + " by " + filter);
+ } else if (v != null && !v.equals(newValue)) {
+ changes.incrementAndGet();
+ LOG.finest(() -> "Filter - " + k + ": " + v + " -> " + newValue + " by " + filter);
+ }
+ return newValue;
+ });
+ }
+ if (changes.get() == 0) {
+ LOG.finest(() -> "Finishing filter loop, no changes detected.");
+ break;
+ } else {
+ if (i == (MAX_FILTER_LOOPS - 1)) {
+ if (LOG.isLoggable(Level.WARNING)) {
+ LOG.warning("Maximal filter loop count reached, aborting filter evaluation after cycles: " + i);
+ }
+ } else {
+ LOG.finest(() -> "Repeating filter loop, changes detected: " + changes.get());
+ }
+ changes.set(0);
+ }
+ }
+ // Remove null values
+ return inputMap.entrySet().parallelStream().filter((e) -> e.getValue() != null).collect(
+ Collectors.toMap((e) -> e.getKey(), (e) -> e.getValue()));
+ }
+
+ /**
+ * Accesses the current String value for the given key (see {@link #get(String)}) and tries to convert it
+ * using the {@link org.apache.tamaya.spi.PropertyConverter} instances provided by the current
+ * {@link org.apache.tamaya.spi.ConfigurationContext}.
+ *
+ * @param key the property's absolute, or relative path, e.g. @code
+ * a/b/c/d.myProperty}.
+ * @param type The target type required, not null.
+ * @param <T> the value type
+ * @return the converted value, never null.
+ */
+ @Override
+ public <T> Optional<T> get(String key, Class<T> type) {
+ Optional<String> value = get(key);
+ if (value.isPresent()) {
+ List<PropertyConverter<T>> converters = configurationContext.getPropertyConverters(type);
+ for (PropertyConverter<T> converter : converters) {
+ try {
+ T t = converter.convert(value.get());
+ if (t != null) {
+ return Optional.of(t);
+ }
+ } catch (Exception e) {
+ LOG.log(Level.FINEST, e, () -> "PropertyConverter: " + converter +
+ " failed to convert value: " + value.get());
+ }
+ }
+ throw new ConfigException("Unparseable config value for type: " + type.getName() + ": " + key);
+ }
+ return Optional.empty();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/328a4ac7/java8/core/src/main/java/org/apache/tamaya/core/internal/DefaultConfigurationContext.java
----------------------------------------------------------------------
diff --git a/java8/core/src/main/java/org/apache/tamaya/core/internal/DefaultConfigurationContext.java b/java8/core/src/main/java/org/apache/tamaya/core/internal/DefaultConfigurationContext.java
new file mode 100644
index 0000000..b6acae5
--- /dev/null
+++ b/java8/core/src/main/java/org/apache/tamaya/core/internal/DefaultConfigurationContext.java
@@ -0,0 +1,183 @@
+/*
+ * 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.tamaya.core.internal;
+
+import org.apache.tamaya.spi.ConfigurationContext;
+import org.apache.tamaya.spi.PropertyConverter;
+import org.apache.tamaya.spi.PropertyFilter;
+import org.apache.tamaya.spi.PropertySource;
+import org.apache.tamaya.spi.PropertySourceProvider;
+import org.apache.tamaya.spi.ServiceContext;
+
+import javax.annotation.Priority;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.StampedLock;
+
+/**
+ * Default Implementation of a simple ConfigurationContext.
+ */
+public class DefaultConfigurationContext implements ConfigurationContext {
+ /**
+ * Cubcomponent handling {@link org.apache.tamaya.spi.PropertyConverter} instances.
+ */
+ private PropertyConverterManager propertyConverterManager = new PropertyConverterManager();
+
+ /**
+ * The current unmodifiable list of loaded {@link org.apache.tamaya.spi.PropertySource} instances.
+ */
+ private List<PropertySource> immutablePropertySources;
+
+ /**
+ * The current unmodifiable list of loaded {@link org.apache.tamaya.spi.PropertyFilter} instances.
+ */
+ private List<PropertyFilter> immutablePropertyFilters;
+
+ /**
+ * Lock for internal synchronization.
+ */
+ private StampedLock propertySourceLock = new StampedLock();
+
+
+ /**
+ * The first time the Configuration system gets invoked we do initialize
+ * all our {@link org.apache.tamaya.spi.PropertySource}s and
+ * {@link org.apache.tamaya.spi.PropertyFilter}s which are known at startup.
+ */
+ public DefaultConfigurationContext() {
+ List<PropertySource> propertySources = new ArrayList<>();
+
+ // first we load all PropertySources which got registered via java.util.ServiceLoader
+ propertySources.addAll(ServiceContext.getInstance().getServices(PropertySource.class));
+
+ // after that we add all PropertySources which get dynamically registered via their PropertySourceProviders
+ propertySources.addAll(evaluatePropertySourcesFromProviders());
+
+ // now sort them according to their ordinal values
+ Collections.sort(propertySources, this::comparePropertySources);
+
+ immutablePropertySources = Collections.unmodifiableList(propertySources);
+
+ // as next step we pick up the PropertyFilters pretty much the same way
+ List<PropertyFilter> propertyFilters = new ArrayList<>();
+ propertyFilters.addAll(ServiceContext.getInstance().getServices(PropertyFilter.class));
+ Collections.sort(propertyFilters, this::comparePropertyFilters);
+
+ immutablePropertyFilters = Collections.unmodifiableList(propertyFilters);
+ }
+
+ /**
+ * Pick up all {@link org.apache.tamaya.spi.PropertySourceProvider}s and return all the
+ * {@link org.apache.tamaya.spi.PropertySource}s they like to register.
+ */
+ private Collection<? extends PropertySource> evaluatePropertySourcesFromProviders() {
+ List<PropertySource> propertySources = new ArrayList<>();
+ List<PropertySourceProvider> propertySourceProviders = ServiceContext.getInstance().getServices(PropertySourceProvider.class);
+ for (PropertySourceProvider propertySourceProvider : propertySourceProviders) {
+ propertySources.addAll(propertySourceProvider.getPropertySources());
+ }
+
+ return propertySources;
+ }
+
+ @Override
+ public void addPropertySources(PropertySource... propertySourcesToAdd) {
+ Lock writeLock = propertySourceLock.asWriteLock();
+ try {
+ writeLock.lock();
+ List<PropertySource> newPropertySources = new ArrayList<>(this.immutablePropertySources);
+ newPropertySources.addAll(Arrays.asList(propertySourcesToAdd));
+ Collections.sort(newPropertySources, this::comparePropertySources);
+
+ this.immutablePropertySources = Collections.unmodifiableList(newPropertySources);
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+ /**
+ * Order property source reversely, the most important come first.
+ *
+ * @param source1 the first PropertySource
+ * @param source2 the second PropertySource
+ * @return the comparison result.
+ */
+ private int comparePropertySources(PropertySource source1, PropertySource source2) {
+ if (source1.getOrdinal() < source2.getOrdinal()) {
+ return 1;
+ } else if (source1.getOrdinal() > source2.getOrdinal()) {
+ return -1;
+ } else {
+ return source2.getClass().getName().compareTo(source1.getClass().getName());
+ }
+ }
+
+ /**
+ * Compare 2 filters for ordering the filter chain.
+ *
+ * @param filter1 the first filter
+ * @param filter2 the second filter
+ * @return the comparison result
+ */
+ private int comparePropertyFilters(PropertyFilter filter1, PropertyFilter filter2) {
+ Priority prio1 = filter1.getClass().getAnnotation(Priority.class);
+ Priority prio2 = filter2.getClass().getAnnotation(Priority.class);
+ int ord1 = prio1 != null ? prio1.value() : 0;
+ int ord2 = prio2 != null ? prio2.value() : 0;
+
+ if (ord1 < ord2) {
+ return -1;
+ } else if (ord1 > ord2) {
+ return 1;
+ } else {
+ return filter1.getClass().getName().compareTo(filter2.getClass().getName());
+ }
+ }
+
+ @Override
+ public List<PropertySource> getPropertySources() {
+ return immutablePropertySources;
+ }
+
+ @Override
+ public <T> void addPropertyConverter(Class<T> typeToConvert, PropertyConverter<T> propertyConverter) {
+ propertyConverterManager.register(typeToConvert, propertyConverter);
+ }
+
+ @Override
+ public Map<Class<?>, List<PropertyConverter<?>>> getPropertyConverters() {
+ return propertyConverterManager.getPropertyConverters();
+ }
+
+ @Override
+ public <T> List<PropertyConverter<T>> getPropertyConverters(Class<T> targetType) {
+ return propertyConverterManager.getPropertyConverters(targetType);
+ }
+
+ @Override
+ public List<PropertyFilter> getPropertyFilters() {
+ return immutablePropertyFilters;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/328a4ac7/java8/core/src/main/java/org/apache/tamaya/core/internal/DefaultServiceContext.java
----------------------------------------------------------------------
diff --git a/java8/core/src/main/java/org/apache/tamaya/core/internal/DefaultServiceContext.java b/java8/core/src/main/java/org/apache/tamaya/core/internal/DefaultServiceContext.java
new file mode 100644
index 0000000..8e27d4a
--- /dev/null
+++ b/java8/core/src/main/java/org/apache/tamaya/core/internal/DefaultServiceContext.java
@@ -0,0 +1,89 @@
+/*
+ * 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.tamaya.core.internal;
+
+import org.apache.tamaya.spi.ServiceContext;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.ServiceLoader;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class implements the (default) {@link org.apache.tamaya.spi.ServiceContext} interface and hereby uses the JDK
+ * {@link java.util.ServiceLoader} to load the services required.
+ */
+public final class DefaultServiceContext implements ServiceContext {
+ /**
+ * List current services loaded, per class.
+ */
+ private final ConcurrentHashMap<Class, List<Object>> servicesLoaded = new ConcurrentHashMap<>();
+ /**
+ * Singletons.
+ */
+ private final Map<Class, Optional<?>> singletons = new ConcurrentHashMap<>();
+
+ @Override
+ public <T> Optional<T> getService(Class<T> serviceType) {
+ Optional<T> cached = Optional.class.cast(singletons.get(serviceType));
+ if (cached == null) {
+ List<? extends T> services = getServices(serviceType);
+ if (services.isEmpty()) {
+ cached = Optional.empty();
+ } else {
+ cached = Optional.of(services.get(0));
+ }
+ singletons.put(serviceType, cached);
+ }
+ return cached;
+ }
+
+ /**
+ * Loads and registers services.
+ *
+ * @param serviceType The service type.
+ * @param <T> the concrete type.
+ * @return the items found, never {@code null}.
+ */
+ @Override
+ public <T> List<T> getServices(final Class<T> serviceType) {
+ List<T> found = List.class.cast(servicesLoaded.get(serviceType));
+ if (found != null) {
+ return found;
+ }
+ List<T> services = new ArrayList<>();
+ try {
+ for (T t : ServiceLoader.load(serviceType)) {
+ services.add(t);
+ }
+ services = Collections.unmodifiableList(services);
+ } catch (Exception e) {
+ Logger.getLogger(DefaultServiceContext.class.getName()).log(Level.WARNING,
+ "Error loading services current type " + serviceType, e);
+ }
+ final List<T> previousServices = List.class.cast(servicesLoaded.putIfAbsent(serviceType, (List<Object>) services));
+ return previousServices != null ? previousServices : services;
+ }
+
+}
\ No newline at end of file