You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tamaya.apache.org by an...@apache.org on 2017/11/14 09:25:45 UTC
[04/12] incubator-tamaya git commit: TAMAYA-318 Moved spi-support as
API base implementation package to remove code duplicates.
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/7917a9f3/code/spi-support/src/main/java/org/apache/tamaya/spisupport/PropertyConverterManager.java
----------------------------------------------------------------------
diff --git a/code/spi-support/src/main/java/org/apache/tamaya/spisupport/PropertyConverterManager.java b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/PropertyConverterManager.java
new file mode 100644
index 0000000..921cac6
--- /dev/null
+++ b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/PropertyConverterManager.java
@@ -0,0 +1,471 @@
+/*
+ * 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.spisupport;
+
+import org.apache.tamaya.ConfigException;
+import org.apache.tamaya.TypeLiteral;
+import org.apache.tamaya.spi.ConversionContext;
+import org.apache.tamaya.spi.PropertyConverter;
+import org.apache.tamaya.spi.ServiceContextManager;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Manager that deals with {@link PropertyConverter} instances.
+ * This class is thread-safe.
+ */
+public class PropertyConverterManager {
+ /**
+ * The logger used.
+ */
+ private static final Logger LOG = Logger.getLogger(PropertyConverterManager.class.getName());
+ /**
+ * The registered converters.
+ */
+ private final Map<TypeLiteral<?>, List<PropertyConverter<?>>> converters = new ConcurrentHashMap<>();
+ /**
+ * The transitive converters.
+ */
+ private final Map<TypeLiteral<?>, List<PropertyConverter<?>>> transitiveConverters = new ConcurrentHashMap<>();
+ /**
+ * The lock used.
+ */
+ private final ReadWriteLock lock = new ReentrantReadWriteLock();
+
+ private static final Comparator<Object> PRIORITY_COMPARATOR = new Comparator<Object>() {
+
+ @Override
+ public int compare(Object o1, Object o2) {
+ int prio = PriorityServiceComparator.getPriority(o1) - PriorityServiceComparator.getPriority(o2);
+ if (prio < 0) {
+ return 1;
+ } else if (prio > 0) {
+ return -1;
+ } else {
+ return o1.getClass().getSimpleName().compareTo(o2.getClass().getSimpleName());
+ }
+ }
+ };
+
+ /**
+ * Constructor.
+ */
+ public PropertyConverterManager() {
+ this(false);
+ }
+
+ public PropertyConverterManager(boolean init) {
+ if (init) {
+ initConverters();
+ }
+ }
+
+ /**
+ * Registers the default converters provided out of the box.
+ */
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ protected void initConverters() {
+ for (PropertyConverter conv : ServiceContextManager.getServiceContext().getServices(PropertyConverter.class)) {
+ Type type = TypeLiteral.getGenericInterfaceTypeParameters(conv.getClass(), PropertyConverter.class)[0];
+ register(TypeLiteral.of(type), conv);
+ }
+ }
+
+ /**
+ * Registers a new converters instance.
+ *
+ * @param targetType the target type, not {@code null}.
+ * @param converter the converters, not {@code null}.
+ * @param <T> the type.
+ */
+ @SuppressWarnings("unchecked")
+ public <T> void register(TypeLiteral<T> targetType, PropertyConverter<T> converter) {
+ Objects.requireNonNull(converter);
+ Lock writeLock = lock.writeLock();
+ try {
+ writeLock.lock();
+ List<PropertyConverter<?>> converters = List.class.cast(this.converters.get(targetType));
+ if(converters!=null && converters.contains(converter)){
+ return;
+ }
+ List<PropertyConverter<?>> newConverters = new ArrayList<>();
+ if (converters != null) {
+ newConverters.addAll(converters);
+ }
+ if(!newConverters.contains(converter)) {
+ newConverters.add(converter);
+ }
+ Collections.sort(newConverters, PRIORITY_COMPARATOR);
+ this.converters.put(targetType, Collections.unmodifiableList(newConverters));
+ // evaluate transitive closure for all inherited supertypes and implemented interfaces
+ // direct implemented interfaces
+ for (Class<?> ifaceType : targetType.getRawType().getInterfaces()) {
+ converters = List.class.cast(this.transitiveConverters.get(TypeLiteral.of(ifaceType)));
+ newConverters = new ArrayList<>();
+ if (converters != null) {
+ newConverters.addAll(converters);
+ }
+ newConverters.add(converter);
+ Collections.sort(newConverters, PRIORITY_COMPARATOR);
+ this.transitiveConverters.put(TypeLiteral.of(ifaceType), Collections.unmodifiableList(newConverters));
+ }
+ Class<?> superClass = targetType.getRawType().getSuperclass();
+ while (superClass != null && !superClass.equals(Object.class)) {
+ converters = List.class.cast(this.transitiveConverters.get(TypeLiteral.of(superClass)));
+ newConverters = new ArrayList<>();
+ if (converters != null) {
+ newConverters.addAll(converters);
+ }
+ newConverters.add(converter);
+ Collections.sort(newConverters, PRIORITY_COMPARATOR);
+ this.transitiveConverters.put(TypeLiteral.of(superClass), Collections.unmodifiableList(newConverters));
+ for (Class<?> ifaceType : superClass.getInterfaces()) {
+ converters = List.class.cast(this.transitiveConverters.get(TypeLiteral.of(ifaceType)));
+ newConverters = new ArrayList<>();
+ if (converters != null) {
+ newConverters.addAll(converters);
+ }
+ newConverters.add(converter);
+ Collections.sort(newConverters, PRIORITY_COMPARATOR);
+ this.transitiveConverters.put(TypeLiteral.of(ifaceType), Collections.unmodifiableList(newConverters));
+ }
+ superClass = superClass.getSuperclass();
+ }
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+ /**
+ * Allows to evaluate if a given target type is supported.
+ *
+ * @param targetType the target type, not {@code null}.
+ * @return true, if a converters for the given type is registered, or a default one can be created.
+ */
+ public boolean isTargetTypeSupported(TypeLiteral<?> targetType) {
+ return converters.containsKey(targetType) || transitiveConverters.containsKey(targetType) || createDefaultPropertyConverter(targetType) != null;
+ }
+
+ /**
+ * Get a map of all property converters currently registered. This will not contain the converters that
+ * may be created, when an instance is adapted, which provides a String constructor or compatible
+ * factory methods taking a single String instance.
+ *
+ * @return the current map of instantiated and registered converters.
+ * @see #createDefaultPropertyConverter(org.apache.tamaya.TypeLiteral)
+ */
+ public Map<TypeLiteral<?>, List<PropertyConverter<?>>> getPropertyConverters() {
+ Lock readLock = lock.readLock();
+ try {
+ readLock.lock();
+ return new HashMap<>(this.converters);
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * Get the list of all current registered converters for the given target type.
+ * If not converters are registered, they component tries to create and register a dynamic
+ * converters based on String constructor or static factory methods available.
+ * The converters provided are of the following type and returned in the following order:
+ * <ul>
+ * <li>Converters mapped explicitly to the required target type are returned first, ordered
+ * by decreasing priority. This means, if explicit converters are registered these are used
+ * primarily for converting a value.</li>
+ * <li>The target type of each explicitly registered converters also can be transitively mapped to
+ * 1) all directly implemented interfaces, 2) all its superclasses (except Object), 3) all the interfaces
+ * implemented by its superclasses. These groups of transitive converters is returned similarly in the
+ * order as mentioned, whereas also here a priority based decreasing ordering is applied.</li>
+ * <li>java.lang wrapper classes and native types are automatically mapped.</li>
+ * <li>If no explicit converters are registered, for Enum types a default implementation is provided that
+ * compares the configuration values with the different enum members defined (cases sensitive mapping).</li>
+ * </ul>
+ * <p>
+ * So given that list above directly registered mappings always are tried first, before any transitive mapping
+ * should be used. Also in all cases @Priority annotations are honored for ordering of the converters in place.
+ * Transitive conversion is supported for all directly implemented interfaces (including inherited ones) and
+ * the inheritance hierarchy (exception Object). Superinterfaces of implemented interfaces are ignored.
+ *
+ * @param targetType the target type, not {@code null}.
+ * @param <T> the type class
+ * @return the ordered list of converters (may be empty for not convertible types).
+ * @see #createDefaultPropertyConverter(org.apache.tamaya.TypeLiteral)
+ */
+ public <T> List<PropertyConverter<T>> getPropertyConverters(TypeLiteral<T> targetType) {
+ Lock readLock = lock.readLock();
+ List<PropertyConverter<T>> converterList = new ArrayList<>();
+ // direct mapped converters
+ try {
+ readLock.lock();
+ addConvertersToList(List.class.cast(this.converters.get(targetType)), converterList);
+ addConvertersToList(List.class.cast(this.transitiveConverters.get(targetType)), converterList);
+ } finally {
+ readLock.unlock();
+ }
+ // handling of java.lang wrapper classes
+ TypeLiteral<T> boxedType = mapBoxedType(targetType);
+ if (boxedType != null) {
+ try {
+ readLock.lock();
+ addConvertersToList(List.class.cast(this.converters.get(boxedType)), converterList);
+ } finally {
+ readLock.unlock();
+ }
+ }
+ if (converterList.isEmpty() && !TypeLiteral.of(String.class).equals(targetType)) {
+ // adding any converters created on the fly, e.g. for enum types.
+ PropertyConverter<T> defaultConverter = createDefaultPropertyConverter(targetType);
+ if (defaultConverter != null) {
+ register(targetType, defaultConverter);
+ try {
+ readLock.lock();
+ addConvertersToList(List.class.cast(this.converters.get(targetType)), converterList);
+ } finally {
+ readLock.unlock();
+ }
+ }
+ }
+ // check for parametrized types, ignoring param type
+ // direct mapped converters
+ if(targetType.getType()!=null) {
+ try {
+ readLock.lock();
+ addConvertersToList(List.class.cast(this.converters.get(
+ TypeLiteral.of(targetType.getRawType()))), converterList);
+ } finally {
+ readLock.unlock();
+ }
+ }
+ return converterList;
+ }
+
+ private <T> void addConvertersToList(Collection<PropertyConverter<T>> converters, List<PropertyConverter<T>> converterList) {
+ if (converters != null) {
+ for(PropertyConverter<T> conv:converters) {
+ if(!converterList.contains(conv)) {
+ converterList.add(conv);
+ }
+ }
+ }
+ }
+
+ /**
+ * Maps native types to the corresponding boxed types.
+ *
+ * @param targetType the native type.
+ * @param <T> the type
+ * @return the boxed type, or null.
+ */
+ @SuppressWarnings("unchecked")
+ private <T> TypeLiteral<T> mapBoxedType(TypeLiteral<T> targetType) {
+ Type parameterType = targetType.getType();
+ if (parameterType == int.class) {
+ return TypeLiteral.class.cast(TypeLiteral.of(Integer.class));
+ }
+ if (parameterType == short.class) {
+ return TypeLiteral.class.cast(TypeLiteral.of(Short.class));
+ }
+ if (parameterType == byte.class) {
+ return TypeLiteral.class.cast(TypeLiteral.of(Byte.class));
+ }
+ if (parameterType == long.class) {
+ return TypeLiteral.class.cast(TypeLiteral.of(Long.class));
+ }
+ if (parameterType == boolean.class) {
+ return TypeLiteral.class.cast(TypeLiteral.of(Boolean.class));
+ }
+ if (parameterType == char.class) {
+ return TypeLiteral.class.cast(TypeLiteral.of(Character.class));
+ }
+ if (parameterType == float.class) {
+ return TypeLiteral.class.cast(TypeLiteral.of(Float.class));
+ }
+ if (parameterType == double.class) {
+ return TypeLiteral.class.cast(TypeLiteral.of(Double.class));
+ }
+ if (parameterType == int[].class) {
+ return TypeLiteral.class.cast(TypeLiteral.of(Integer[].class));
+ }
+ if (parameterType == short[].class) {
+ return TypeLiteral.class.cast(TypeLiteral.of(Short[].class));
+ }
+ if (parameterType == byte[].class) {
+ return TypeLiteral.class.cast(TypeLiteral.of(Byte[].class));
+ }
+ if (parameterType == long[].class) {
+ return TypeLiteral.class.cast(TypeLiteral.of(Long[].class));
+ }
+ if (parameterType == boolean.class) {
+ return TypeLiteral.class.cast(TypeLiteral.of(Boolean.class));
+ }
+ if (parameterType == char[].class) {
+ return TypeLiteral.class.cast(TypeLiteral.of(Character[].class));
+ }
+ if (parameterType == float[].class) {
+ return TypeLiteral.class.cast(TypeLiteral.of(Float[].class));
+ }
+ if (parameterType == double[].class) {
+ return TypeLiteral.class.cast(TypeLiteral.of(Double[].class));
+ }
+ return null;
+ }
+
+ /**
+ * Creates a dynamic PropertyConverter for the given target type.
+ *
+ * @param targetType the target type
+ * @param <T> the type class
+ * @return a new converters, or null.
+ */
+ protected <T> PropertyConverter<T> createDefaultPropertyConverter(final TypeLiteral<T> targetType) {
+ if (Enum.class.isAssignableFrom(targetType.getRawType())) {
+ return new EnumConverter<>(targetType.getRawType());
+ }
+ PropertyConverter<T> converter = null;
+ final Method factoryMethod = getFactoryMethod(targetType.getRawType(), "of", "valueOf", "instanceOf", "getInstance", "from", "fromString", "parse");
+ if (factoryMethod != null) {
+ converter = new DefaultPropertyConverter<>(factoryMethod, targetType.getRawType());
+ }
+ if (converter == null) {
+ final Constructor<T> constr;
+ try {
+ constr = targetType.getRawType().getDeclaredConstructor(String.class);
+ } catch (NoSuchMethodException e) {
+ LOG.log(Level.FINEST, "No matching constrctor for " + targetType, e);
+ return null;
+ }
+ converter = new PropertyConverter<T>() {
+ @Override
+ public T convert(String value, ConversionContext context) {
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ constr.setAccessible(true);
+ return null;
+ }
+ });
+ return null;
+ }
+ });
+ try {
+ return constr.newInstance(value);
+ } catch (Exception e) {
+ LOG.log(Level.SEVERE, "Error creating new PropertyConverter instance " + targetType, e);
+ }
+ return null;
+ }
+ };
+ }
+ return converter;
+ }
+
+ /**
+ * Tries to evaluate a factory method that can be used to create an instance based on a String.
+ *
+ * @param type the target type
+ * @param methodNames the possible static method names
+ * @return the first method found, or null.
+ */
+ private Method getFactoryMethod(Class<?> type, String... methodNames) {
+ Method m;
+ for (String name : methodNames) {
+ try {
+ m = type.getDeclaredMethod(name, String.class);
+ return m;
+ } catch (NoSuchMethodException | RuntimeException e) {
+ LOG.finest("No such factory method found on type: " + type.getName() + ", methodName: " + name);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof PropertyConverterManager)) {
+ return false;
+ }
+ PropertyConverterManager that = (PropertyConverterManager) o;
+ return converters.equals(that.converters);
+
+ }
+
+ @Override
+ public int hashCode() {
+ return converters.hashCode();
+ }
+
+ /**
+ * Default converters imüöementation perfoming several lookups for String converion
+ * option.
+ * @param <T>
+ */
+ private static class DefaultPropertyConverter<T> implements PropertyConverter<T> {
+
+ private final Method factoryMethod;
+ private final Class<T> targetType;
+
+ DefaultPropertyConverter(Method factoryMethod, Class<T> targetType){
+ this.factoryMethod = Objects.requireNonNull(factoryMethod);
+ this.targetType = Objects.requireNonNull(targetType);
+ }
+
+ @Override
+ public T convert(String value, ConversionContext context) {
+ context.addSupportedFormats(getClass(), "<String -> "+factoryMethod.toGenericString());
+
+ if (!Modifier.isStatic(factoryMethod.getModifiers())) {
+ throw new ConfigException(factoryMethod.toGenericString() +
+ " is not a static method. Only static " +
+ "methods can be used as factory methods.");
+ }
+ try {
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ factoryMethod.setAccessible(true);
+ return null;
+ }
+ });
+ Object invoke = factoryMethod.invoke(null, value);
+ return targetType.cast(invoke);
+ } catch (Exception e) {
+ throw new ConfigException("Failed to decode '" + value + "'", e);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/7917a9f3/code/spi-support/src/main/java/org/apache/tamaya/spisupport/PropertyFilterComparator.java
----------------------------------------------------------------------
diff --git a/code/spi-support/src/main/java/org/apache/tamaya/spisupport/PropertyFilterComparator.java b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/PropertyFilterComparator.java
new file mode 100644
index 0000000..20eef63
--- /dev/null
+++ b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/PropertyFilterComparator.java
@@ -0,0 +1,72 @@
+/*
+ * 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.spisupport;
+
+import org.apache.tamaya.spi.PropertyFilter;
+
+import javax.annotation.Priority;
+import java.io.Serializable;
+import java.util.Comparator;
+
+/**
+ * Comparator for PropertyFilters based on their priority annotations.
+ */
+public final class PropertyFilterComparator implements Comparator<PropertyFilter>, Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final PropertyFilterComparator INSTANCE = new PropertyFilterComparator();
+
+ /**
+ * Get the shared instance of the comparator.
+ * @return the shared instance, never null.
+ */
+ public static PropertyFilterComparator getInstance(){
+ return INSTANCE;
+ }
+
+ private PropertyFilterComparator(){}
+
+ /**
+ * 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 int compare(PropertyFilter filter1, PropertyFilter filter2) {
+ return comparePropertyFilters(filter1, filter2);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/7917a9f3/code/spi-support/src/main/java/org/apache/tamaya/spisupport/PropertyFiltering.java
----------------------------------------------------------------------
diff --git a/code/spi-support/src/main/java/org/apache/tamaya/spisupport/PropertyFiltering.java b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/PropertyFiltering.java
new file mode 100644
index 0000000..20f1aaf
--- /dev/null
+++ b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/PropertyFiltering.java
@@ -0,0 +1,124 @@
+/*
+ * 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.spisupport;
+
+import org.apache.tamaya.spi.ConfigurationContext;
+import org.apache.tamaya.spi.FilterContext;
+import org.apache.tamaya.spi.PropertyFilter;
+import org.apache.tamaya.spi.PropertyValue;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * 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 PropertyFilter}
+ * instance to evaluate the current Configuration.
+ */
+public final class PropertyFiltering{
+ /**
+ * The logger.
+ */
+ private static final Logger LOG = Logger.getLogger(PropertyFiltering.class.getName());
+ /**
+ * The maximal number of filter cycles performed before aborting.
+ */
+ private static final int MAX_FILTER_LOOPS = 10;
+
+ /**
+ * Private singleton constructor.
+ */
+ private PropertyFiltering(){}
+
+ /**
+ * Filters a single value.
+ * @param value the raw value, not {@code null}.
+ * @param context the context
+ * @return the filtered value, including {@code null}.
+ */
+ public static PropertyValue applyFilter(PropertyValue value, ConfigurationContext context) {
+ FilterContext filterContext = new FilterContext(value, context);
+ return filterValue(filterContext);
+ }
+
+ /**
+ * Filters all properties.
+ * @param rawProperties the unfiltered properties, not {@code null}.
+ * @param context the context
+ * @return the filtered value, inclusing null.
+ */
+ public static Map<String, PropertyValue> applyFilters(Map<String, PropertyValue> rawProperties, ConfigurationContext context) {
+ Map<String, PropertyValue> result = new HashMap<>();
+ // Apply filters to values, prevent values filtered to null!
+ for (Map.Entry<String, PropertyValue> entry : rawProperties.entrySet()) {
+ FilterContext filterContext = new FilterContext(entry.getValue(), rawProperties, context);
+ PropertyValue filtered = filterValue(filterContext);
+ if(filtered!=null){
+ result.put(filtered.getKey(), filtered);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Basic filter logic.
+ * @param context the filter context, not {@code null}.
+ * @return the filtered value.
+ */
+ private static PropertyValue filterValue(FilterContext context) {
+ PropertyValue inputValue = context.getProperty();
+ PropertyValue filteredValue = inputValue;
+
+ for (int i = 0; i < MAX_FILTER_LOOPS; i++) {
+ int changes = 0;
+ for (PropertyFilter filter : context.getContext().getPropertyFilters()) {
+ filteredValue = filter.filterProperty(inputValue, context);
+ if (filteredValue != null && !filteredValue.equals(inputValue)) {
+ changes++;
+ LOG.finest("Filter - " + inputValue + " -> " + filteredValue + " by " + filter);
+ }
+ if(filteredValue==null){
+ LOG.finest("Filter removed entry - " + inputValue + ": " + filter);
+ break;
+ }else{
+ inputValue = filteredValue;
+ }
+ }
+ if (changes == 0) {
+ LOG.finest("Finishing filter loop, no changes detected.");
+ break;
+ } else if (filteredValue == null) {
+ 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);
+ }
+ }
+ }
+ return filteredValue;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/7917a9f3/code/spi-support/src/main/java/org/apache/tamaya/spisupport/PropertySourceComparator.java
----------------------------------------------------------------------
diff --git a/code/spi-support/src/main/java/org/apache/tamaya/spisupport/PropertySourceComparator.java b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/PropertySourceComparator.java
new file mode 100644
index 0000000..d572335
--- /dev/null
+++ b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/PropertySourceComparator.java
@@ -0,0 +1,122 @@
+/*
+ * 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.spisupport;
+
+import org.apache.tamaya.spi.PropertySource;
+import org.apache.tamaya.spi.PropertyValue;
+
+import javax.annotation.Priority;
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.Comparator;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Comparator for ordering of PropertySources based on their ordinal method and class name.
+ */
+public class PropertySourceComparator implements Comparator<PropertySource>, Serializable {
+ /** serial version UID. */
+ private static final long serialVersionUID = 1L;
+
+ private static final Logger LOG = Logger.getLogger(PropertySourceComparator.class.getName());
+
+ private static final PropertySourceComparator INSTANCE = new PropertySourceComparator();
+
+ private String alternativeOrdinalKey;
+
+ /** Singleton constructor. */
+ private PropertySourceComparator(){}
+
+ /**
+ * Get the shared instance of the comparator.
+ * @return the shared instance, never null.
+ */
+ public static PropertySourceComparator getInstance(){
+ return INSTANCE;
+ }
+
+
+ /**
+ * Order property source reversely, the most important comes first.
+ *
+ * @param source1 the first PropertySource
+ * @param source2 the second PropertySource
+ * @return the comparison result.
+ */
+ private int comparePropertySources(PropertySource source1, PropertySource source2) {
+ if (getOrdinal(source1) < getOrdinal(source2)) {
+ return -1;
+ } else if (getOrdinal(source1) > getOrdinal(source2)) {
+ return 1;
+ } else {
+ return source1.getClass().getName().compareTo(source2.getClass().getName());
+ }
+ }
+
+ /**
+ * Evaluates an ordinal value from a {@link PropertySource}, Hereby the ordinal of type {@code int}
+ * is evaluated as follows:
+ * <ol>
+ * <li>It evaluates the {@code String} value for {@link PropertySource#TAMAYA_ORDINAL} and tries
+ * to convert it to an {@code int} value, using {@link Integer#parseInt(String)}.</li>
+ * <li>It tries to find and evaluate a method {@code int getOrdinal()}.</li>
+ * <li>It tries to find and evaluate a static field {@code int ORDINAL}.</li>
+ * <li>It tries to find and evaluate a class level {@link Priority} annotation.</li>
+ * <li>It uses the default priority ({@code 0}.</li>
+ * </ol>
+ * @param propertySource the property source, not {@code null}.
+ * @return the ordinal value to compare the property source.
+ */
+ public static int getOrdinal(PropertySource propertySource) {
+ return getOrdinal(propertySource, null);
+ }
+
+ public static int getOrdinal(PropertySource propertySource, String alternativeOrdinalKey) {
+ if(alternativeOrdinalKey!=null) {
+ PropertyValue ordinalValue = propertySource.get(alternativeOrdinalKey);
+ if (ordinalValue != null) {
+ try {
+ return Integer.parseInt(ordinalValue.getValue().trim());
+ } catch (Exception e) {
+ LOG.finest("Failed to parse ordinal from " + alternativeOrdinalKey +
+ " in " + propertySource.getName() + ": " + ordinalValue.getValue());
+ }
+ }
+ }
+ return propertySource.getOrdinal();
+ }
+
+ /**
+ * Overrides/adds the key to evaluate/override a property sources ordinal.
+ * @param ordinalKey sets the alternative ordinal key, if null default
+ * behaviour will be active.
+ * @return the instance for chaining.
+ */
+ public PropertySourceComparator setOrdinalKey(String ordinalKey) {
+ this.alternativeOrdinalKey = ordinalKey;
+ return this;
+ }
+
+ @Override
+ public int compare(PropertySource source1, PropertySource source2) {
+ return comparePropertySources(source1, source2);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/7917a9f3/code/spi-support/src/main/java/org/apache/tamaya/spisupport/ReflectionUtil.java
----------------------------------------------------------------------
diff --git a/code/spi-support/src/main/java/org/apache/tamaya/spisupport/ReflectionUtil.java b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/ReflectionUtil.java
new file mode 100644
index 0000000..390c8d8
--- /dev/null
+++ b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/ReflectionUtil.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.spisupport;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+
+/**
+ * Small utility class used by other parts.
+ */
+public final class ReflectionUtil {
+
+ private ReflectionUtil(){}
+
+ public static ParameterizedType getParametrizedType(Class<?> clazz) {
+ Type[] genericTypes = clazz.getGenericInterfaces();
+ for (Type type : genericTypes) {
+ if (type instanceof ParameterizedType) {
+ return (ParameterizedType) type;
+ }
+
+ }
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/7917a9f3/code/spi-support/src/main/java/org/apache/tamaya/spisupport/RegexPropertyFilter.java
----------------------------------------------------------------------
diff --git a/code/spi-support/src/main/java/org/apache/tamaya/spisupport/RegexPropertyFilter.java b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/RegexPropertyFilter.java
new file mode 100644
index 0000000..1f8cce9
--- /dev/null
+++ b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/RegexPropertyFilter.java
@@ -0,0 +1,84 @@
+/*
+ * 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.spisupport;
+
+import org.apache.tamaya.spi.FilterContext;
+import org.apache.tamaya.spi.PropertyFilter;
+import org.apache.tamaya.spi.PropertyValue;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Predicate filtering using a regex expression operating on the key. It allows either
+ * to define the target keys to be selected (includes), or to be excluded (excludes).
+ */
+public final class RegexPropertyFilter implements PropertyFilter{
+ /** The expression used to include entries that match. */
+ private List<String> includes;
+ /** The expression used to exclude entries that match. */
+ private List<String> excludes;
+
+ /**
+ * Sets the regex expression to be applied on the key to filter the corresponding entry
+ * if matching.
+ * @param expressions the regular expression for inclusion, not null.
+ */
+ public void setIncludes(String... expressions){
+ this.includes = Arrays.asList(expressions);
+ }
+
+ /**
+ * Sets the regex expression to be applied on the key to remove the corresponding entries
+ * if matching.
+ * @param expressions the regular expressions for exclusion, not null.
+ */
+ public void setExcludes(String... expressions){
+ this.excludes= Arrays.asList(expressions);
+ }
+
+ @Override
+ public PropertyValue filterProperty(PropertyValue valueToBeFiltered, FilterContext context) {
+ if(includes!=null){
+ for(String expression:includes){
+ if(context.getProperty().getKey().matches(expression)){
+ return valueToBeFiltered;
+ }
+ }
+ return null;
+ }
+ if(excludes!=null){
+ for(String expression:excludes){
+ if(context.getProperty().getKey().matches(expression)){
+ return null;
+ }
+ }
+ }
+ return valueToBeFiltered;
+ }
+
+ @Override
+ public String toString() {
+ return "RegexPropertyFilter{" +
+ "includes='" + includes + '\'' +
+ "excludes='" + excludes + '\'' +
+ '}';
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/7917a9f3/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/BasePropertySource.java
----------------------------------------------------------------------
diff --git a/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/BasePropertySource.java b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/BasePropertySource.java
new file mode 100644
index 0000000..54481ac
--- /dev/null
+++ b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/BasePropertySource.java
@@ -0,0 +1,173 @@
+/*
+ * 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.spisupport.propertysource;
+
+import org.apache.tamaya.spi.PropertySource;
+import org.apache.tamaya.spi.PropertyValue;
+import org.apache.tamaya.spi.PropertyValueBuilder;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Abstract {@link org.apache.tamaya.spi.PropertySource} that allows to set a default ordinal that will be used, if no
+ * ordinal is provided with the config.
+ */
+public abstract class BasePropertySource implements PropertySource{
+ /** default ordinal that will be used, if no ordinal is provided with the config. */
+ private int defaultOrdinal;
+ /** Used if the ordinal has been set explicitly. */
+ private volatile Integer ordinal;
+ /** The name of the property source. */
+ private String name;
+
+ /**
+ * Constructor.
+ * @param name the (unique) property source name, not {@code null}.
+ */
+ protected BasePropertySource(String name){
+ this.name = Objects.requireNonNull(name);
+ this.defaultOrdinal = 0;
+ }
+
+ /**
+ * Constructor.
+ * @param defaultOrdinal default ordinal that will be used, if no ordinal is provided with the config.
+ */
+ protected BasePropertySource(int defaultOrdinal){
+ this.name = getClass().getSimpleName();
+ this.defaultOrdinal = defaultOrdinal;
+ }
+
+ /**
+ * Constructor.
+ * @param name the (unique) property source name, not {@code null}.
+ * @param defaultOrdinal default ordinal that will be used, if no ordinal is provided with the config.
+ */
+ protected BasePropertySource(String name, int defaultOrdinal){
+ this.name = Objects.requireNonNull(name);
+ this.defaultOrdinal = defaultOrdinal;
+ }
+
+
+ /**
+ * Constructor, using a default ordinal of 0.
+ */
+ protected BasePropertySource(){
+ this(0);
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the property source's (unique) name.
+ * @param name the name, not {@code null}.
+ */
+ public void setName(String name){
+ this.name = Objects.requireNonNull(name);
+ }
+
+ /**
+ * Allows to set the ordinal of this property source explcitly. This will override any evaluated
+ * ordinal, or default ordinal. To reset an explcit ordinal call {@code setOrdinal(null);}.
+ * @param ordinal the explicit ordinal, or {@code null}.
+ */
+ public void setOrdinal(Integer ordinal){
+ this.ordinal = ordinal;
+ }
+
+ /**
+ * Allows to set the ordinal of this property source explcitly. This will override any evaluated
+ * ordinal, or default ordinal. To reset an explcit ordinal call {@code setOrdinal(null);}.
+ * @param defaultOrdinal the default ordinal, or {@code null}.
+ */
+ public void setDefaultOrdinal(Integer defaultOrdinal){
+ this.defaultOrdinal = defaultOrdinal;
+ }
+
+ public int getOrdinal() {
+ Integer ordinal = this.ordinal;
+ if(ordinal!=null){
+ Logger.getLogger(getClass().getName()).finest(
+ "Using explicit ordinal '"+ordinal+"' for property source: " + getName());
+ return ordinal;
+ }
+ PropertyValue configuredOrdinal = get(TAMAYA_ORDINAL);
+ if(configuredOrdinal!=null){
+ try {
+ return Integer.parseInt(configuredOrdinal.getValue());
+ } catch (Exception e) {
+ Logger.getLogger(getClass().getName()).log(Level.WARNING,
+ "Configured ordinal is not an int number: " + configuredOrdinal, e);
+ }
+ }
+ return getDefaultOrdinal();
+ }
+
+ /**
+ * Returns the default ordinal used, when no ordinal is set, or the ordinal was not parseable to an int value.
+ * @return the default ordinal used, by default 0.
+ */
+ public int getDefaultOrdinal(){
+ return defaultOrdinal;
+ }
+
+ @Override
+ public PropertyValue get(String key) {
+ Map<String,PropertyValue> properties = getProperties();
+ PropertyValue val = properties.get(key);
+ if(val==null){
+ return null;
+ }
+ return val;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ BasePropertySource that = (BasePropertySource) o;
+
+ return name.equals(that.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "{" +
+ toStringValues() +
+ '}';
+ }
+
+ protected String toStringValues() {
+ return " defaultOrdinal=" + defaultOrdinal + '\n' +
+ " ordinal=" + ordinal + '\n' +
+ " name='" + name + '\'' + '\n';
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/7917a9f3/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/BuildablePropertySource.java
----------------------------------------------------------------------
diff --git a/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/BuildablePropertySource.java b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/BuildablePropertySource.java
new file mode 100644
index 0000000..fbea188
--- /dev/null
+++ b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/BuildablePropertySource.java
@@ -0,0 +1,231 @@
+/*
+ * 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.spisupport.propertysource;
+
+import org.apache.tamaya.spi.PropertySource;
+import org.apache.tamaya.spi.PropertyValue;
+
+import java.util.*;
+
+/**
+ * A Buildable property source.
+ */
+public class BuildablePropertySource implements PropertySource{
+
+ private int ordinal;
+ private String name = "PropertySource-"+UUID.randomUUID().toString();
+ private Map<String,PropertyValue> properties = new HashMap<>();
+
+ @Override
+ public int getOrdinal() {
+ return ordinal;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public PropertyValue get(String key) {
+ return properties.get(key);
+ }
+
+ @Override
+ public Map<String, PropertyValue> getProperties() {
+ return Collections.unmodifiableMap(properties);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ BuildablePropertySource that = (BuildablePropertySource) o;
+
+ return name.equals(that.name);
+ }
+
+ @Override
+ public boolean isScannable() {
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "BuildablePropertySource{" +
+ "ordinal=" + ordinal +
+ ", name='" + name + '\'' +
+ ", properties=" + properties +
+ '}';
+ }
+
+ /**
+ * Builder builder.
+ *
+ * @return the builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+
+ /**
+ * The type Builder.
+ */
+ public static final class Builder {
+ private int ordinal;
+ private String source = "<on-the-fly-build>";
+ private String name = "PropertySource-"+ UUID.randomUUID().toString();
+ private Map<String,PropertyValue> properties = new HashMap<>();
+
+ private Builder() {
+ }
+
+ /**
+ * With ordinal builder.
+ *
+ * @param ordinal the ordinal
+ * @return the builder
+ */
+ public Builder withOrdinal(int ordinal) {
+ this.ordinal = ordinal;
+ return this;
+ }
+
+ /**
+ * With source builder.
+ *
+ * @param source the source
+ * @return the builder
+ */
+ public Builder withSource(String source) {
+ this.source = Objects.requireNonNull(source);
+ return this;
+ }
+
+ /**
+ * With name builder.
+ *
+ * @param name the name
+ * @return the builder
+ */
+ public Builder withName(String name) {
+ this.name = Objects.requireNonNull(name);
+ return this;
+ }
+
+ /**
+ * With simple property builder.
+ *
+ * @param key the key
+ * @param value the value
+ * @return the builder
+ */
+ public Builder withSimpleProperty(String key, String value) {
+ return withProperties(PropertyValue.of(key, value, this.source));
+ }
+
+ /**
+ * With simple property builder.
+ *
+ * @param key the key
+ * @param value the value
+ * @param source the source
+ * @return the builder
+ */
+ public Builder withSimpleProperty(String key, String value, String source) {
+ return withProperties(PropertyValue.of(key, value, source));
+ }
+
+ /**
+ * With properties builder.
+ *
+ * @param values the values
+ * @return the builder
+ */
+ public Builder withProperties(PropertyValue... values) {
+ for(PropertyValue val:values){
+ this.properties.put(val.getKey(), val);
+ }
+ return this;
+ }
+
+ /**
+ * With properties builder.
+ *
+ * @param properties the properties
+ * @return the builder
+ */
+ public Builder withProperties(Map<String, PropertyValue> properties) {
+ this.properties = Objects.requireNonNull(properties);
+ return this;
+ }
+
+ /**
+ * With properties builder.
+ *
+ * @param properties the properties
+ * @param source the source
+ * @return the builder
+ */
+ public Builder withProperties(Map<String, String> properties, String source) {
+ this.properties.putAll(PropertyValue.map(properties, source));
+ return this;
+ }
+
+ /**
+ * With simple properties builder.
+ *
+ * @param properties the properties
+ * @return the builder
+ */
+ public Builder withSimpleProperties(Map<String, String> properties) {
+ this.properties.putAll(PropertyValue.map(properties, this.source));
+ return this;
+ }
+
+ /**
+ * But builder.
+ *
+ * @return the builder
+ */
+ public Builder but() {
+ return builder().withOrdinal(ordinal).withName(name).withProperties(properties);
+ }
+
+ /**
+ * Build buildable property source.
+ *
+ * @return the buildable property source
+ */
+ public BuildablePropertySource build() {
+ BuildablePropertySource buildablePropertySource = new BuildablePropertySource();
+ buildablePropertySource.name = this.name;
+ buildablePropertySource.properties = this.properties;
+ buildablePropertySource.ordinal = this.ordinal;
+ return buildablePropertySource;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/7917a9f3/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/BuildablePropertySourceProvider.java
----------------------------------------------------------------------
diff --git a/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/BuildablePropertySourceProvider.java b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/BuildablePropertySourceProvider.java
new file mode 100644
index 0000000..1becb50
--- /dev/null
+++ b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/BuildablePropertySourceProvider.java
@@ -0,0 +1,114 @@
+/*
+ * 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.spisupport.propertysource;
+
+import org.apache.tamaya.spi.PropertySource;
+import org.apache.tamaya.spi.PropertySourceProvider;
+
+import java.util.*;
+
+/**
+ * A Buildable property source.
+ */
+public class BuildablePropertySourceProvider implements PropertySourceProvider{
+
+ private List<PropertySource> sources = new ArrayList<>();
+
+ @Override
+ public Collection<PropertySource> getPropertySources() {
+ return Collections.unmodifiableCollection(sources);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ BuildablePropertySourceProvider that = (BuildablePropertySourceProvider) o;
+
+ return sources.equals(that.sources);
+ }
+
+ @Override
+ public int hashCode() {
+ return sources.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "BuildablePropertySourceProvider{" +
+ "sources=" + sources +
+ '}';
+ }
+
+ /**
+ * Builder builder.
+ *
+ * @return the builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+
+
+
+ /**
+ * The type Builder.
+ */
+ public static final class Builder {
+ private List<PropertySource> sources = new ArrayList<>();
+
+ private Builder() {
+ }
+
+ /**
+ * With propertySources.
+ *
+ * @param propertySources the propertySources
+ * @return the builder
+ */
+ public Builder withPropertySourcs(PropertySource... propertySources) {
+ this.sources.addAll(Arrays.asList(propertySources));
+ return this;
+ }
+
+ /**
+ * With property sources builder.
+ *
+ * @param sources the property sources
+ * @return the builder
+ */
+ public Builder withPropertySourcs(Collection<PropertySource> sources) {
+ this.sources.addAll(sources);
+ return this;
+ }
+
+ /**
+ * Build buildable property source.
+ *
+ * @return the buildable property source
+ */
+ public BuildablePropertySourceProvider build() {
+ BuildablePropertySourceProvider buildablePropertySource = new BuildablePropertySourceProvider();
+ buildablePropertySource.sources.addAll(this.sources);
+ return buildablePropertySource;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/7917a9f3/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/CLIPropertySource.java
----------------------------------------------------------------------
diff --git a/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/CLIPropertySource.java b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/CLIPropertySource.java
new file mode 100644
index 0000000..a83722f
--- /dev/null
+++ b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/CLIPropertySource.java
@@ -0,0 +1,137 @@
+/*
+ * 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.spisupport.propertysource;
+
+import org.apache.tamaya.spi.PropertyValue;
+
+import java.util.*;
+
+/**
+ * PropertySource that allows to add the programs main arguments as configuration entries. Unix syntax using '--' and
+ * '-' params is supported.
+ */
+public class CLIPropertySource extends BasePropertySource {
+
+ /** The original main arguments. */
+ private static String[] args = new String[0];
+
+ /** The map of parsed main arguments. */
+ private static Map<String,PropertyValue> mainArgs;
+
+ /** Initializes the initial state. */
+ static{
+ initMainArgs(args);
+ }
+
+ /**
+ * Creates a new instance.
+ */
+ public CLIPropertySource(){
+ this((String[])null);
+ }
+
+ /**
+ * Creates a new instance, allows optionally to pass the main arguments.
+ * @param args the args, or null.
+ */
+ public CLIPropertySource(String... args){
+ super("CLI");
+ if(args!=null){
+ initMainArgs(args);
+ }
+ }
+
+ /**
+ * Creates a new instance, allows optionally to pass the main arguments.
+ * @param args the args, or null.
+ * @param ordinal the ordinal to be applied.
+ */
+ public CLIPropertySource(int ordinal, String... args){
+ if(args!=null){
+ initMainArgs(args);
+ }
+ setOrdinal(ordinal);
+ }
+
+
+
+ /**
+ * Configure the main arguments, hereby parsing and mapping the main arguments into
+ * configuration propertiesi as key-value pairs.
+ * @param args the main arguments, not null.
+ */
+ public static void initMainArgs(String... args){
+ CLIPropertySource.args = Objects.requireNonNull(args);
+ // TODO is there a way to figure out the args?
+ String argsProp = System.getProperty("main.args");
+ if(argsProp!=null){
+ CLIPropertySource.args = argsProp.split("\\s");
+ }
+ Map<String,String> result = null;
+ if(CLIPropertySource.args==null){
+ result = Collections.emptyMap();
+ }else{
+ result = new HashMap<>();
+ String prefix = System.getProperty("main.args.prefix");
+ if(prefix==null){
+ prefix="";
+ }
+ String key = null;
+ for(String arg:CLIPropertySource.args){
+ if(arg.startsWith("--")){
+ arg = arg.substring(2);
+ int index = arg.indexOf("=");
+ if(index>0){
+ key = arg.substring(0,index).trim();
+ result.put(prefix+key, arg.substring(index+1).trim());
+ key = null;
+ }else{
+ result.put(prefix+arg, arg);
+ }
+ }else if(arg.startsWith("-")){
+ key = arg.substring(1);
+ }else{
+ if(key!=null){
+ result.put(prefix+key, arg);
+ key = null;
+ }else{
+ result.put(prefix+arg, arg);
+ }
+ }
+ }
+ }
+ Map<String,PropertyValue> finalProps = new HashMap<>();
+ for(Map.Entry<String,String> en:result.entrySet()) {
+ finalProps.put(en.getKey(),
+ PropertyValue.of(en.getKey(), en.getValue(), "main-args"));
+ }
+ CLIPropertySource.mainArgs = Collections.unmodifiableMap(finalProps);
+ }
+
+ @Override
+ public Map<String, PropertyValue> getProperties() {
+ return Collections.unmodifiableMap(mainArgs);
+ }
+
+ @Override
+ protected String toStringValues() {
+ return super.toStringValues() +
+ " args=" + Arrays.toString(args) + '\n';
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/7917a9f3/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/EnvironmentPropertySource.java
----------------------------------------------------------------------
diff --git a/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/EnvironmentPropertySource.java b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/EnvironmentPropertySource.java
new file mode 100644
index 0000000..bb9f6b8
--- /dev/null
+++ b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/EnvironmentPropertySource.java
@@ -0,0 +1,287 @@
+/*
+ * 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.spisupport.propertysource;
+
+import org.apache.tamaya.spi.PropertyValue;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * <p>{@link PropertySource} to access environment variables via Tamaya
+ * which are set via {@code export VARIABLE=value} on UNIX systems or
+ * {@code set VARIABLE=value} on Windows systems.</p>
+ *
+ * <p>Using the {@linkplain EnvironmentPropertySource} without any
+ * additional configuration gives access to all existing environment
+ * variables available to the Java process Tamaya is running in.</p>
+ *
+ * <h1>Simple usage example</h1>
+ *
+ * <pre>
+ * $ export OPS_MODE=production
+ * $ export COLOR=false
+ * $ java -jar application.jar
+ * </pre>
+ *
+ * <p>To access {@code OPS_MODE} and {@code COLOR} with the following code
+ * fragment could be used:</p>
+ *
+ * <pre>
+ * PropertySource ps = new EnvironmentPropertySource();
+ * PropertyValue opsMode = ps.get("OPS_MODE");
+ * PropertyValue color = ps.get("COLOR");
+ * </pre>
+ *
+ * <h1>Application specific environmet variables with prefix</h1>
+ *
+ * <p>Given the case where to instances of the same application are running on
+ * a single machine but need different values for the environment variable
+ * {@code CUSTOMER}. The {@linkplain EnvironmentPropertySource} allows you
+ * to prefix the environment variable with an application specific prefix
+ * and to access it by the non-prefixed variable name.</p>
+ *
+ * <pre>
+ * $ export CUSTOMER=none
+ * $ export a81.CUSTOMER=moon
+ * $ export b78.CUSTOMER=luna
+ * </pre>
+ *
+ * <p>Given an environment with these tree variables the application running
+ * for the customer called Moon could be started with the following command:</p>
+ *
+ * <pre>
+ * $ java -Dtamaya.envprops.prefix=a81 -jar application.jar
+ * </pre>
+ *
+ * <p>The application specific value can now be accessed from the code of the
+ * application like this:</p>
+ *
+ * <pre>
+ * PropertySource ps = new EnvironmentPropertySource();
+ * PropertyValue pv = ps.get("CUSTOMER");
+ * System.out.println(pv.getValue());
+ * </pre>
+ *
+ * <p>The output of application would be {@code moon}.</p>
+ *
+ * <h1>Disabling the access to environment variables</h1>
+ *
+ * <p>The access to environment variables could be simply
+ * disabled by the setting the systemproperty {@code tamaya.envprops.disable}
+ * or {@code tamaya.defaults.disable} to {@code true}.</p>
+ */
+public class EnvironmentPropertySource extends BasePropertySource {
+ private static final String TAMAYA_ENVPROPS_PREFIX = "tamaya.envprops.prefix";
+ private static final String TAMAYA_ENVPROPS_DISABLE = "tamaya.envprops.disable";
+ private static final String TAMAYA_DEFAULT_DISABLE = "tamaya.defaults.disable";
+
+ /**
+ * Default ordinal for {@link org.apache.tamaya.spisupport.propertysource.EnvironmentPropertySource}
+ */
+ public static final int DEFAULT_ORDINAL = 300;
+
+ /**
+ * Prefix that allows environment properties to virtually be mapped on specified sub section.
+ */
+ private String prefix;
+
+ /**
+ * If true, this property source does not return any properties. This is useful since this
+ * property source is applied by default, but can be switched off by setting the
+ * {@code tamaya.envprops.disable} system/environment property to {@code true}.
+ */
+ private boolean disabled = false;
+
+ private SystemPropertiesProvider propertiesProvider = new SystemPropertiesProvider();
+
+ /**
+ * Creates a new instance. Also initializes the {@code prefix} and {@code disabled} properties
+ * from the system-/ environment properties:
+ * <pre>
+ * tamaya.envprops.prefix
+ * tamaya.envprops.disable
+ * </pre>
+ */
+ public EnvironmentPropertySource(){
+ initFromSystemProperties();
+ }
+
+ /**
+ * Initializes the {@code prefix} and {@code disabled} properties from the system-/
+ * environment properties:
+ * <pre>
+ * tamaya.envprops.prefix
+ * tamaya.envprops.disable
+ * </pre>
+ */
+ private void initFromSystemProperties() {
+ String value = System.getProperty("tamaya.envprops.prefix");
+ if(value==null){
+ prefix = System.getenv("tamaya.envprops.prefix");
+ }
+ value = System.getProperty("tamaya.envprops.disable");
+ if(value==null){
+ value = System.getenv("tamaya.envprops.disable");
+ }
+ if(value==null){
+ value = System.getProperty("tamaya.defaults.disable");
+ }
+ if(value==null){
+ value = System.getenv("tamaya.defaults.disable");
+ }
+ if(value!=null && !value.isEmpty()) {
+ this.disabled = Boolean.parseBoolean(value);
+ }
+ }
+
+ /**
+ * Creates a new instance using a fixed ordinal value.
+ * @param ordinal the ordinal number.
+ */
+ public EnvironmentPropertySource(int ordinal){
+ this(null, ordinal);
+ }
+
+ /**
+ * Creates a new instance.
+ * @param prefix the prefix to be used, or null.
+ * @param ordinal the ordinal to be used.
+ */
+ public EnvironmentPropertySource(String prefix, int ordinal){
+ super("environment-properties");
+ this.prefix = prefix;
+ setOrdinal(ordinal);
+ }
+
+ /**
+ * Creates a new instance.
+ * @param prefix the prefix to be used, or null.
+ */
+ public EnvironmentPropertySource(String prefix){
+ this.prefix = prefix;
+ }
+
+ @Override
+ public int getDefaultOrdinal() {
+ return DEFAULT_ORDINAL;
+ }
+
+ @Override
+ public String getName() {
+ if (isDisabled()) {
+ return "environment-properties(disabled)";
+ }
+ return "environment-properties";
+ }
+
+ @Override
+ public PropertyValue get(String key) {
+ if (isDisabled()) {
+ return null;
+ }
+
+ String effectiveKey = hasPrefix() ? getPrefix() + "." + key
+ : key;
+
+ String value = getPropertiesProvider().getenv(effectiveKey);
+
+ return PropertyValue.of(key, value, getName());
+ }
+
+ private boolean hasPrefix() {
+ return null != prefix;
+ }
+
+ @Override
+ public Map<String, PropertyValue> getProperties() {
+ if(disabled){
+ return Collections.emptyMap();
+ }
+ String prefix = this.prefix;
+ if(prefix==null) {
+ Map<String, PropertyValue> entries = new HashMap<>(System.getenv().size());
+ for (Map.Entry<String, String> entry : System.getenv().entrySet()) {
+ entries.put(entry.getKey(), PropertyValue.of(entry.getKey(), entry.getValue(), getName()));
+ }
+ return entries;
+ }else{
+ Map<String, PropertyValue> entries = new HashMap<>(System.getenv().size());
+ for (Map.Entry<String, String> entry : System.getenv().entrySet()) {
+ entries.put(prefix + entry.getKey(), PropertyValue.of(prefix + entry.getKey(), entry.getValue(), getName()));
+ }
+ return entries;
+ }
+ }
+
+
+ @Override
+ protected String toStringValues() {
+ return super.toStringValues() +
+ " prefix=" + prefix + '\n' +
+ " disabled=" + disabled + '\n';
+ }
+
+ void setPropertiesProvider(SystemPropertiesProvider spp) {
+ propertiesProvider = spp;
+ initFromSystemProperties();
+ }
+
+ SystemPropertiesProvider getPropertiesProvider() {
+ return propertiesProvider;
+ }
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+ public boolean isDisabled() {
+ return disabled;
+ }
+
+ /**
+ * <p>Provides access to the system properties used to configure
+ * {@linkplain EnvironmentPropertySource}.</p>
+ *
+ * <p>This implementation delegates all property lookups
+ * to {@linkplain System#getProperty(String)}.</p>
+ */
+ static class SystemPropertiesProvider {
+ String getEnvPropsPrefix() {
+ return System.getenv(TAMAYA_ENVPROPS_PREFIX);
+ }
+
+ String getEnvPropsDisable() {
+ return System.getenv(TAMAYA_ENVPROPS_DISABLE);
+ }
+
+ String getDefaultsDisable() {
+ return System.getenv(TAMAYA_DEFAULT_DISABLE);
+ }
+
+ String getenv(String name) {
+ return System.getenv(name);
+ }
+
+ Map<String, String> getenv() {
+ return System.getenv();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/7917a9f3/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/JavaConfigurationPropertySource.java
----------------------------------------------------------------------
diff --git a/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/JavaConfigurationPropertySource.java b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/JavaConfigurationPropertySource.java
new file mode 100644
index 0000000..92f520e
--- /dev/null
+++ b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/JavaConfigurationPropertySource.java
@@ -0,0 +1,134 @@
+/*
+ * 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.spisupport.propertysource;
+
+import org.apache.tamaya.ConfigException;
+import org.apache.tamaya.spi.PropertySource;
+import org.apache.tamaya.spi.PropertyValue;
+import org.apache.tamaya.spi.ServiceContextManager;
+import org.apache.tamaya.spisupport.PropertySourceComparator;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.*;
+
+import static java.lang.String.format;
+import static java.lang.Thread.currentThread;
+
+/**
+ * Provider which reads all {@value DEFAULT_SIMPLE_PROPERTIES_FILE_NAME} and
+ * {@value DEFAULT_XML_PROPERTIES_FILE_NAME} files found in the
+ * classpath. By setting
+ * {@code tamaya.defaultprops.disable} or {@code tamaya.defaults.disable}
+ * as system or environment property this feature can be disabled.
+ */
+public class JavaConfigurationPropertySource extends BasePropertySource {
+ /**
+ * Default location in the classpath, where Tamaya looks for simple line based configuration by default.
+ */
+ public static final String DEFAULT_SIMPLE_PROPERTIES_FILE_NAME="META-INF/javaconfiguration.properties";
+
+ /**
+ * Default location in the classpath, where Tamaya looks for XML based configuration by default.
+ */
+ public static final String DEFAULT_XML_PROPERTIES_FILE_NAME = "META-INF/javaconfiguration.xml";
+
+ private static final int DEFAULT_ORDINAL = 900;
+
+ private boolean enabled = evaluateEnabled();
+
+ public JavaConfigurationPropertySource(){
+ super("resource:META-INF/javaconfiguration.*", DEFAULT_ORDINAL);
+ }
+
+ private boolean evaluateEnabled() {
+ String value = System.getProperty("tamaya.defaultprops.disable");
+ if(value==null){
+ value = System.getenv("tamaya.defaultprops.disable");
+ }
+ if(value==null){
+ value = System.getProperty("tamaya.defaults.disable");
+ }
+ if(value==null){
+ value = System.getenv("tamaya.defaults.disable");
+ }
+ if(value==null){
+ return true;
+ }
+ return value.isEmpty() || !Boolean.parseBoolean(value);
+ }
+
+ private List<PropertySource> getPropertySources() {
+ List<PropertySource> propertySources = new ArrayList<>();
+ propertySources.addAll(loadPropertySourcesByName(DEFAULT_SIMPLE_PROPERTIES_FILE_NAME));
+ propertySources.addAll(loadPropertySourcesByName(DEFAULT_XML_PROPERTIES_FILE_NAME));
+ Collections.sort(propertySources, PropertySourceComparator.getInstance());
+ return propertySources;
+ }
+
+ private Collection<? extends PropertySource> loadPropertySourcesByName(String filename) {
+ List<PropertySource> propertySources = new ArrayList<>();
+ Enumeration<URL> propertyLocations;
+ try {
+ propertyLocations = ServiceContextManager.getServiceContext()
+ .getResources(filename, currentThread().getContextClassLoader());
+ } catch (IOException e) {
+ String msg = format("Error while searching for %s", filename);
+
+ throw new ConfigException(msg, e);
+ }
+
+ while (propertyLocations.hasMoreElements()) {
+ URL currentUrl = propertyLocations.nextElement();
+ SimplePropertySource sps = new SimplePropertySource(currentUrl);
+
+ propertySources.add(sps);
+ }
+
+ return propertySources;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled){
+ this.enabled = enabled;
+ }
+
+
+ @Override
+ public Map<String, PropertyValue> getProperties() {
+ if (!isEnabled()) {
+ return Collections.emptyMap();
+ }
+ Map<String,PropertyValue> result = new HashMap<>();
+ for(PropertySource ps:getPropertySources()){
+ result.putAll(ps.getProperties());
+ }
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "JavaConfigurationPropertySource{" +
+ "enabled=" + enabled +
+ '}';
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/7917a9f3/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/MapPropertySource.java
----------------------------------------------------------------------
diff --git a/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/MapPropertySource.java b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/MapPropertySource.java
new file mode 100644
index 0000000..0cabb35
--- /dev/null
+++ b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/MapPropertySource.java
@@ -0,0 +1,102 @@
+/*
+ * 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.spisupport.propertysource;
+
+import org.apache.tamaya.spi.PropertyValue;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * Simple PropertySource implementation that just takes a Map and an (optional) priority.
+ * Optionally the entries passed can be mapped to a different rootContext.
+ */
+public class MapPropertySource extends BasePropertySource {
+
+ /**
+ * The current properties.
+ */
+ private final Map<String, PropertyValue> props = new HashMap<>();
+
+ /**
+ * Creates a new instance, hereby using the default mechanism for evaluating the property source's
+ * priority.
+ *
+ * @param name unique name of this source.
+ * @param props the properties
+ */
+ public MapPropertySource(String name, Map<String, String> props) {
+ this(name, props, null);
+ }
+
+ /**
+ * Creates a new instance, hereby using the default mechanism for evaluating the property source's
+ * priority, but applying a custom mapping {@code prefix} to the entries provided.
+ *
+ * @param name unique name of this source.
+ * @param props the properties
+ * @param prefix the prefix context mapping, or null (for no mapping).
+ */
+ public MapPropertySource(String name, Map<String, String> props, String prefix) {
+ super(name);
+ if (prefix == null) {
+ for (Map.Entry<String, String> en : props.entrySet()) {
+ this.props.put(en.getKey(),
+ PropertyValue.of(en.getKey(), en.getValue(), name));
+ }
+ } else {
+ for (Map.Entry<String, String> en : props.entrySet()) {
+ this.props.put(prefix + en.getKey(),
+ PropertyValue.of(prefix + en.getKey(), en.getValue(), name));
+ }
+ }
+ }
+
+ /**
+ * Creates a new instance, hereby using the default mechanism for evaluating the property source's
+ * priority, but applying a custom mapping {@code rootContext} to the entries provided.
+ *
+ * @param name unique name of this source.
+ * @param props the properties
+ * @param prefix the prefix context mapping, or null (for no mapping).
+ */
+ public MapPropertySource(String name, Properties props, String prefix) {
+ this(name, getMap(props), prefix);
+ }
+
+ /**
+ * Simple method to convert Properties into a Map instance.
+ * @param props the properties, not null.
+ * @return the corresponding Map instance.
+ */
+ public static Map<String, String> getMap(Properties props) {
+ Map<String, String> result = new HashMap<>();
+ for (Map.Entry en : props.entrySet()) {
+ result.put(en.getKey().toString(), en.getValue().toString());
+ }
+ return result;
+ }
+
+
+ @Override
+ public Map<String, PropertyValue> getProperties() {
+ return Collections.unmodifiableMap(this.props);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/7917a9f3/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/PropertiesResourcePropertySource.java
----------------------------------------------------------------------
diff --git a/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/PropertiesResourcePropertySource.java b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/PropertiesResourcePropertySource.java
new file mode 100644
index 0000000..27b6e4b
--- /dev/null
+++ b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/propertysource/PropertiesResourcePropertySource.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.spisupport.propertysource;
+
+import org.apache.tamaya.spi.ServiceContextManager;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Simple PropertySource, with a fixed ordinal that reads a .properties file from a given URL.
+ */
+public class PropertiesResourcePropertySource extends MapPropertySource {
+ /** The logger used. */
+ private static final Logger LOGGER = Logger.getLogger(PropertiesResourcePropertySource.class.getName());
+
+ /**
+ * Creates a new instance.
+ * @param url the resource URL, not null.
+ */
+ public PropertiesResourcePropertySource(URL url){
+ this(url, null);
+ }
+
+ /**
+ * Creates a new instance.
+ * @param prefix the (optional) prefix context for mapping (prefixing) the properties loaded.
+ * @param url the resource URL, not null.
+ */
+ public PropertiesResourcePropertySource(URL url, String prefix){
+ super(url.toExternalForm(), loadProps(url), prefix);
+ }
+
+ /**
+ * Creates a new instance.
+ * @param prefix the (optional) prefix context for mapping (prefixing) the properties loaded.
+ * @param path the resource path, not null.
+ */
+ public PropertiesResourcePropertySource(String path, String prefix){
+ super(path, loadProps(path, null), prefix);
+ }
+
+ /**
+ * Creates a new instance.
+ * @param prefix the (optional) prefix context for mapping (prefixing) the properties loaded.
+ * @param path the resource path, not null.
+ */
+ public PropertiesResourcePropertySource(String path, String prefix, ClassLoader cl){
+ super(path, loadProps(path, cl), prefix);
+ }
+
+ /**
+ * Loads the properties using the JDK's Property loading mechanism.
+ * @param path the resource classpath, not null.
+ * @return the loaded properties.
+ */
+ private static Map<String, String> loadProps(String path, ClassLoader cl) {
+ if(cl==null){
+ cl = PropertiesResourcePropertySource.class.getClassLoader();
+ }
+ URL url = ServiceContextManager.getServiceContext().getResource(path, cl);
+ return loadProps(url);
+ }
+
+ /**
+ * Loads the properties using the JDK's Property loading mechanism.
+ * @param url the resource URL, not null.
+ * @return the loaded properties.
+ */
+ private static Map<String, String> loadProps(URL url) {
+ Map<String,String> result = new HashMap<>();
+ if(url!=null) {
+ try (InputStream is = url.openStream()) {
+ Properties props = new Properties();
+ props.load(is);
+ for (Map.Entry en : props.entrySet()) {
+ result.put(en.getKey().toString(), en.getValue().toString());
+ }
+ } catch (Exception e) {
+ LOGGER.log(Level.WARNING, "Failed to read properties from " + url, e);
+ }
+ }else{
+ LOGGER.log(Level.WARNING, "No properties found at " + url);
+ }
+ return result;
+ }
+
+}