blob: 083f494b49342d10fc603d0e9d5bf5e6c3a420a9 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.tamaya;
import org.apache.tamaya.spi.ConfigurationBuilder;
import org.apache.tamaya.spi.ConfigurationContext;
import org.apache.tamaya.spi.ConfigurationProviderSpi;
import org.apache.tamaya.spi.ServiceContextManager;
import java.util.*;
import java.util.function.Function;
import java.util.function.UnaryOperator;
/**
* <p>A configuration models an aggregated setPropertyValue of current properties, identified by
* a unique key, but adds higher level access functions to
* a {@link org.apache.tamaya.spi.PropertySource}. Hereby in most
* cases a configuration is a wrapper around a composite
* {@link org.apache.tamaya.spi.PropertySource} instance, which may combine
* multiple child configurations in a well defined tree like structure,
* where nodes define logically the rules' current priority, filtering,
* combination and overriding.
* </p>
* <h3>Implementation Requirements</h3>
* Implementations of this interface must be
* <ul>
* <li>Thread safe</li>
* <li>Immutable</li>
* </ul>
*
* <p>It is not recommended that implementations also are serializable, since the any configuration can be <i>frozen</i>
* by reading out its complete configuration mapProperties into a serializable and remotable structure. This helps significantly
* by simplifying the development of this interface, e.g. for being backed up by systems and stores that are not part of
* this library at all.</p>
*/
public interface Configuration {
/**
* Access a property.
*
* @param key the property's key, not {@code null}.
* @return the property's value.
*/
default String get(String key){
return get(key, TypeLiteral.of(String.class));
}
/**
* Access a property.
*
* @param keys the property's keys, in order of evaluation, not {@code null}.
* @return the property's value.
*/
default String get(Iterable<String> keys){
return get(keys, TypeLiteral.of(String.class));
}
/**
* Access a property.
*
* @param key the property's key, not {@code null}.
* @param defaultValue value to be returned, if no value is present, not {@code null}
* @return the property's keys.
*/
default String getOrDefault(String key, String defaultValue){
return getOrDefault(key, TypeLiteral.of(String.class), defaultValue);
}
/**
* Access a property.
*
* @param keys the property's keys, in order of evaluation, not {@code null}.
* @param defaultValue value to be returned, if no value is present, not {@code null}
* @return the property's keys.
*/
default String getOrDefault(Iterable<String> keys, String defaultValue){
return getOrDefault(keys, TypeLiteral.of(String.class), defaultValue);
}
/**
* Access a String property, using an an {@link Optional} instance.
*
* @param key the property's key, not {@code null}.
* @return the property's keys.
*/
default Optional<String> getOptional(String key){
return Optional.ofNullable(getOrDefault(key, String.class, null));
}
/**
* Access a String property, using an an {@link Optional} instance.
*
* @param keys the property's keys, in evaluation order, not {@code null}.
* @return the property's keys.
*/
default Optional<String> getOptional(Iterable<String> keys){
return Optional.ofNullable(getOrDefault(keys, String.class, null));
}
/**
* Access a property, using an an {@link Optional} instance.
*
* @param key the property's key, not {@code null}.
* @param type the target type, not null.
* @param <T> the type of the class modeled by the type parameter
* @return the property's keys.
*/
default <T> Optional<T> getOptional(String key, Class<T> type){
return Optional.ofNullable(getOrDefault(key, TypeLiteral.of(type), null));
}
/**
* Access a property, using an an {@link Optional} instance.
*
* @param keys the property's keys, in evaluation order, not {@code null}.
* @param type the target type, not null.
* @param <T> the type of the class modeled by the type parameter
* @return the property's keys.
*/
default <T> Optional<T> getOptional(Iterable<String> keys, Class<T> type){
return Optional.ofNullable(getOrDefault(keys, TypeLiteral.of(type), null));
}
/**
* Access a property, using an an {@link Optional} instance.
*
* @param key the property's key, not {@code null}.
* @param type the target type, not null.
* @param <T> the type of the class modeled by the type parameter
* @return the property's keys.
*/
default <T> Optional<T> getOptional(String key, TypeLiteral<T> type){
return Optional.ofNullable(getOrDefault(key, type, null));
}
/**
* Access a property, using an an {@link Optional} instance.
*
* @param keys the property's keys, in evaluation order, not {@code null}.
* @param type the target type, not null.
* @param <T> the type of the class modeled by the type parameter
* @return the property's keys.
*/
default <T> Optional<T> getOptional(Iterable<String> keys, TypeLiteral<T> type){
return Optional.ofNullable(getOrDefault(keys, type, null));
}
/**
* Gets the property keys as type T. This will implicitly require a corresponding {@link
* org.apache.tamaya.spi.PropertyConverter} to be available that is capable of providing type T
* fromMap for the given String keys.
*
* @param <T> the type of the class modeled by the type parameter
* @param key the property's absolute, or relative path, e.g. {@code
* a/b/c/d.myProperty}, not {@code null}.
* @param type The target type required, not {@code null}.
* @param defaultValue value to be used, if no value is present, not {@code null}
* @return the property value, never {@code null}.
* @throws ConfigException if the keys could not be converted to the required target type.
*/
default <T> T getOrDefault(String key, Class<T> type, T defaultValue){
return getOrDefault(key, TypeLiteral.of(type), defaultValue);
}
/**
* Gets the property keys as type T. This will implicitly require a corresponding {@link
* org.apache.tamaya.spi.PropertyConverter} to be available that is capable of providing type T
* fromMap for the given String keys.
*
* @param <T> the type of the class modeled by the type parameter
* @param keys the property's keys, in evaluation order, not {@code null}.
* @param type The target type required, not {@code null}.
* @param defaultValue value to be used, if no value is present, not {@code null}
* @return the property value, never {@code null}.
* @throws ConfigException if the keys could not be converted to the required target type.
*/
default <T> T getOrDefault(Iterable<String> keys, Class<T> type, T defaultValue){
return getOrDefault(keys, TypeLiteral.of(type), defaultValue);
}
/**
* Gets the property keys as type T. This will implicitly require a corresponding {@link
* org.apache.tamaya.spi.PropertyConverter} to be available that is capable of providing type T
* fromMap for the given String keys.
*
* @param <T> the type of the class modeled by the type parameter
* @param key the property's absolute, or relative path, e.g. @code
* a/b/c/d.myProperty}.
* @param type The target type required, not {@code null}.
* @return the property value, never {@code null}.
* @throws ConfigException if the keys could not be converted to the required target type.
*/
default <T> T get(String key, Class<T> type){
return get(key, TypeLiteral.of(type));
}
/**
* Gets the property keys as type T. This will implicitly require a corresponding {@link
* org.apache.tamaya.spi.PropertyConverter} to be available that is capable of providing type T
* fromMap for the given String keys.
*
* @param <T> the type of the class modeled by the type parameter
* @param keys the property's keys, in evaluation order, not {@code null}.
* @param type The target type required, not {@code null}.
* @return the property value, never {@code null}.
* @throws ConfigException if the keys could not be converted to the required target type.
*/
default <T> T get(Iterable<String> keys, Class<T> type){
return get(keys, TypeLiteral.of(type));
}
/**
* Get the property keys as type T. This will implicitly require a corresponding {@link
* org.apache.tamaya.spi.PropertyConverter} to be available that is capable of providing type T
* literals for the given key.
*
* @param <T> the type of the type literal
* @param key the property's absolute, or relative path, e.g. @code
* a/b/c/d.myProperty}, not {@code null}.
* @param type The target type required, not {@code null}.
* @return the property value, never {@code null}.
* @throws ConfigException if the keys could not be converted to the required target type.
*/
<T> T get(String key, TypeLiteral<T> type);
/**
* Get the property keys as type T. This will implicitly require a corresponding {@link
* org.apache.tamaya.spi.PropertyConverter} to be available that is capable of providing type T
* literals for the given key.
*
* @param <T> the type of the type literal
* @param keys the property's keys, in evaluation order, not {@code null}.
* @param type The target type required, not {@code null}.
* @return the property value, never {@code null}.
* @throws ConfigException if the keys could not be converted to the required target type.
*/
default <T> T get(Iterable<String> keys, TypeLiteral<T> type){
for(String k:keys){
T t = getOrDefault(k, type, null);
if(t!=null){
return t;
}
}
return null;
}
/**
* Get the property keys as type T. This will implicitly require a corresponding {@link
* org.apache.tamaya.spi.PropertyConverter} to be available that is capable of providing type T
* literals for the given key.
*
* @param <T> the type of the type literal
* @param key the property's absolute, or relative path, e.g.
* {@code a/b/c/d.myProperty}, not {@code null}.
* @param type The target type required, not {@code null}.
* @param defaultValue default value to be used, if no value is present.
* @return the property value, never null.
* @throws ConfigException if the keys could not be converted to the required target type.
*/
<T> T getOrDefault(String key, TypeLiteral<T> type, T defaultValue);
/**
* Get the property keys as type T. This will implicitly require a corresponding {@link
* org.apache.tamaya.spi.PropertyConverter} to be available that is capable of providing type T
* literals for the given key.
*
* @param <T> the type of the type literal
* @param keys the property's keys, in evaluation order, not {@code null}.
* @param type The target type required, not {@code null}.
* @param defaultValue default value to be used, if no value is present.
* @return the property value, never null.
* @throws ConfigException if the keys could not be converted to the required target type.
*/
default <T> T getOrDefault(Iterable<String> keys, TypeLiteral<T> type, T defaultValue){
for(String k:keys){
T t = getOrDefault(k, type, null);
if(t!=null){
return t;
}
}
return defaultValue;
}
/**
* Access all currently known configuration properties as a full {@code Map<String,String>}.
* Be aware that entries from non scannable parts of the registered {@link org.apache.tamaya.spi.PropertySource}
* instances may not be contained in the result, but nevertheless be accessible by calling one of the
* {@code current(...)} methods.
* @return all currently known configuration properties.
*/
Map<String,String> getProperties();
/**
* Extension point for adjusting configuration.
*
* @param operator A configuration operator, e.g. a filter, or an adjuster
* combining configurations, never {@code null}.
* @return the new adjusted configuration returned by the {@code operator}, never {@code null}.
* @deprecated use {@link #map(UnaryOperator)}
*/
@Deprecated
default Configuration with(ConfigOperator operator){
Objects.requireNonNull(operator, "Operator must be given.");
return operator.operate(this);
}
/**
* Extension point for adjusting configuration.
*
* @param operator A configuration operator, e.g. a filter, or an adjuster
* combining configurations, never {@code null}.
* @return the new adjusted configuration returned by the {@code operator}, never {@code null}.
*/
default Configuration map(UnaryOperator<Configuration> operator){
Objects.requireNonNull(operator, "Operator must be given.");
return operator.apply(this);
}
/**
* Query a configuration.
*
* @param <T> the type of the configuration.
* @param query the query, not {@code null}.
* @return the result returned by the {@code query}.
* @deprecated Use {@link #adapt(Function)}
*/
@Deprecated
default <T> T query(ConfigQuery<T> query){
Objects.requireNonNull(query, "Query must be given.");
return query.query(this);
}
/**
* Query a configuration.
*
* @param <T> the type of the configuration.
* @param query the query, not {@code null}.
* @return the result returned by the {@code query}.
*/
default <T> T adapt(Function<Configuration, T> query){
Objects.requireNonNull(query, "Adapter must be given.");
return query.apply(this);
}
/**
* Access a configuration's context.
* @return the configuration context, never null.
*/
ConfigurationContext getContext();
/**
* Create a snapshot, which contains the given keys.
*
* @param keys the keys, not null. If empty a full snapshot with all known keys is returned.
* @return a corresponding snapshot instance.
*/
ConfigurationSnapshot getSnapshot(Iterable<String> keys);
/**
* Create a snapshot, which contains all known keys.
*
* @param keys the target key. If empty a full snapshot with all known keys is returned.
* @return a corresponding snapshot instance.
*/
default ConfigurationSnapshot getSnapshot(String... keys){
return getSnapshot(Arrays.asList(keys));
}
/**
* Create a new builder using this instance as its base.
* @return a new builder, never null.
*/
default ConfigurationBuilder toBuilder() {
return ServiceContextManager.getServiceContext(Configuration.class.getClassLoader())
.getService(ConfigurationProviderSpi.class).getConfigurationBuilder().setConfiguration(this);
}
/**
* This method allows replacement of the current default {@link org.apache.tamaya.Configuration} with a new
* instance. It is the responsibility of the ConfigurationProvider to trigger
* corresponding update events for the current {@link org.apache.tamaya.Configuration}, so observing
* listeners can do whatever is appropriate to react to any given configuration change.
*
* @param config the new Configuration to be applied, not {@code null}
* @throws java.lang.UnsupportedOperationException if the current provider is read-only and
* does not support
* applying a new Configuration.
*/
static void setCurrent(Configuration config) {
ServiceContextManager.getServiceContext(Configuration.class.getClassLoader())
.getService(ConfigurationProviderSpi.class).setConfiguration(config, Thread.currentThread().getContextClassLoader());
}
/**
* This method allows replacement of the current default {@link org.apache.tamaya.Configuration} with a new
* instance. It is the responsibility of the ConfigurationProvider to trigger
* corresponding update events for the current {@link org.apache.tamaya.Configuration}, so observing
* listeners can do whatever is appropriate to react to any given configuration change.
*
* @param config the new Configuration to be applied, not {@code null}
* @param classLoader the target classloader, not null.
* @throws java.lang.UnsupportedOperationException if the current provider is read-only and
* does not support
* applying a new Configuration.
*/
static void setCurrent(Configuration config, ClassLoader classLoader) {
ServiceContextManager.getServiceContext(Configuration.class.getClassLoader())
.getService(ConfigurationProviderSpi.class).setConfiguration(config, classLoader);
}
/**
* Access the configuration instance for the current thread's context classloader.
* @return the configuration instance, never null.
*/
static Configuration current(){
return ServiceContextManager.getServiceContext(Configuration.class.getClassLoader())
.getService(ConfigurationProviderSpi.class).getConfiguration(Thread.currentThread().getContextClassLoader());
}
/**
* Accesses the configuration for a given classloader.
* @param classloader the classloader, not null.
* @return the configuration instance, never null.
*/
static Configuration current(ClassLoader classloader){
return ServiceContextManager.getServiceContext(Configuration.class.getClassLoader())
.getService(ConfigurationProviderSpi.class).getConfiguration(classloader);
}
/**
* Releases the configuration associated with the given classloader.
* @param classloader the classloader, not null.
* @return the released configuration, or null.
*/
static Configuration releaseConfiguration(ClassLoader classloader) {
return ServiceContextManager.getServiceContext(Configuration.class.getClassLoader())
.getService(ConfigurationProviderSpi.class).releaseConfiguration(classloader);
}
/**
* Access a new configuration builder initialized with the current thread's context classloader.
* @return the builder, never null.
*/
static ConfigurationBuilder createConfigurationBuilder(){
return ServiceContextManager.getServiceContext(ServiceContextManager.getDefaultClassLoader())
.getService(ConfigurationProviderSpi.class).getConfigurationBuilder();
}
/**
* Immutable and reusable, thread-safe implementation of an empty propertySource.
*/
Configuration EMPTY = new Configuration() {
@Override
public String get(String key) {
return null;
}
@Override
public String getOrDefault(String key, String defaultValue) {
return defaultValue;
}
@Override
public <T> T getOrDefault(String key, Class<T> type, T defaultValue) {
return defaultValue;
}
@Override
public <T> T get(String key, Class<T> type) {
return null;
}
@Override
public <T> T get(String key, TypeLiteral<T> type) {
return null;
}
@Override
public <T> T getOrDefault(String key, TypeLiteral<T> type, T defaultValue) {
return defaultValue;
}
@Override
public Map<String, String> getProperties() {
return Collections.emptyMap();
}
@Override
public ConfigurationContext getContext() {
return ConfigurationContext.EMPTY;
}
@Override
public ConfigurationSnapshot getSnapshot(Iterable<String> keys) {
return ConfigurationSnapshot.EMPTY;
}
@Override
public String toString(){
return "Configuration<EMPTY>";
}
};
}