http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/compat/src/main/java/org/apache/tamaya/spi/ConversionContext.java ---------------------------------------------------------------------- diff --git a/code/compat/src/main/java/org/apache/tamaya/spi/ConversionContext.java b/code/compat/src/main/java/org/apache/tamaya/spi/ConversionContext.java new file mode 100644 index 0000000..22c4d5b --- /dev/null +++ b/code/compat/src/main/java/org/apache/tamaya/spi/ConversionContext.java @@ -0,0 +1,266 @@ +/* + * 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.apache.tamaya.Configuration; + +import java.lang.reflect.AnnotatedElement; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +/** + * A conversion context containing all the required values for implementing conversion. Use the included #Builder + * for creating new instances of. This class is thread-safe to use. Adding supported formats is synchronized. + * @see PropertyConverter + */ +public class ConversionContext { + + private final Configuration configuration; + private final String key; + private final TypeLiteral<?> targetType; + private final AnnotatedElement annotatedElement; + private final List<String> supportedFormats = new ArrayList<>(); + private final ConfigurationContext configurationContext; + + /** + * Private constructor used from builder. + * @param builder the builder, not {@code null}. + */ + protected ConversionContext(Builder builder){ + this.key = builder.key; + this.annotatedElement = builder.annotatedElement; + this.targetType = builder.targetType; + this.supportedFormats.addAll(builder.supportedFormats); + this.configuration = builder.configuration; + this.configurationContext = builder.configurationContext; + } + + /** + * Get the key accessed. This information is very useful to evaluate additional metadata needed to determine/ + * control further aspects of the conversion. + * @return the key. This may be null in case where a default value has to be converted and no unique underlying + * key/value configuration is present. + */ + public String getKey(){ + return key; + } + + /** + * Get the target type required. + * @return the target type required. + */ + public TypeLiteral<?> getTargetType(){ + return targetType; + } + + /** + * Get the annotated element, if conversion is performed using injection mechanisms. + * @return the annotated element, or {@code null}. + */ + public AnnotatedElement getAnnotatedElement(){ + return annotatedElement; + } + + /** + * Get the configuration, which is targeted. + * @return the configuration instance, or {@code null}. + */ + public Configuration getConfiguration(){ + return configuration; + } + + /** + * Allows to add information on the supported/tried formats, which can be shown to the user, especially when + * conversion failed. Adding of formats is synchronized, all formats are added in order to the overall list. + * This means formats should be passed in order of precedence. + * @param converterType the converters, which implements the formats provided. + * @param formatDescriptors the format descriptions in a human readable form, e.g. as regular expressions. + */ + public void addSupportedFormats(@SuppressWarnings("rawtypes") Class<? extends PropertyConverter> converterType, String... formatDescriptors){ + synchronized (supportedFormats){ + for(String format: formatDescriptors) { + supportedFormats.add(format + " (" + converterType.getSimpleName() + ")"); + } + } + } + + /** + * Get the supported/tried formats in precedence order. The contents of this method depends on the + * {@link PropertyConverter} instances involved in a conversion. + * @return the supported/tried formats, never {@code null}. + */ + public List<String> getSupportedFormats(){ + synchronized (supportedFormats){ + return new ArrayList<>(supportedFormats); + } + } + + @Override + public String toString() { + return "ConversionContext{" + + "configuration=" + configuration + + ", key='" + key + '\'' + + ", targetType=" + targetType + + ", annotatedElement=" + annotatedElement + + ", supportedFormats=" + supportedFormats + + '}'; + } + + public ConfigurationContext getConfigurationContext() { + return configurationContext; + } + + /** + * Builder to create new instances of {@link ConversionContext}. + */ + public static final class Builder{ + /** The backing configuration. */ + private Configuration configuration; + /** The configuration context. */ + private ConfigurationContext configurationContext; + /** The accessed key, or null. */ + private String key; + /** The target type. */ + private TypeLiteral<?> targetType; + /** The injection target (only set with injection used). */ + private AnnotatedElement annotatedElement; + /** The ordered list of formats tried. */ + private final Set<String> supportedFormats = new HashSet<>(); + + /** + * Creates a new Builder instance. + * @param targetType the target type + */ + public Builder(TypeLiteral<?> targetType) { + this(null, null, null, targetType); + } + + /** + * Creates a new Builder instance. + * @param key the requested key, may be null. + * @param targetType the target type + */ + public Builder(String key, TypeLiteral<?> targetType) { + this(null, null, key, targetType); + } + + /** + * Creates a new Builder instance. + * @param configuration the configuration, not {@code null}. + * @param configurationContext configuration context, not {@code null}. + * @param key the requested key, may be {@code null}. + * @param targetType the target type + */ + public Builder(Configuration configuration, ConfigurationContext configurationContext, String key, TypeLiteral<?> targetType){ + this.key = key; + this.configuration = configuration; + this.configurationContext = configurationContext; + this.targetType = Objects.requireNonNull(targetType); + } + + /** + * Sets the key. + * @param key the key, not {@code null}. + * @return the builder instance, for chaining + */ + public Builder setKey(String key){ + this.key = Objects.requireNonNull(key); + return this; + } + + /** + * Sets the configuration. + * @param configuration the configuration, not {@code null} + * @return the builder instance, for chaining + */ + public Builder setConfiguration(Configuration configuration){ + this.configuration = Objects.requireNonNull(configuration); + return this; + } + + /** + * Sets the configuration. + * @param configurationContext the configuration, not {@code null} + * @return the builder instance, for chaining + */ + public Builder setConfigurationContext(ConfigurationContext configurationContext){ + this.configurationContext = Objects.requireNonNull(configurationContext); + return this; + } + + /** + * Sets the annotated element, when configuration is injected. + * @param annotatedElement the annotated element, not {@code null} + * @return the builder instance, for chaining + */ + public Builder setAnnotatedElement(AnnotatedElement annotatedElement){ + this.annotatedElement = Objects.requireNonNull(annotatedElement); + return this; + } + + /** + * Sets the target type explicitly. This is required in some rare cases, e.g. injection of {@code Provider} + * instances, where the provider's result type must be produced. + * @param targetType the + * @return the builder for chaining. + */ + public Builder setTargetType(@SuppressWarnings("rawtypes") TypeLiteral targetType) { + this.targetType = Objects.requireNonNull(targetType); + return this; + } + + /** + * Add the formats provided by a {@link PropertyConverter}. This method should be called by each converters + * performing/trying conversion, so the user can be given feedback on the supported formats on failure. + * @param converterType the converters type, not {@code null}. + * @param formatDescriptors the formats supported in a human readable form, e.g. as regular expressions. + * @return the builder instance, for chaining + */ + public Builder addSupportedFormats(@SuppressWarnings("rawtypes") Class<? extends PropertyConverter> converterType, String... formatDescriptors){ + for(String format: formatDescriptors) { + supportedFormats.add(format + " (" + converterType.getSimpleName() + ")"); + } + return this; + } + + /** + * Builds a new context instance. + * @return a new context, never null. + */ + public ConversionContext build(){ + return new ConversionContext(this); + } + + @Override + public String toString() { + return "Builder{" + + "configuration=" + configuration + + "context=" + configurationContext + + ", key='" + key + '\'' + + ", targetType=" + targetType + + ", annotatedElement=" + annotatedElement + + ", supportedFormats=" + supportedFormats + + '}'; + } + + } +}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/compat/src/main/java/org/apache/tamaya/spi/FilterContext.java ---------------------------------------------------------------------- diff --git a/code/compat/src/main/java/org/apache/tamaya/spi/FilterContext.java b/code/compat/src/main/java/org/apache/tamaya/spi/FilterContext.java new file mode 100644 index 0000000..2851697 --- /dev/null +++ b/code/compat/src/main/java/org/apache/tamaya/spi/FilterContext.java @@ -0,0 +1,140 @@ +/* + * 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.apache.tamaya.Configuration; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * A filter context containing all the required values for implementing filtering. + * + * @see PropertyFilter + */ +public class FilterContext { + /** The key. */ + private final PropertyValue value; + /** The current context. */ + private final ConfigurationContext context; + @Experimental + private Map<String, PropertyValue> configEntries = new HashMap<>(); + @Experimental + private boolean singlePropertyScoped; + + + /** + * Creates a new FilterContext, for filtering of a multi value access + * using {@link Configuration#getProperties()}. + * + * @param value the value under evaluation, not {@code null}. + * @param configEntries the raw configuration data available in the + * current evaluation context, not {@code null}. + * @param context the current context, not {@code null}. + */ + public FilterContext(PropertyValue value, Map<String,PropertyValue> configEntries, ConfigurationContext context) { + Objects.requireNonNull(value, "Value must not be null."); + Objects.requireNonNull(configEntries, "Initial configuration entries must be not null."); + Objects.requireNonNull(context, "Context must be not null."); + + this.singlePropertyScoped = false; + this.value = Objects.requireNonNull(value); + this.context = Objects.requireNonNull(context); + this.configEntries.putAll(configEntries); + this.configEntries = Collections.unmodifiableMap(this.configEntries); + } + + /** + * Creates a new FilterContext, for filtering of a single value access + * using {@link Configuration#getProperties()}. + * @param value the value under evaluation, not {@code null}. + * @param context the current context, not {@code null}. + */ + public FilterContext(PropertyValue value, ConfigurationContext context) { + Objects.requireNonNull(value, "Value must not be null."); + Objects.requireNonNull(context, "Context must be not null."); + + this.singlePropertyScoped = true; + this.context = Objects.requireNonNull(context); + this.value = Objects.requireNonNull(value); + this.configEntries = Collections.unmodifiableMap(this.configEntries); + } + + /** + * Get the current context. + * @return the current context, not {@code null}. + */ + public ConfigurationContext getContext(){ + return context; + } + + /** + * Get the property value under evaluation. This information is very useful to evaluate additional metadata needed to determine/ + * control further aspects of the conversion. + * + * @return the key. This may be null in case where a default value has to be converted and no unique underlying + * key/value configuration is present. + */ + public PropertyValue getProperty() { + return value; + } + + /** + * Method that determines if filtering is done for a single property accessed, or as part of call to + * {@code getProperties()}. + * @return true, if its scoped to a single property accessed. + */ + @Experimental + public boolean isSinglePropertyScoped(){ + return singlePropertyScoped; + } + + /** + * This map contains the following keys: + * <ul> + * <li>the original value <b>before</b> any filters were applied on it.</li> + * <li>all values starting with an {@code _<key>.}, for example {@code a.value} + * may have a map set with {@code a.value} (oringinal value), {@code _a.value.origin, + * _a.value.type, etc}. The exact contents is determine by the {@link PropertySource}s + * active.</li> + * </ul> + * Also important to know is that this map given contains all the evaluated raw entries, regardless + * of the filters that are later applied. This ensures that met-information required by one filter is + * not hidden by another filter, because of an invalid filter ordering. In other words filters may remove + * key/value pairs, e.g. fir security reasons, by returning {@code null}, but the values in the raw map + * passed as input to the filter process will not be affected by any such removal (but the final properties + * returned are affected, of course). + * + * Finally, when a single property is accessed, e.g. by calling {@code Configuration.get(String)}. + * + * @return the configuration instance, or null. + */ + @Experimental + public Map<String, PropertyValue> getConfigEntries() { + return configEntries; + } + + @Override + public String toString() { + return "FilterContext{value='" + value + "', configEntries=" + configEntries.keySet() + '}'; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/compat/src/main/java/org/apache/tamaya/spi/PropertyConverter.java ---------------------------------------------------------------------- diff --git a/code/compat/src/main/java/org/apache/tamaya/spi/PropertyConverter.java b/code/compat/src/main/java/org/apache/tamaya/spi/PropertyConverter.java new file mode 100644 index 0000000..ab96b6b --- /dev/null +++ b/code/compat/src/main/java/org/apache/tamaya/spi/PropertyConverter.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.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 multi-value (e.g. collections) or complex if needed. + * + * @param <T> the type of the type literal + */ +@FunctionalInterface +public interface PropertyConverter<T>{ + + /** + * Convert the given configuration keys from its String representation into the required target type. + * The context instance passed also allows to add a list of supported formats, which is very handy in case a + * value could not be converted. This list of supported formats can then shown to the user to give some hints + * how a value could be configured. + * + * @param value configuration key that needs to be converted + * @param context the {@link ConversionContext}, containing the String value and the requested configuration key. + * @return converted keys + * @see ConversionContext#addSupportedFormats(Class, String...) + */ + T convert(String value, ConversionContext context); + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/compat/src/main/java/org/apache/tamaya/spi/PropertyFilter.java ---------------------------------------------------------------------- diff --git a/code/compat/src/main/java/org/apache/tamaya/spi/PropertyFilter.java b/code/compat/src/main/java/org/apache/tamaya/spi/PropertyFilter.java new file mode 100644 index 0000000..3054496 --- /dev/null +++ b/code/compat/src/main/java/org/apache/tamaya/spi/PropertyFilter.java @@ -0,0 +1,51 @@ +/* + * 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> + */ +@FunctionalInterface +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.</p> + * <h3>Implementation specification</h3> + * Implementations of this class must be + * <ul> + * <li>reentrant</li> + * <li>thread-safe</li> + * </ul> + * @param value the value to be filtered, which also can be {@code null} if removed by another filter. + * @param context the filter context, not {@code null}. + * @return the filtered value, or {@code null} if the value should be removed alltogether. + * @see PropertyValue + * @see PropertyValueBuilder + */ + PropertyValue filterProperty(PropertyValue value, FilterContext context); + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/compat/src/main/java/org/apache/tamaya/spi/PropertySource.java ---------------------------------------------------------------------- diff --git a/code/compat/src/main/java/org/apache/tamaya/spi/PropertySource.java b/code/compat/src/main/java/org/apache/tamaya/spi/PropertySource.java new file mode 100644 index 0000000..e38e278 --- /dev/null +++ b/code/compat/src/main/java/org/apache/tamaya/spi/PropertySource.java @@ -0,0 +1,174 @@ +/* +* 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.apache.tamaya.Configuration; + +import java.util.Collections; +import java.util.Map; + + +/** + * <p>This interface models a provider that serves configuration properties. The contained + * properties may be read from a Map of single or several sources (composite). + * PropertySources are the building blocks of the final configuration. </p> + * <h3>Implementation Requirements</h3> + * <p>Implementations of this interface must be</p> + * <ul> + * <li>Thread safe.</li> + * </ul> + * + * <p>A PropertySourceProvider will get picked up via the + * {@link java.util.ServiceLoader} mechanism and can be registered via + * {@code META-INF/services/org.apache.tamaya.spi.PropertySource} + * </p> + * <p> + * If you like to addSources 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 + */ + String TAMAYA_ORDINAL = "tamaya.ordinal"; + + /** + * A resusable instance of an empty PropertySource. + */ + PropertySource EMPTY = new PropertySource() { + + @Override + public int getOrdinal() { + return Integer.MIN_VALUE; + } + + @Override + public String getName() { + return "<empty>"; + } + + @Override + public PropertyValue get(String key) { + return null; + } + + @Override + public Map<String, PropertyValue> getProperties() { + return Collections.emptyMap(); + } + + @Override + public boolean isScannable() { + return false; + } + + @Override + public String toString(){ + return "PropertySource.EMPTY"; + } + }; + + + /** + * The ordinal value is the default ordering parameter which definines the default order of + * auto-discovered property sources. Ordering of property sources is important since values + * from property sources with higher ordinal values override values from less significant + * property sources. + * + * By default Tamaya includes the following property sources: + * <ol> + * <li>Properties file values (/META-INF/javaconfiguration.properties) (ordinal 100)</li> + * <li>JNDI values (ordinal 200, only when adding the {@code tamaya-jndi} extension module)</li> + * <li>Environment properties (ordinal 300)</li> + * <li>System properties (ordinal 1000)</li> + * </ol> + * + * <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 > 1000 + * </p> + * <p> + * If a custom implementation should be invoked <b>after</b> the default implementations, use a value < 100 + * </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 > 1000. That means, you have to add e.g. tamaya.ordinal=401 to + * /META-INF/javaconfiguration.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> + * + * In cases where it is not possible to change a config sources ordinal value, you may have several options: + * <ul> + * <li>you can addSources an alternate implementation of {@link PropertyValueCombinationPolicy}.</li> + * <li>you can use a {@link ConfigurationContextBuilder} to redefine the source order and finally use + * {@link org.apache.tamaya.ConfigurationProvider#setConfiguration(Configuration)} to + * change the current default {@link Configuration}.</li> + * <li>finally, the imeplementor of this API may define alternate mechanism to reconfigure an ordinal + * in a vendor specific way.</li> + * </ul> + * @return the 'importance' aka ordinal of the configured values. The higher, the more important. + */ + 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 {@code null}. + */ + String getName(); + + /** + * Access a property. + * + * @param key the property's key, not {@code null}. + * @return the property value map, where {@code map.get(key) == value}, including also any metadata. In case a + * value is null, simply return {@code null}. + */ + PropertyValue get(String key); + + /** + * Access the current properties as Set. The resulting Map may not return all items accessible, e.g. + * when the underlying storage does not support iteration of its entries. + {@code null} + * @return the corresponding map, never null. + */ + Map<String, PropertyValue> getProperties(); + + /** + * Determines if this config source can 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 might happen + * if the underlying storage doesn't support listing. + * </p> + * + * @return {@code true} if this PropertySource can be scanned for its list of properties, + * {@code false} if it cannot/should not be scanned. + */ + default boolean isScannable(){ + return true; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/compat/src/main/java/org/apache/tamaya/spi/PropertySourceProvider.java ---------------------------------------------------------------------- diff --git a/code/compat/src/main/java/org/apache/tamaya/spi/PropertySourceProvider.java b/code/compat/src/main/java/org/apache/tamaya/spi/PropertySourceProvider.java new file mode 100644 index 0000000..aa09ad8 --- /dev/null +++ b/code/compat/src/main/java/org/apache/tamaya/spi/PropertySourceProvider.java @@ -0,0 +1,61 @@ +/* + * 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; +import java.util.Collections; + +/** + * <p>Implement this interfaces to provide a PropertySource provider which + * is able to addSources 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> + */ +@FunctionalInterface +public interface PropertySourceProvider { + + /** + * A resusable instance of an empty PropertySource. + */ + PropertySourceProvider EMPTY = new PropertySourceProvider() { + + @Override + public Collection<PropertySource> getPropertySources() { + return Collections.emptySet(); + } + + @Override + public String toString(){ + return "PropertySourceProvider(empty)"; + } + }; + + /** + * @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/d0e14ed7/code/compat/src/main/java/org/apache/tamaya/spi/PropertyValue.java ---------------------------------------------------------------------- diff --git a/code/compat/src/main/java/org/apache/tamaya/spi/PropertyValue.java b/code/compat/src/main/java/org/apache/tamaya/spi/PropertyValue.java new file mode 100644 index 0000000..c538de7 --- /dev/null +++ b/code/compat/src/main/java/org/apache/tamaya/spi/PropertyValue.java @@ -0,0 +1,232 @@ +/* + * 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.io.Serializable; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * Class modelling the result of a request for a property value. A property value is basically identified by its key. + * There might be reasons, where one want to further analyze, which PropertySources provided a value and which not, so + * it is possible to create a PropertyValue with a null value. Nevertheless in all cases the provider source (typically + * the name of the PropertySource) must be set. + */ +public final class PropertyValue implements Serializable{ + private static final long serialVersionUID = 1L; + /** The requested key. */ + private String key; + /** The value. */ + private String value; + /** The source of the value. */ + private String source; + /** Additional metadata provided by the provider. */ + private Map<String,String> metaEntries = new HashMap<>(); + + PropertyValue(PropertyValueBuilder builder){ + this.key = Objects.requireNonNull(builder.key); + this.value = builder.value; + this.source = Objects.requireNonNull(builder.source); + if(builder.metaEntries !=null) { + this.metaEntries.putAll(builder.metaEntries); + } + } + + /** + * Creates a new instance + * @param key the key, not {@code null}. + * @param value the value, not {@code null}. + * @param source the source, typically the name of the {@link PropertySource} providing + * the value, not {@code null}. + */ + private PropertyValue(String key, String value, String source){ + this.key = Objects.requireNonNull(key, "Key is required."); + this.value = Objects.requireNonNull(value, "Value is required."); + this.source = Objects.requireNonNull(source, "Source is required."); + } + + /** + * The requested key. + * @return the, key never {@code null}. + */ + public String getKey() { + return key; + } + + /** + * The source. + * @return the source, which provided the value, not {@code null}. + * @see PropertySource#getName() . + */ + public String getSource() { + return this.source; + } + + + /** + * The value. + * @return the value, in case a value is null it is valid to return {#code null} as result for + * {@link PropertySource#get(String)}. + */ + public String getValue() { + return this.value; + } + + /** + * Creates a full configuration map for this key, value pair and all its meta context data. This map + * is also used for subsequent processing, like value filtering. + * @return the property value entry map. + */ + public Map<String, String> getMetaEntries() { + return Collections.unmodifiableMap(metaEntries); + } + + /** + * Creates a new builder instance. + * @param key the key, not {@code null}. + * @param source the source, typically the name of the {@link PropertySource} + * providing the value, not {@code null}. + * @return a new builder instance. + */ + public static PropertyValueBuilder builder(String key, String source){ + Objects.requireNonNull(key, "Key must be given."); + Objects.requireNonNull(source, "Source must be given"); + + return new PropertyValueBuilder(key, source); + } + + /** + * Creates a new builder instance. + * @param key the key, not {@code null}. + * @param value the property value, not {@code null}. + * @param source the source, typically the name of the {@link PropertySource} + * providing the value, not {@code null}. + * @return a new builder instance. + */ + public static PropertyValueBuilder builder(String key, String value, String source) { + Objects.requireNonNull(key, "Key must be given."); + Objects.requireNonNull(value, "Value must be given"); + Objects.requireNonNull(source, "Source must be given."); + + return new PropertyValueBuilder(key, value, source); + } + + + /** + * Creates a new PropertyValue without any metadata.. + * @param key the key, not {@code null}. + * @param value the value. + * @param source the source, typically the name of the {@link PropertySource} + * providing the value, not {@code null}. + * @return a new property value instance, or {@code null}, + * if the value passed is {@code null}.. + */ + public static PropertyValue of(String key, String value, String source) { + if (value==null) { + return null; + } + return new PropertyValue(key, value, source); + } + + /** + * Access the given key from this value. Valid keys are the key or any meta-context key. + * @param key the key, not {@code null}. + * @return the value found, or {@code null}. + */ + public String getMetaEntry(String key) { + return this.metaEntries.get(Objects.requireNonNull(key)); + } + + /** + * Creates a new builder instance based on this item. + * @return a new builder, never null. + */ + public PropertyValueBuilder toBuilder() { + return new PropertyValueBuilder(this.getKey(), this.getSource()) + .setValue(this.getValue()) + .setMetaEntries(this.metaEntries); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof PropertyValue)) return false; + PropertyValue that = (PropertyValue) o; + return Objects.equals(getKey(), that.getKey()) && + Objects.equals(getValue(), that.getValue()) && + Objects.equals(getSource(), that.getSource()) && + Objects.equals(getMetaEntries(), that.getMetaEntries()); + } + + @Override + public int hashCode() { + return Objects.hash(getKey(), getValue(), getSource(), + getMetaEntries()); + } + + @Override + public String toString() { + return "PropertyValue{" + + "key='" + key + '\'' + + ", value='" + value + '\'' + + ", source='" + source + '\'' + + (metaEntries.isEmpty()?"":", metaEntries=" + metaEntries) + + '}'; + } + + /** + * Maps a map of {@code Map<String,String>} to a {@code Map<String,PropertyValue>}. + * @param config the String based map, not {@code null}. + * @param source the source name, not {@code null}. + * @return the corresponding value based map. + */ + public static Map<String,PropertyValue> map(Map<String, String> config, String source) { + Map<String,PropertyValue> result = new HashMap<>(config.size()); + for(Map.Entry<String,String> en:config.entrySet()){ + result.put(en.getKey(), PropertyValue.of(en.getKey(), en.getValue(), source)); + } + return result; + } + + /** + * Maps a map of {@code Map<String,String>} to a {@code Map<String,PropertyValue>}. + * + * @param config the String based map, not {@code null}. + * @param source the source name, not {@code null}. + * @param metaData additional metadata, not {@code null}. + * @return the corresponding value based map. + */ + public static Map<String,PropertyValue> map(Map<String, String> config, String source, + Map<String,String> metaData) { + Objects.requireNonNull(config, "Config must be given."); + Objects.requireNonNull(source, "Source must be given."); + Objects.requireNonNull(metaData, "Meta data must be given."); + + Map<String,PropertyValue> result = new HashMap<>(config.size()); + + for(Map.Entry<String,String> en:config.entrySet()){ + PropertyValue value = new PropertyValueBuilder(en.getKey(), source).setValue(en.getValue()) + .addMetaEntries(metaData).build(); + result.put(en.getKey(), value); + } + return result; + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/compat/src/main/java/org/apache/tamaya/spi/PropertyValueBuilder.java ---------------------------------------------------------------------- diff --git a/code/compat/src/main/java/org/apache/tamaya/spi/PropertyValueBuilder.java b/code/compat/src/main/java/org/apache/tamaya/spi/PropertyValueBuilder.java new file mode 100644 index 0000000..af01987 --- /dev/null +++ b/code/compat/src/main/java/org/apache/tamaya/spi/PropertyValueBuilder.java @@ -0,0 +1,196 @@ +/* + * 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.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * Builder to create a {@link PropertyValue} instance. + */ +public class PropertyValueBuilder { + /** The key accessed. */ + String key; + /** The property value. */ + String value; + /** The property vaoue source. */ + String source; + /** additional metadata entries (optional). */ + Map<String,String> metaEntries = new HashMap<>(); + + /** + * Create a new builder instance, for a given set of parameters. + * Before calling build at least a {@link #value} and its {@link #source} + * must be set. + */ + PropertyValueBuilder(String key){ + this.key = Objects.requireNonNull(key); + } + + /** + * Create a new builder instance, for a given set of parameters. + * @param key to access a property value, not {@code null}. + * @param source property source. + */ + PropertyValueBuilder(String key, String source) { + this.key = Objects.requireNonNull(key); + this.source = Objects.requireNonNull(source); + } + + /** + * Create a new builder instance, for a given set of parameters. + * + * @param key to access a property value. + * @param value the value, not {@code null}. If a value is {@code null} + * {@link PropertySource#get(String)} should return {@code null}. + * @param source property source. + */ + PropertyValueBuilder(String key, String value, String source) { + this.key = Objects.requireNonNull(key); + this.value = value; + this.source = Objects.requireNonNull(source); + } + + /** + * Replaces/sets the context data. + * @param metaEntries the context data to be applied, not {@code null}. + * @return the builder for chaining. + */ + public PropertyValueBuilder setMetaEntries(Map<String, String> metaEntries) { + this.metaEntries.clear(); + this.metaEntries.putAll(metaEntries); + return this; + } + + /** + * Add an additional context data information. + * @param key the context data key, not {@code null}. + * @param value the context value, not {@code null} (will be converted to String). + * @return the builder for chaining. + */ + public PropertyValueBuilder addMetaEntry(String key, Object value) { + Objects.requireNonNull(key, "Meta key must be given."); + Objects.requireNonNull(value, "Meta value must be given."); + + this.metaEntries.put(key, String.valueOf(value)); + return this; + } + + /** + * Adds the context data given. + * @param metaEntries the context data to be applied, not {@code null}. + * @return the builder for chaining. + */ + public PropertyValueBuilder addMetaEntries(Map<String, String> metaEntries) { + this.metaEntries.putAll(metaEntries); + return this; + } + + /** + * Removes a meta entry. + * @param key the entry's key, not {@code null}. + * @return the builder for chaining. + */ + public PropertyValueBuilder removeMetaEntry(String key) { + Objects.requireNonNull(key, "Key must be given."); + + this.metaEntries.remove(key); + return this; + } + + /** + * Get the value's context data. + * @return the context data, not {@code null}. + */ + public Map<String,String> getMetaEntries() { + return Collections.unmodifiableMap(this.metaEntries); + } + + /** + * Changes the entry's key, mapping also corresponding context entries. + * @param key the new key, not {@code null}. + * @return the builder for chaining. + */ + public PropertyValueBuilder mapKey(String key) { + // todo obf if (1==1) throw new RuntimeException("No tests written."); + Map<String,String> newContext = new HashMap<>(); + for(Map.Entry<String,String> en:this.metaEntries.entrySet()){ + if(en.getKey().startsWith("_"+this.key)){ + newContext.put("_"+key+'.'+ en.getKey().substring(this.key.length()+1), en.getValue()); + }else{ + newContext.put(en.getKey(), en.getValue()); + } + } + this.metaEntries = newContext; + this.key = key; + return this; + } + + /** + * Sets a new key. + * @param key the new key, not {@code null}. + * @return the builder for chaining. + */ + public PropertyValueBuilder setKey(String key) { + this.key = Objects.requireNonNull(key); + return this; + } + + /** + * Sets a new value. + * @param value the new value, not {@code null}. + * @return the builder for chaining. + */ + public PropertyValueBuilder setValue(String value) { + this.value = Objects.requireNonNull(value, "Value must be given."); + + return this; + } + + /** + * Sets a new source. + * @param source the new source, not {@code null}. + * @return the builder for chaining. + */ + public PropertyValueBuilder setSource(String source) { + // todo obf if (1==1) throw new RuntimeException("No tests written."); + this.source = Objects.requireNonNull(source); + return this; + } + + /** + * Creates a new immutable {@link PropertyValue}. + * @return a new immutable {@link PropertyValue}, never {@code null}. + */ + public PropertyValue build(){ + return new PropertyValue(this); + } + + @Override + public String toString() { + return "PropertyValueBuilder{" + + "key='" + key + '\'' + + "value='" + value + '\'' + + ", metaEntries=" + metaEntries + + '}'; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/compat/src/main/java/org/apache/tamaya/spi/PropertyValueCombinationPolicy.java ---------------------------------------------------------------------- diff --git a/code/compat/src/main/java/org/apache/tamaya/spi/PropertyValueCombinationPolicy.java b/code/compat/src/main/java/org/apache/tamaya/spi/PropertyValueCombinationPolicy.java new file mode 100644 index 0000000..14640e6 --- /dev/null +++ b/code/compat/src/main/java/org/apache/tamaya/spi/PropertyValueCombinationPolicy.java @@ -0,0 +1,71 @@ +/* + * 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; + + +/** + * Policy that determines how the final value of a configuration entry is evaluated. An instances of this + * interface can be registered to get control how multiple PropertySources are combined. This is useful in cases + * where the default overriding policy as implemented in {@link #DEFAULT_OVERRIDING_POLICY} is not matching + * the need of the current application, e.g. then entries containing multiple values should be combined to new + * values instead of overridden. + */ +public interface PropertyValueCombinationPolicy { + + /** + * Default overriding collector, where each existing entry ({@code current} is overridden by a subsequent non-null + * entry evaluated by {@code propertySource.get(key)}. + */ + PropertyValueCombinationPolicy DEFAULT_OVERRIDING_POLICY = new PropertyValueCombinationPolicy(){ + + @Override + public PropertyValue collect(PropertyValue currentValue, String key, PropertySource propertySource) { + PropertyValue value = propertySource.get(key); + return value!=null?value:currentValue; + } + }; + + /** + * @deprecated Use {@linkplain #DEFAULT_OVERRIDING_POLICY} instead. Will be removed in 1.0. + */ + @Deprecated + PropertyValueCombinationPolicy DEFAULT_OVERRIDING_COLLECTOR = DEFAULT_OVERRIDING_POLICY; + + + /** + * Method that is called for each value evaluated by a PropertySource for the given key. This method is called + * either when a single key is accessed, e.g. by calling {@code org.apache.tamaya.Configuration.getXXX}, but also + * when the full configuration property map is accessed by calling + * {@link org.apache.tamaya.Configuration#getProperties()}. + * + * @param currentValue the current value, including metadata entries. If no such key is present the current value + * is null. + * The collector should either combine the existing value with value from {@code currentValue} + * or replace the value in {@code currentValue} with {@code valueRead}, hereby returning the + * result to be used as new {@code currentValue}. + * @param key The current key to be evaluated. + * @param propertySource The PropertySource that may return an value for the given key. The PropertySource given + * may be evaluated for additional meta-data, how the given values are to be combined. + * Note that the value returned by a PropertySource can be null. In that case + * {@code currentValue} should be returned in almost all cases. + * @return the value to be used for future evaluation, including metadata entries. + */ + PropertyValue collect(PropertyValue currentValue, String key, PropertySource propertySource); + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/compat/src/main/java/org/apache/tamaya/spi/package-info.java ---------------------------------------------------------------------- diff --git a/code/compat/src/main/java/org/apache/tamaya/spi/package-info.java b/code/compat/src/main/java/org/apache/tamaya/spi/package-info.java new file mode 100644 index 0000000..49dbab9 --- /dev/null +++ b/code/compat/src/main/java/org/apache/tamaya/spi/package-info.java @@ -0,0 +1,24 @@ +/* + * 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 package contains the Apache Tamaya SPI artifacts. + */ [email protected]("0.4") +package org.apache.tamaya.spi; \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/compat/src/main/java/org/apache/tamaya/spisupport/ConfigValueEvaluator.java ---------------------------------------------------------------------- diff --git a/code/compat/src/main/java/org/apache/tamaya/spisupport/ConfigValueEvaluator.java b/code/compat/src/main/java/org/apache/tamaya/spisupport/ConfigValueEvaluator.java new file mode 100644 index 0000000..92fd614 --- /dev/null +++ b/code/compat/src/main/java/org/apache/tamaya/spisupport/ConfigValueEvaluator.java @@ -0,0 +1,48 @@ +/* + * 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.PropertyValue; + +import java.util.Map; + + +/** + * Component SPI which encapsulates the evaluation of a single or full <b>raw</b>value + * for a {@link ConfigurationContext}. + */ +public interface ConfigValueEvaluator { + + /** + * Evaluates single value using a {@link ConfigurationContext}. + * @param key the config key, not null. + * @param context the context, not null. + * @return the value, or null. + */ + PropertyValue evaluteRawValue(String key, ConfigurationContext context); + + /** + * Evaluates all property values from a {@link ConfigurationContext}. + * @param context the context, not null. + * @return the value, or null. + */ + Map<String, PropertyValue> evaluateRawValues(ConfigurationContext context); + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/compat/src/main/java/org/apache/tamaya/spisupport/DefaultConfigValueEvaluator.java ---------------------------------------------------------------------- diff --git a/code/compat/src/main/java/org/apache/tamaya/spisupport/DefaultConfigValueEvaluator.java b/code/compat/src/main/java/org/apache/tamaya/spisupport/DefaultConfigValueEvaluator.java new file mode 100644 index 0000000..d50ed7d --- /dev/null +++ b/code/compat/src/main/java/org/apache/tamaya/spisupport/DefaultConfigValueEvaluator.java @@ -0,0 +1,70 @@ +/* + * 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.PropertyFilter; +import org.apache.tamaya.spi.PropertySource; +import org.apache.tamaya.spi.PropertyValue; + +import java.util.HashMap; +import java.util.Map; + + +/** + * Implementation of the Configuration API. This class uses the current {@link ConfigurationContext} to evaluate the + * chain of {@link PropertySource} and {@link PropertyFilter} + * instance to evaluate the current Configuration. + */ +public class DefaultConfigValueEvaluator implements ConfigValueEvaluator{ + + @Override + public PropertyValue evaluteRawValue(String key, ConfigurationContext context) { + PropertyValue unfilteredValue = null; + for (PropertySource propertySource : context.getPropertySources()) { + unfilteredValue = context.getPropertyValueCombinationPolicy(). + collect(unfilteredValue, key, propertySource); + } + if(unfilteredValue==null || unfilteredValue.getValue()==null){ + return null; + } + return unfilteredValue; + } + + @Override + public Map<String, PropertyValue> evaluateRawValues(ConfigurationContext context) { + Map<String, PropertyValue> result = new HashMap<>(); + for (PropertySource propertySource : context.getPropertySources()) { + for (Map.Entry<String,PropertyValue> propEntry: propertySource.getProperties().entrySet()) { + PropertyValue unfilteredValue = result.get(propEntry.getKey()); + unfilteredValue = context.getPropertyValueCombinationPolicy(). + collect(unfilteredValue, propEntry.getKey(), propertySource); + if(unfilteredValue!=null){ + result.put(unfilteredValue.getKey(), unfilteredValue); + } + } + } + return result; + } + + @Override + public String toString() { + return "DefaultConfigEvaluator{}"; + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/compat/src/main/java/org/apache/tamaya/spisupport/DefaultConfiguration.java ---------------------------------------------------------------------- diff --git a/code/compat/src/main/java/org/apache/tamaya/spisupport/DefaultConfiguration.java b/code/compat/src/main/java/org/apache/tamaya/spisupport/DefaultConfiguration.java new file mode 100644 index 0000000..bdbd09c --- /dev/null +++ b/code/compat/src/main/java/org/apache/tamaya/spisupport/DefaultConfiguration.java @@ -0,0 +1,281 @@ +/* + * 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.ConfigOperator; +import org.apache.tamaya.ConfigQuery; +import org.apache.tamaya.Configuration; +import org.apache.tamaya.spi.TypeLiteral; +import org.apache.tamaya.spi.ConfigurationContext; +import org.apache.tamaya.spi.ConversionContext; +import org.apache.tamaya.spi.PropertyConverter; +import org.apache.tamaya.spi.PropertySource; +import org.apache.tamaya.spi.PropertyValue; +import org.apache.tamaya.spi.PropertyValueCombinationPolicy; +import org.apache.tamaya.spi.ServiceContextManager; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +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 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 current {@link ConfigurationContext} of the current instance. + */ + private final ConfigurationContext configurationContext; + + /** + * EvaluationStrategy + */ + private ConfigValueEvaluator configEvaluator = loadConfigValueEvaluator(); + + private ConfigValueEvaluator loadConfigValueEvaluator() { + ConfigValueEvaluator eval = null; + try{ + eval = ServiceContextManager.getServiceContext() + .getService(ConfigValueEvaluator.class); + }catch(Exception e){ + LOG.log(Level.WARNING, "Failed to load ConfigValueEvaluator from ServiceContext, using default.", e); + } + if(eval==null){ + eval = new DefaultConfigValueEvaluator(); + } + return eval; + } + + + /** + * Constructor. + * @param configurationContext The configuration Context to be used. + */ + public DefaultConfiguration(ConfigurationContext configurationContext){ + this.configurationContext = Objects.requireNonNull(configurationContext); + } + + /** + * Get a given value, filtered with the context's filters as needed. + * @param key the property's key, not null. + * @return the filtered value, or null. + */ + @Override + public String get(String key) { + Objects.requireNonNull(key, "Key must not be null."); + + PropertyValue value = configEvaluator.evaluteRawValue(key, configurationContext); + if(value==null || value.getValue()==null){ + return null; + } + value = PropertyFiltering.applyFilter(value, configurationContext); + if(value!=null){ + return value.getValue(); + } + return null; + } + + /** + * Evaluates the raw value using the context's PropertyValueCombinationPolicy. + * @param key the key, not null. + * @return the value, before filtering is applied. + */ + protected PropertyValue evaluteRawValue(String key) { + List<PropertySource> propertySources = configurationContext.getPropertySources(); + PropertyValue filteredValue = null; + PropertyValueCombinationPolicy combinationPolicy = this.configurationContext + .getPropertyValueCombinationPolicy(); + for (PropertySource propertySource : propertySources) { + filteredValue = combinationPolicy.collect(filteredValue, key, propertySource); + } + return filteredValue; + } + + + @Override + public String getOrDefault(String key, String defaultValue) { + Objects.requireNonNull(key, "Key must not be null."); + + String val = get(key); + if(val==null){ + return defaultValue; + } + return val; + } + + @Override + public <T> T getOrDefault(String key, Class<T> type, T defaultValue) { + Objects.requireNonNull(key, "Key must not be null."); + Objects.requireNonNull(type, "Target type must not be null"); + + T val = get(key, type); + if(val==null){ + return defaultValue; + } + return val; + } + + /** + * Get the current properties, composed by the loaded {@link PropertySource} and filtered + * by registered {@link org.apache.tamaya.spi.PropertyFilter}. + * + * @return the final properties. + */ + @Override + public Map<String, String> getProperties() { + Map<String, PropertyValue> filtered = PropertyFiltering.applyFilters( + configEvaluator.evaluateRawValues(configurationContext), + configurationContext); + Map<String,String> result = new HashMap<>(); + for(PropertyValue val:filtered.values()){ + if(val.getValue()!=null) { + result.put(val.getKey(), val.getValue()); + // TODO: Discuss metadata handling... + result.putAll(val.getMetaEntries()); + } + } + return result; + } + + + /** + * Accesses the current String value for the given key and tries to convert it + * using the {@link PropertyConverter} instances provided by the current + * {@link ConfigurationContext}. + * + * @param key the property's absolute, or relative path, e.g. @code + * a/b/c/d.myProperty}, never {@code null}. + * @param type The target type required, not {@code null}. + * @param <T> the value type + * @return the converted value, never {@code null}. + */ + @Override + public <T> T get(String key, Class<T> type) { + return get(key, TypeLiteral.of(type)); + } + + /** + * Accesses the current String value for the given key and tries to convert it + * using the {@link PropertyConverter} instances provided by the current + * {@link 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> T get(String key, TypeLiteral<T> type) { + Objects.requireNonNull(key, "Key must not be null."); + Objects.requireNonNull(type, "Target type must not be null"); + + return convertValue(key, get(key), type); + } + + @SuppressWarnings("unchecked") + protected <T> T convertValue(String key, String value, TypeLiteral<T> type) { + if (value != null) { + List<PropertyConverter<T>> converters = configurationContext.getPropertyConverters(type); + ConversionContext context = new ConversionContext.Builder(this, this.configurationContext, key, type) + .build(); + for (PropertyConverter<T> converter : converters) { + try { + T t = converter.convert(value, context); + if (t != null) { + return t; + } + } catch (Exception e) { + LOG.log(Level.FINEST, "PropertyConverter: " + converter + " failed to convert value: " + value, e); + } + } + // if the target type is a String, we can return the value, no conversion required. + if(type.equals(TypeLiteral.of(String.class))){ + return (T)value; + } + // unsupported type, throw an exception + throw new ConfigException("Unparseable config value for type: " + type.getRawType().getName() + ": " + key + + ", supported formats: " + context.getSupportedFormats()); + } + return null; + } + + @Override + public <T> T getOrDefault(String key, TypeLiteral<T> type, T defaultValue) { + Objects.requireNonNull(key); + Objects.requireNonNull(type); + + T val = get(key, type); + if(val==null){ + return defaultValue; + } + return val; + } + + @Override + public Configuration with(ConfigOperator operator) { + return operator.operate(this); + } + + @Override + public <T> T query(ConfigQuery<T> query) { + return query.query(this); + } + + @Override + public ConfigurationContext getContext() { + return this.configurationContext; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + DefaultConfiguration that = (DefaultConfiguration) o; + + if (!configurationContext.equals(that.configurationContext)) return false; + return configEvaluator.getClass().equals(that.configEvaluator.getClass()); + } + + @Override + public int hashCode() { + int result = configurationContext.hashCode(); + result = 31 * result + configEvaluator.getClass().hashCode(); + return result; + } + + @Override + public String toString() { + return "Configuration{\n " + + configurationContext + + '}'; + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/compat/src/main/java/org/apache/tamaya/spisupport/DefaultConfigurationBuilder.java ---------------------------------------------------------------------- diff --git a/code/compat/src/main/java/org/apache/tamaya/spisupport/DefaultConfigurationBuilder.java b/code/compat/src/main/java/org/apache/tamaya/spisupport/DefaultConfigurationBuilder.java new file mode 100644 index 0000000..1b3f1ce --- /dev/null +++ b/code/compat/src/main/java/org/apache/tamaya/spisupport/DefaultConfigurationBuilder.java @@ -0,0 +1,239 @@ +/* + * 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.Configuration; +import org.apache.tamaya.spi.*; +import org.apache.tamaya.spi.ConfigurationBuilder; + +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + + +/** + * Default implementation of {@link ConfigurationBuilder}. + */ +public class DefaultConfigurationBuilder implements ConfigurationBuilder { + + protected final DefaultConfigurationContextBuilder contextBuilder; + + /** + * Creates a new builder instance. + */ + public DefaultConfigurationBuilder() { + this.contextBuilder = new DefaultConfigurationContextBuilder(); + } + + /** + * Creates a new builder instance. + */ + public DefaultConfigurationBuilder(ConfigurationContext context) { + this.contextBuilder = new DefaultConfigurationContextBuilder(context); + } + + /** + * Creates a new builder instance initializing it with the given context. + * @param configuration the configuration to be used, not null. + */ + public DefaultConfigurationBuilder(Configuration configuration) { + this.contextBuilder = new DefaultConfigurationContextBuilder(configuration.getContext()); + } + + /** + * Allows to set configuration context during unit tests. + */ + public ConfigurationBuilder setConfiguration(Configuration configuration) { + this.contextBuilder.setContext(configuration.getContext()); + return this; + } + + + @Override + public ConfigurationBuilder setContext(ConfigurationContext context) { + this.contextBuilder.setContext(context); + return this; + } + + @Override + public ConfigurationBuilder addPropertySources(PropertySource... sources){ + this.contextBuilder.addPropertySources(sources); + return this; + } + + @Override + public ConfigurationBuilder addPropertySources(Collection<PropertySource> sources){ + this.contextBuilder.addPropertySources(sources); + return this; + } + + public ConfigurationBuilder addDefaultPropertyFilters() { + this.contextBuilder.addDefaultPropertyFilters(); + return this; + } + + public ConfigurationBuilder addDefaultPropertySources() { + this.contextBuilder.addDefaultPropertySources(); + return this; + } + + public ConfigurationBuilder addDefaultPropertyConverters() { + this.contextBuilder.addDefaultPropertyConverters(); + return this; + } + + @Override + public ConfigurationBuilder removePropertySources(PropertySource... propertySources) { + this.contextBuilder.removePropertySources(propertySources); + return this; + } + + @Override + public ConfigurationBuilder removePropertySources(Collection<PropertySource> propertySources) { + this.contextBuilder.removePropertySources(propertySources); + return this; + } + + @Override + public List<PropertySource> getPropertySources() { + return this.contextBuilder.getPropertySources(); + } + + @Override + public ConfigurationBuilder increasePriority(PropertySource propertySource) { + this.contextBuilder.increasePriority(propertySource); + return this; + } + + @Override + public ConfigurationBuilder decreasePriority(PropertySource propertySource) { + this.contextBuilder.decreasePriority(propertySource); + return this; + } + + @Override + public ConfigurationBuilder highestPriority(PropertySource propertySource) { + this.contextBuilder.highestPriority(propertySource); + return this; + } + + @Override + public ConfigurationBuilder lowestPriority(PropertySource propertySource) { + this.contextBuilder.lowestPriority(propertySource); + return this; + } + + @Override + public ConfigurationBuilder addPropertyFilters(Collection<PropertyFilter> filters){ + this.contextBuilder.addPropertyFilters(filters); + return this; + } + + @Override + public ConfigurationBuilder addPropertyFilters(PropertyFilter... filters){ + this.contextBuilder.addPropertyFilters(filters); + return this; + } + + @Override + public ConfigurationBuilder removePropertyFilters(PropertyFilter... filters) { + this.contextBuilder.removePropertyFilters(filters); + return this; + } + + @Override + public ConfigurationBuilder removePropertyFilters(Collection<PropertyFilter> filters) { + this.contextBuilder.removePropertyFilters(filters); + return this; + } + + + @Override + public <T> ConfigurationBuilder removePropertyConverters(TypeLiteral<T> typeToConvert, + PropertyConverter<T>... converters) { + this.contextBuilder.removePropertyConverters(typeToConvert, converters); + return this; + } + + @Override + public <T> ConfigurationBuilder removePropertyConverters(TypeLiteral<T> typeToConvert, + Collection<PropertyConverter<T>> converters) { + this.contextBuilder.removePropertyConverters(typeToConvert, converters); + return this; + } + + @Override + public ConfigurationBuilder removePropertyConverters(TypeLiteral<?> typeToConvert) { + this.contextBuilder.removePropertyConverters(typeToConvert); + return this; + } + + + @Override + public ConfigurationBuilder setPropertyValueCombinationPolicy(PropertyValueCombinationPolicy combinationPolicy){ + this.contextBuilder.setPropertyValueCombinationPolicy(combinationPolicy); + return this; + } + + @Override + public <T> ConfigurationBuilder addPropertyConverters(TypeLiteral<T> type, PropertyConverter<T>... propertyConverters){ + this.contextBuilder.addPropertyConverters(type, propertyConverters); + return this; + } + + @Override + public <T> ConfigurationBuilder addPropertyConverters(TypeLiteral<T> type, Collection<PropertyConverter<T>> propertyConverters){ + this.contextBuilder.addPropertyConverters(type, propertyConverters); + return this; + } + + /** + * Builds a new configuration based on the configuration of this builder instance. + * + * @return a new {@link org.apache.tamaya.Configuration configuration instance}, + * never {@code null}. + */ + @Override + public Configuration build() { + return new DefaultConfiguration(this.contextBuilder.build()); + } + + @Override + public ConfigurationBuilder sortPropertyFilter(Comparator<PropertyFilter> comparator) { + this.contextBuilder.sortPropertyFilter(comparator); + return this; + } + + @Override + public ConfigurationBuilder sortPropertySources(Comparator<PropertySource> comparator) { + this.contextBuilder.sortPropertySources(comparator); + return this; + } + + @Override + public List<PropertyFilter> getPropertyFilters() { + return this.contextBuilder.getPropertyFilters(); + } + + @Override + public Map<TypeLiteral<?>, Collection<PropertyConverter<?>>> getPropertyConverter() { + return this.contextBuilder.getPropertyConverter(); + } +}
