Added full JSR implementation. Signed-off-by: Anatole Tresch <[email protected]>
Project: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/commit/063f8ada Tree: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/tree/063f8ada Diff: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/diff/063f8ada Branch: refs/heads/configjsr Commit: 063f8adadaaa8ecb650c9f5195ac94ff6d8fae95 Parents: a31af00 Author: Anatole Tresch <[email protected]> Authored: Sun Dec 17 09:00:16 2017 +0100 Committer: Anatole Tresch <[email protected]> Committed: Sun Dec 17 09:00:16 2017 +0100 ---------------------------------------------------------------------- .../org/apache/tamaya/base/DefaultConfig.java | 165 +++++ .../tamaya/base/DefaultConfigBuilder.java | 305 +++++++++ .../tamaya/base/DefaultServiceContext.java | 225 +++++++ .../tamaya/base/PriorityServiceComparator.java | 84 +++ .../org/apache/tamaya/base/ReflectionUtil.java | 42 ++ .../base/configsource/BaseConfigSource.java | 170 +++++ .../configsource/BuildableConfigSource.java | 208 ++++++ .../BuildableConfigSourceProvider.java | 113 ++++ .../base/configsource/CLIConfigSource.java | 130 ++++ .../configsource/ConfigSourceComparator.java | 117 ++++ .../base/configsource/ConfigSourceManager.java | 375 +++++++++++ .../configsource/EnvironmentConfigSource.java | 281 +++++++++ .../JavaConfigurationConfigSource.java | 131 ++++ .../base/configsource/MapConfigSource.java | 98 +++ .../PropertiesResourceConfigSource.java | 109 ++++ .../base/configsource/SimpleConfigSource.java | 282 +++++++++ .../base/configsource/SystemConfigSource.java | 186 ++++++ .../base/configsource/WrappedConfigSource.java | 117 ++++ .../tamaya/base/configsource/package-info.java | 23 + .../tamaya/base/convert/ConversionContext.java | 253 ++++++++ .../tamaya/base/convert/ConverterManager.java | 630 +++++++++++++++++++ .../tamaya/base/convert/EnumConverter.java | 81 +++ .../tamaya/base/convert/package-info.java | 23 + .../tamaya/base/filter/FilterComparator.java | 72 +++ .../tamaya/base/filter/FilterContext.java | 144 +++++ .../tamaya/base/filter/FilterManager.java | 273 ++++++++ .../apache/tamaya/base/filter/package-info.java | 23 + .../org/apache/tamaya/base/package-info.java | 23 + .../org/apache/tamaya/spi/ConfigContext.java | 154 +++++ .../java/org/apache/tamaya/spi/ConfigValue.java | 209 ++++++ .../apache/tamaya/spi/ConfigValueBuilder.java | 179 ++++++ .../org/apache/tamaya/spi/Experimental.java | 32 + .../main/java/org/apache/tamaya/spi/Filter.java | 50 ++ .../org/apache/tamaya/spi/ServiceContext.java | 147 +++++ .../tamaya/spi/ServiceContextManager.java | 118 ++++ .../spi/StandaloneConfigContextBuilder.java | 407 ++++++++++++ .../apache/tamaya/spi/TamayaConfigBuilder.java | 333 ++++++++++ .../java/org/apache/tamaya/spi/TypeLiteral.java | 225 +++++++ .../org/apache/tamaya/spi/package-info.java | 23 + .../tamaya/base/DefaultConfigBuilderTest.java | 198 ++++++ .../apache/tamaya/base/DefaultConfigTest.java | 130 ++++ .../tamaya/base/DefaultServiceContextTest.java | 137 ++++ .../base/PriorityServiceComparatorTest.java | 45 ++ .../tamaya/base/TestConfigurationProvider.java | 82 +++ .../base/configsource/BaseConfigSourceTest.java | 124 ++++ .../BuildableConfigSourceProviderTest.java | 78 +++ .../configsource/BuildableConfigSourceTest.java | 89 +++ .../configsource/CLIPropertySourceTest.java | 59 ++ .../base/configsource/EnumConverterTest.java | 63 ++ .../EnvironmentPropertySourceTest.java | 62 ++ .../JavaConfigurationProviderTest.java | 46 ++ .../configsource/MapPropertySourceTest.java | 76 +++ .../PropertiesFilePropertySourceTest.java | 60 ++ .../configsource/SimplePropertySourceTest.java | 88 +++ .../configsource/SystemPropertySourceTest.java | 92 +++ .../configsource/TestConfigDefaultSource.java | 52 ++ .../java/org/apache/tamaya/base/convert/A.java | 29 + .../java/org/apache/tamaya/base/convert/B.java | 29 + .../java/org/apache/tamaya/base/convert/C.java | 56 ++ .../tamaya/base/convert/CTestConverter.java | 31 + .../base/convert/ConverterManagerTest.java | 184 ++++++ .../tamaya/base/convert/EnumConverterTest.java | 53 ++ .../base/services/DefaultServiceContext.java | 204 ++++++ .../tamaya/filter/FilterComparatorTest.java | 75 +++ .../resources/META-INF/javaconfig.properties | 22 + .../src/test/resources/META-INF/javaconfig.xml | 25 + .../javax.config.spi.ConfigProviderResolver | 18 + .../services/javax.config.spi.Converter | 19 + ...tServiceContextTest$InvalidPriorityInterface | 18 + ...efaultServiceContextTest$MultiImplsInterface | 20 + .../org.apache.tamaya.spi.ServiceContext | 19 + .../src/test/resources/invalid-properties.xml | 25 + .../src/test/resources/non-xml-properties.xml | 18 + .../test/resources/overrideOrdinal.properties | 25 + .../base/src/test/resources/testfile.properties | 22 + .../src/test/resources/valid-properties.xml | 25 + .../examples/customconfigsource/Main.java | 91 +++ .../customconfigsource/SimpleConfigSource.java | 65 ++ .../SimpleConfigSourceProvider.java | 49 ++ .../customconfigsource/package-info.java | 19 + .../examples/custompropertysource/Main.java | 91 --- .../SimpleConfigSource.java | 64 -- .../SimplePropertySourceProvider.java | 49 -- .../custompropertysource/package-info.java | 19 - .../services/javax.config.spi.ConfigSource | 19 + .../javax.config.spi.ConfigSourceProvider | 19 + .../org.apache.tamaya.spi.PropertySource | 19 - ...org.apache.tamaya.spi.PropertySourceProvider | 19 - 88 files changed, 9220 insertions(+), 261 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/base/DefaultConfig.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/base/DefaultConfig.java b/code/base/src/main/java/org/apache/tamaya/base/DefaultConfig.java new file mode 100644 index 0000000..a3762f1 --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/base/DefaultConfig.java @@ -0,0 +1,165 @@ +/* + * 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.base; + + +import org.apache.tamaya.base.configsource.ConfigSourceManager; +import org.apache.tamaya.base.convert.ConverterManager; +import org.apache.tamaya.spi.ConfigValue; +import org.apache.tamaya.spi.Filter; +import org.apache.tamaya.base.filter.FilterManager; +import org.apache.tamaya.spi.*; + +import javax.config.Config; +import javax.config.spi.ConfigSource; +import javax.config.spi.Converter; +import java.lang.reflect.Type; +import java.util.*; +import java.util.logging.Logger; + +/** + * Implementation of the Configuration API. + */ +public class DefaultConfig implements Config, ConfigContextSupplier { + /** + * The logger. + */ + private static final Logger LOG = Logger.getLogger(DefaultConfig.class.getName()); + + /** + * The current {@link ConverterManager} of the current instance. + */ + private final ConverterManager converterManager = new ConverterManager(); + + /** + * The current {@link ConverterManager} of the current instance. + */ + private final FilterManager filterManager = new FilterManager(); + + /** + * The current {@link ConfigSourceManager} of the current instance. + */ + private final ConfigSourceManager configSourceManager = new ConfigSourceManager(); + + + /** + * Constructor. + * @param configContext The configuration Context to be used. + */ + public DefaultConfig(ConfigContext configContext){ + this.configSourceManager.addSources(configContext.getSources()); + this.configSourceManager.setConfigValueCombinationPolicy(configContext.getConfigValueCombinationPolicy()); + configContext.getConverters().forEach((t,c) -> this.converterManager.addConverter(t, Collection.class.cast(c))); + this.filterManager.addFilter(configContext.getFilters()); + } + + /** + * Accesses the current String value for the given key and tries to convert it + * using the {@link javax.config.spi.Converter} instances provided by the current + * {@link ConfigContext}. + * + * @param key the property's absolute, or relative path, e.g. @code + * a/b/c/d.myProperty}, never {@code null}. + * @param targetType The target type required, not {@code null}. + * @param <T> the value type + * @return the converted value, never {@code null}. + */ + @Override + public <T> T getValue(String key, Class<T> targetType) { + Objects.requireNonNull(key, "Key must not be null."); + Objects.requireNonNull(targetType, "Target type must not be null."); + + ConfigValue value = configSourceManager.evaluteRawValue(key); + if(value==null || value.getValue()==null){ + return null; + } + value = filterManager.filterValue(value, this); + if(value!=null){ + return (T)converterManager.convertValue(key, value.getValue(), targetType, this); + } + return null; + } + + + @Override + public <T> Optional<T> getOptionalValue(String key, Class<T> type) { + Objects.requireNonNull(key, "Key must not be null."); + Objects.requireNonNull(type, "Type must not be null."); + T val = getValue(key, type); + return Optional.ofNullable(val); + } + + /** + * Get the current properties, composed by the loaded {@link ConfigSource} and filtered + * by registered {@link Filter}. + * + * @return the final properties. + */ + @Override + public Set<String> getPropertyNames() { + Map<String, ConfigValue> filtered = configSourceManager.evaluateRawValues(); + return filtered.keySet(); + } + + @Override + public Iterable<ConfigSource> getConfigSources() { + return configSourceManager.getSources(); + } + + + public <T> T getOrDefault(String key, Class<T> type, T defaultValue) { + Objects.requireNonNull(key); + Objects.requireNonNull(type); + + Optional<T> val = getOptionalValue(key, type); + return val.orElse(defaultValue); + } + + @Override + public ConfigContext getConfigContext() { + return new ConfigContext() { + @Override + public List<ConfigSource> getSources() { + return configSourceManager.getSources(); + } + + @Override + public List<Filter> getFilters() { + return filterManager.getFilters(); + } + + @Override + public Map<Type, List<Converter>> getConverters() { + return converterManager.getConverters(); + } + }; + } + + + @Override + public String toString() { + StringBuilder b = new StringBuilder("Configuration{\n") + .append(this.configSourceManager).append('\n') + .append(this.filterManager).append('\n') + .append(this.converterManager).append('\n') + .append('}'); + return b.toString(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/base/DefaultConfigBuilder.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/base/DefaultConfigBuilder.java b/code/base/src/main/java/org/apache/tamaya/base/DefaultConfigBuilder.java new file mode 100644 index 0000000..2eb851a --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/base/DefaultConfigBuilder.java @@ -0,0 +1,305 @@ +/* + * 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.base; + +import org.apache.tamaya.base.configsource.*; +import org.apache.tamaya.base.convert.ConverterManager; +import org.apache.tamaya.base.filter.FilterManager; +import org.apache.tamaya.spi.TamayaConfigBuilder; +import org.apache.tamaya.spi.*; +import org.apache.tamaya.spi.Filter; + +import javax.config.Config; +import javax.config.spi.ConfigSource; +import javax.config.spi.Converter; +import java.lang.reflect.Type; +import java.util.*; + +/** + * Default implementation of {@link TamayaConfigBuilder}. + */ +public class DefaultConfigBuilder implements TamayaConfigBuilder { + + protected final ConfigSourceManager configSourceManager = new ConfigSourceManager(); + protected final ConverterManager converterManager = new ConverterManager(); + protected final FilterManager filterManager = new FilterManager(); + + /** + * Creates a new builder instance. + */ + public DefaultConfigBuilder() { + } + + /** + * Creates a new builder instance. + */ + public DefaultConfigBuilder(ConfigContext context) { + this.configSourceManager.addSources(context.getSources()); + this.configSourceManager.setConfigValueCombinationPolicy(context.getConfigValueCombinationPolicy()); + this.filterManager.addFilter(context.getFilters()); + context.getConverters().forEach((t,c) -> converterManager.addConverter(t, Collection.class.cast(c))); + } + + /** + * Creates a new builder instance initializing it with the given context. + * @param contextSupplier the context supplier to be used, not null. + */ + public DefaultConfigBuilder(ConfigContextSupplier contextSupplier) { + this(((ConfigContextSupplier)contextSupplier).getConfigContext()); + } + +// /** +// * Allows to set configuration context during unit tests. +// */ +// @Override +// public TamayaConfigBuilder setConfiguration(Config configuration) { +// this.contextBuilder.withContext(((DefaultConfig)configuration).getConfigContext()); +// return this; +// } + + + @Override + public TamayaConfigBuilder withSources(ConfigSource... sources){ + this.configSourceManager.addSources(sources); + return this; + } + + @Override + public <T> TamayaConfigBuilder withConverter(Class<T> type, Converter<T> converter) { + this.converterManager.addConverter(type, converter); + return this; + } + + @Override + public TamayaConfigBuilder withConverters(Converter<?>... converters) { + this.converterManager.addConverters(converters); + return this; + } + + @Override + public TamayaConfigBuilder withConverters(Collection<Converter<?>> converters) { + this.converterManager.addConverters(Collection.class.cast(converters)); + return this; + } + + @Override + public TamayaConfigBuilder withSources(Collection<ConfigSource> sources){ + this.configSourceManager.addSources(sources); + return this; + } + + @Override + public TamayaConfigBuilder addDiscoveredFilters() { + this.filterManager.addDefaultFilters(); + return this; + } + + @Override + public TamayaConfigBuilder addDefaultSources() { + this.configSourceManager.addSources( + new EnvironmentConfigSource(), + new JavaConfigurationConfigSource(), + new CLIConfigSource(), + new SystemConfigSource()); + return this; + } + + @Override + public TamayaConfigBuilder addDiscoveredSources() { + this.configSourceManager.addDiscoveredSources(); + return this; + } + + @Override + public TamayaConfigBuilder addDiscoveredConverters() { + this.converterManager.addDiscoveredConverters(); + return this; + } + + @Override + public TamayaConfigBuilder forClassLoader(ClassLoader loader) { + this.configSourceManager.setClassloader(loader); + this.filterManager.setClassloader(loader); + this.converterManager.setClassloader(loader); + return this; + } + + @Override + public TamayaConfigBuilder removeSources(ConfigSource... propertySources) { + this.configSourceManager.removeSources(propertySources); + return this; + } + + @Override + public TamayaConfigBuilder removeSources(Collection<ConfigSource> propertySources) { + this.configSourceManager.removeSources(propertySources); + return this; + } + + @Override + public List<ConfigSource> getSources() { + return this.configSourceManager.getSources(); + } + + @Override + public TamayaConfigBuilder increasePriority(ConfigSource propertySource) { + this.configSourceManager.increasePriority(propertySource); + return this; + } + + @Override + public TamayaConfigBuilder decreasePriority(ConfigSource propertySource) { + this.configSourceManager.decreasePriority(propertySource); + return this; + } + + @Override + public TamayaConfigBuilder highestPriority(ConfigSource propertySource) { + this.configSourceManager.highestPriority(propertySource); + return this; + } + + @Override + public TamayaConfigBuilder lowestPriority(ConfigSource propertySource) { + this.configSourceManager.lowestPriority(propertySource); + return this; + } + + @Override + public TamayaConfigBuilder withFilters(Filter... filters){ + this.filterManager.addFilter(filters); + return this; + } + + @Override + public TamayaConfigBuilder withFilters(Collection<Filter> filters){ + this.filterManager.addFilter(filters); + return this; + } + + @Override + public TamayaConfigBuilder removeFilters(Filter... filters) { + this.filterManager.removeFilters(filters); + return this; + } + + @Override + public TamayaConfigBuilder removeFilters(Collection<Filter> filters) { + this.filterManager.removeFilters(filters); + return this; + } + + + @Override + public <T> TamayaConfigBuilder removeConverters(Class<T> typeToConvert, + Converter<T>... converters) { + this.converterManager.removeConverters(typeToConvert, converters); + return this; + } + + @Override + public <T> TamayaConfigBuilder removeConverters(Class<T> typeToConvert, + Collection<Converter<T>> converters) { + this.converterManager.removeConverters(typeToConvert, Collection.class.cast(converters)); + return this; + } + + @Override + public <T> TamayaConfigBuilder removeConverters(Class<T> typeToConvert) { + this.converterManager.removeConverters(typeToConvert); + return this; + } + + + @Override + public TamayaConfigBuilder withPropertyValueCombinationPolicy(ConfigValueCombinationPolicy combinationPolicy){ + this.configSourceManager.setConfigValueCombinationPolicy(combinationPolicy); + return this; + } + + @Override + public <T> TamayaConfigBuilder withConverters(Class<T> type, Converter<T>... converters){ + this.converterManager.addConverter(type, converters); + return this; + } + + @Override + public <T> TamayaConfigBuilder withConverters(Class<T> type, Collection<Converter<T>> converters){ + this.converterManager.addConverter(type, Collection.class.cast(converters)); + return this; + } + + @Override + public TamayaConfigBuilder sortFilter(Comparator<Filter> comparator) { + this.filterManager.sortFilter(comparator); + return this; + } + + @Override + public TamayaConfigBuilder sortSources(Comparator<ConfigSource> comparator) { + this.configSourceManager.sortSources(comparator); + return this; + } + + @Override + public List<Filter> getFilters() { + return this.filterManager.getFilters(); + } + + @Override + public Map<Type, List<Converter>> getConverter() { + return this.converterManager.getConverters(); + } + + /** + * Builds a new configuration based on the configuration of this builder instance. + * + * @return a new {@link Config configuration instance}, + * never {@code null}. + */ + @Override + public Config build() { + return new DefaultConfig(getConfigContext()); + } + + @Override + public ConfigContext getConfigContext() { + return new ConfigContext() { + @Override + public List<ConfigSource> getSources() { + return configSourceManager.getSources(); + } + + @Override + public List<Filter> getFilters() { + return filterManager.getFilters(); + } + + @Override + public Map<Type, List<Converter>> getConverters() { + return converterManager.getConverters(); + } + + @Override + public ConfigValueCombinationPolicy getConfigValueCombinationPolicy() { + return configSourceManager.getConfigValueCombinationPolicy(); + } + }; + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/base/DefaultServiceContext.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/base/DefaultServiceContext.java b/code/base/src/main/java/org/apache/tamaya/base/DefaultServiceContext.java new file mode 100644 index 0000000..752fd3c --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/base/DefaultServiceContext.java @@ -0,0 +1,225 @@ +/* + * 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.base; + +import org.apache.tamaya.spi.ServiceContext; + +import javax.annotation.Priority; +import java.io.IOException; +import java.net.URL; +import java.text.MessageFormat; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * This class implements the (default) {@link ServiceContext} interface and hereby uses the JDK + * {@link ServiceLoader} to load the services required. + */ +public final class DefaultServiceContext implements ServiceContext { + private static final Logger LOG = Logger.getLogger(DefaultServiceContext.class.getName()); + /** + * List current services loaded, per class. + */ + private final ConcurrentHashMap<Class<?>, List<Object>> servicesLoaded = new ConcurrentHashMap<>(); + /** + * Singletons. + */ + private final Map<Class<?>, Object> singletons = new ConcurrentHashMap<>(); + @SuppressWarnings("rawtypes") + private Map<Class, Class> factoryTypes = new ConcurrentHashMap<>(); + + @Override + public <T> T getService(Class<T> serviceType) { + return getService(serviceType, ServiceContext.defaultClassLoader()); + } + + @Override + public <T> T getService(Class<T> serviceType, ClassLoader classLoader) { + Object cached = singletons.get(serviceType); + if (cached == null) { + cached = create(serviceType); + if(cached!=null) { + singletons.put(serviceType, cached); + } + } + return serviceType.cast(cached); + } + + @Override + public <T> T create(Class<T> serviceType) { + return create(serviceType, ServiceContext.defaultClassLoader()); + } + + @Override + public <T> T create(Class<T> serviceType, ClassLoader classLoader) { + @SuppressWarnings("unchecked") + Class<? extends T> implType = factoryTypes.get(serviceType); + if(implType==null) { + Collection<T> services = getServices(serviceType, classLoader); + if (services.isEmpty()) { + return null; + } else { + return getServiceWithHighestPriority(services, serviceType); + } + } + try { + return implType.newInstance(); + } catch (Exception e) { + LOG.log(Level.SEVERE, "Failed to create instance of " + implType.getName(), e); + return null; + } + } + + /** + * Loads and registers services. + * + * @param <T> the concrete type. + * @param serviceType The service type. + * @return the items found, never {@code null}. + */ + @Override + public <T> List<T> getServices(final Class<T> serviceType) { + return getServices(serviceType, ServiceContext.defaultClassLoader()); + } + + /** + * Loads and registers services. + * + * @param <T> the concrete type. + * @param serviceType The service type. + * @return the items found, never {@code null}. + */ + @Override + public <T> List<T> getServices(final Class<T> serviceType, ClassLoader classLoader) { + @SuppressWarnings("unchecked") + List<T> found = (List<T>) servicesLoaded.get(serviceType); + if (found != null) { + return found; + } + List<T> services = new ArrayList<>(); + try { + for (T t : ServiceLoader.load(serviceType, classLoader)) { + services.add(t); + } + if(services.isEmpty()) { + for (T t : ServiceLoader.load(serviceType, serviceType.getClassLoader())) { + services.add(t); + } + } + Collections.sort(services, org.apache.tamaya.base.PriorityServiceComparator.getInstance()); + services = Collections.unmodifiableList(services); + } catch (ServiceConfigurationError e) { + LOG.log(Level.WARNING, + "Error loading services current type " + serviceType, e); + if(services==null){ + services = Collections.emptyList(); + } + } + @SuppressWarnings("unchecked") + final List<T> previousServices = List.class.cast(servicesLoaded.putIfAbsent(serviceType, (List<Object>) services)); + return previousServices != null ? previousServices : services; + } + + /** + * Checks the given instance for a @Priority annotation. If present the annotation's value is evaluated. If no such + * annotation is present, a default priority of {@code 1} is returned. + * @param o the instance, not {@code null}. + * @return a priority, by default 1. + */ + public static int getPriority(Object o){ + int prio = 1; //X TODO discuss default priority + Priority priority = o.getClass().getAnnotation(Priority.class); + if (priority != null) { + prio = priority.value(); + } + return prio; + } + + /** + * @param services to scan + * @param <T> type of the service + * + * @return the service with the highest {@link Priority#value()} + * + * @throws IllegalStateException if there are multiple service implementations with the maximum priority + */ + private <T> T getServiceWithHighestPriority(Collection<T> services, Class<T> serviceType) { + T highestService = null; + // we do not need the priority stuff if the list contains only one element + if (services.size() == 1) { + highestService = services.iterator().next(); + this.factoryTypes.put(serviceType, highestService.getClass()); + return highestService; + } + + Integer highestPriority = null; + int highestPriorityServiceCount = 0; + + for (T service : services) { + int prio = getPriority(service); + if (highestPriority == null || highestPriority < prio) { + highestService = service; + highestPriorityServiceCount = 1; + highestPriority = prio; + } else if (highestPriority == prio) { + highestPriorityServiceCount++; + } + } + + if (highestPriorityServiceCount > 1) { + throw new IllegalStateException(MessageFormat.format("Found {0} implementations for Service {1} with Priority {2}: {3}", + highestPriorityServiceCount, + serviceType.getName(), + highestPriority, + services)); + } + this.factoryTypes.put(serviceType, highestService.getClass()); + return highestService; + } + + @Override + public int ordinal() { + return 1; + } + + @Override + public Enumeration<URL> getResources(String resource, ClassLoader cl) throws IOException { + if(cl==null){ + cl = Thread.currentThread().getContextClassLoader(); + } + if(cl==null){ + cl = getClass().getClassLoader(); + } + return cl.getResources(resource); + } + + @Override + public URL getResource(String resource, ClassLoader cl) { + if(cl==null){ + cl = Thread.currentThread().getContextClassLoader(); + } + if(cl==null){ + cl = getClass().getClassLoader(); + } + return cl.getResource(resource); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/base/PriorityServiceComparator.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/base/PriorityServiceComparator.java b/code/base/src/main/java/org/apache/tamaya/base/PriorityServiceComparator.java new file mode 100644 index 0000000..37f5095 --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/base/PriorityServiceComparator.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.base; + +import javax.annotation.Priority; +import java.io.Serializable; +import java.util.Comparator; + +/** + * Comparator implementation for odering services loaded based on their increasing priority values. + */ +public class PriorityServiceComparator implements Comparator<Object>, Serializable { + + private static final long serialVersionUID = 1L; + + private static final PriorityServiceComparator INSTANCE = new PriorityServiceComparator(); + + /** Singleton constructor. */ + private PriorityServiceComparator(){} + + /** + * Get the shared instance of the comparator. + * @return the shared instance, never null. + */ + public static PriorityServiceComparator getInstance(){ + return INSTANCE; + } + + @Override + public int compare(Object o1, Object o2) { + int prio = getPriority(o1) - getPriority(o2); + if (prio < 0) { + return 1; + } else if (prio > 0) { + return -1; + } else { + return o1.getClass().getSimpleName().compareTo(o2.getClass().getSimpleName()); + } + } + + /** + * Checks the given instance for a @Priority annotation. If present the annotation's value is evaluated. If no such + * annotation is present, a default priority {@code 1} is returned. + * + * @param o the instance, not {@code null}. + * @return a priority, by default 1. + */ + public static int getPriority(Object o) { + return getPriority(o.getClass()); + } + + /** + * Checks the given type optionally annotated with a @Priority. If present the annotation's value is evaluated. + * If no such annotation is present, a default priority {@code 1} is returned. + * + * @param type the type, not {@code null}. + * @return a priority, by default 1. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static int getPriority(Class type) { + int prio = 1; + Priority priority = (Priority)type.getAnnotation(Priority.class); + if (priority != null) { + prio = priority.value(); + } + return prio; + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/base/ReflectionUtil.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/base/ReflectionUtil.java b/code/base/src/main/java/org/apache/tamaya/base/ReflectionUtil.java new file mode 100644 index 0000000..85d2dc3 --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/base/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.base; + +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/063f8ada/code/base/src/main/java/org/apache/tamaya/base/configsource/BaseConfigSource.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/base/configsource/BaseConfigSource.java b/code/base/src/main/java/org/apache/tamaya/base/configsource/BaseConfigSource.java new file mode 100644 index 0000000..12adfaa --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/base/configsource/BaseConfigSource.java @@ -0,0 +1,170 @@ +/* + * 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.base.configsource; + +import javax.config.spi.ConfigSource; +import java.util.Map; +import java.util.Objects; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Abstract {@link ConfigSource} that allows to set a default ordinal that will be used, if no + * ordinal is provided with the config. + */ +public abstract class BaseConfigSource implements ConfigSource{ + /** 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 BaseConfigSource(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 BaseConfigSource(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 BaseConfigSource(String name, int defaultOrdinal){ + this.name = Objects.requireNonNull(name); + this.defaultOrdinal = defaultOrdinal; + } + + + /** + * Constructor, using a default ordinal of 0. + */ + protected BaseConfigSource(){ + 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; + } + String configuredOrdinal = getValue(CONFIG_ORDINAL); + if(configuredOrdinal!=null){ + try { + return Integer.parseInt(configuredOrdinal); + } 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 String getValue(String key) { + Map<String,String> properties = getProperties(); + String 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; + + BaseConfigSource that = (BaseConfigSource) 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/063f8ada/code/base/src/main/java/org/apache/tamaya/base/configsource/BuildableConfigSource.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/base/configsource/BuildableConfigSource.java b/code/base/src/main/java/org/apache/tamaya/base/configsource/BuildableConfigSource.java new file mode 100644 index 0000000..b0e859b --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/base/configsource/BuildableConfigSource.java @@ -0,0 +1,208 @@ +/* + * 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.base.configsource; + +import javax.config.spi.ConfigSource; +import java.util.*; + +/** + * A Buildable property source. + */ +public class BuildableConfigSource implements ConfigSource{ + + private int ordinal; + private String name = "PropertySource-"+UUID.randomUUID().toString(); + private Map<String,String> properties = new HashMap<>(); + + @Override + public int getOrdinal() { + return ordinal; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getValue(String key) { + return properties.get(key); + } + + @Override + public Map<String, String> getProperties() { + return Collections.unmodifiableMap(properties); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + BuildableConfigSource that = (BuildableConfigSource) o; + + return name.equals(that.name); + } + + @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; + private String name = "PropertySource-"+ UUID.randomUUID().toString(); + private Map<String,String> 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 withProperty(String key, String value) { + return withProperty(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 withProperty(String key, String value, String source) { + this.properties.put(key, value); + if(source!=null) { + this.properties.put(key+"[meta]", "source="+source); + } + return this; + } + + /** + * With properties builder. + * + * @param properties the properties + * @param source the source + * @return the builder + */ + public Builder withProperty(Map<String, String> properties, String source) { + properties.forEach((k,v) -> { + withProperty(k, v, source); + }); + return this; + } + + /** + * With simple properties builder. + * + * @param properties the properties + * @return the builder + */ + public Builder withProperties(Map<String, String> properties) { + properties.forEach((k,v) -> { + withProperty(k, v); + }); + 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 BuildableConfigSource build() { + BuildableConfigSource buildableConfigSource = new BuildableConfigSource(); + buildableConfigSource.name = this.name; + buildableConfigSource.properties = this.properties; + buildableConfigSource.ordinal = this.ordinal; + return buildableConfigSource; + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/base/configsource/BuildableConfigSourceProvider.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/base/configsource/BuildableConfigSourceProvider.java b/code/base/src/main/java/org/apache/tamaya/base/configsource/BuildableConfigSourceProvider.java new file mode 100644 index 0000000..d670576 --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/base/configsource/BuildableConfigSourceProvider.java @@ -0,0 +1,113 @@ +/* + * 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.base.configsource; + +import javax.config.spi.ConfigSource; +import javax.config.spi.ConfigSourceProvider; +import java.util.*; + +/** + * A Buildable property source. + */ +public class BuildableConfigSourceProvider implements ConfigSourceProvider{ + + private List<ConfigSource> sources = new ArrayList<>(); + + @Override + public Collection<ConfigSource> getConfigSources(ClassLoader cl) { + return Collections.unmodifiableCollection(sources); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + BuildableConfigSourceProvider that = (BuildableConfigSourceProvider) 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<ConfigSource> sources = new ArrayList<>(); + + private Builder() { + } + + /** + * With propertySources. + * + * @param propertySources the propertySources + * @return the builder + */ + public Builder withPropertySourcs(ConfigSource... 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<ConfigSource> sources) { + this.sources.addAll(sources); + return this; + } + + /** + * Build buildable property source. + * + * @return the buildable property source + */ + public BuildableConfigSourceProvider build() { + BuildableConfigSourceProvider buildablePropertySource = new BuildableConfigSourceProvider(); + buildablePropertySource.sources.addAll(this.sources); + return buildablePropertySource; + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/base/configsource/CLIConfigSource.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/base/configsource/CLIConfigSource.java b/code/base/src/main/java/org/apache/tamaya/base/configsource/CLIConfigSource.java new file mode 100644 index 0000000..4e57229 --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/base/configsource/CLIConfigSource.java @@ -0,0 +1,130 @@ +/* + * 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.base.configsource; + +import java.util.*; + +/** + * PropertySource that allows to add the programs main arguments as configuration entries. Unix syntax using '--' and + * '-' params is supported. + */ +public class CLIConfigSource extends BaseConfigSource { + + /** The original main arguments. */ + private static String[] args = new String[0]; + + /** The map of parsed main arguments. */ + private static Map<String,String> mainArgs; + + /** Initializes the initial state. */ + static{ + initMainArgs(args); + } + + /** + * Creates a new instance. + */ + public CLIConfigSource(){ + this((String[])null); + } + + /** + * Creates a new instance, allows optionally to pass the main arguments. + * @param args the args, or null. + */ + public CLIConfigSource(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 CLIConfigSource(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){ + CLIConfigSource.args = Objects.requireNonNull(args); + // TODO is there a way to figure out the args? + String argsProp = System.getProperty("main.args"); + if(argsProp!=null){ + CLIConfigSource.args = argsProp.split("\\s"); + } + Map<String,String> result; + if(CLIConfigSource.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: CLIConfigSource.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); + } + } + } + } + CLIConfigSource.mainArgs = Collections.unmodifiableMap(result); + } + + @Override + public Map<String, String> 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/063f8ada/code/base/src/main/java/org/apache/tamaya/base/configsource/ConfigSourceComparator.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/base/configsource/ConfigSourceComparator.java b/code/base/src/main/java/org/apache/tamaya/base/configsource/ConfigSourceComparator.java new file mode 100644 index 0000000..c1015ea --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/base/configsource/ConfigSourceComparator.java @@ -0,0 +1,117 @@ +/* + * 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.base.configsource; + +import javax.config.spi.ConfigSource; +import java.io.Serializable; +import java.util.Comparator; +import java.util.logging.Logger; + +/** + * Comparator for ordering of PropertySources based on their ordinal method and class name. + */ +public class ConfigSourceComparator implements Comparator<ConfigSource>, Serializable { + /** serial version UID. */ + private static final long serialVersionUID = 1L; + + private static final Logger LOG = Logger.getLogger(ConfigSourceComparator.class.getName()); + + private static final ConfigSourceComparator INSTANCE = new ConfigSourceComparator(); + + private String alternativeOrdinalKey; + + /** Singleton constructor. */ + private ConfigSourceComparator(){} + + /** + * Get the shared instance of the comparator. + * @return the shared instance, never null. + */ + public static ConfigSourceComparator 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 compareConfigSources(ConfigSource source1, ConfigSource 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 ConfigSource}, Hereby the ordinal of type {@code int} + * is evaluated as follows: + * <ol> + * <li>It evaluates the {@code String} value for {@link ConfigSource#CONFIG_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 javax.annotation.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(ConfigSource propertySource) { + return getOrdinal(propertySource, null); + } + + public static int getOrdinal(ConfigSource propertySource, String alternativeOrdinalKey) { + if(alternativeOrdinalKey!=null) { + String ordinalValue = propertySource.getValue(alternativeOrdinalKey); + if (ordinalValue != null) { + try { + return Integer.parseInt(ordinalValue.trim()); + } catch (Exception e) { + LOG.finest("Failed to parse ordinal from " + alternativeOrdinalKey + + " in " + propertySource.getName() + ": " + ordinalValue); + } + } + } + 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 ConfigSourceComparator setOrdinalKey(String ordinalKey) { + this.alternativeOrdinalKey = ordinalKey; + return this; + } + + @Override + public int compare(ConfigSource source1, ConfigSource source2) { + return compareConfigSources(source1, source2); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/base/configsource/ConfigSourceManager.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/base/configsource/ConfigSourceManager.java b/code/base/src/main/java/org/apache/tamaya/base/configsource/ConfigSourceManager.java new file mode 100644 index 0000000..7a09bf5 --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/base/configsource/ConfigSourceManager.java @@ -0,0 +1,375 @@ +/* + * 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.base.configsource; + +import org.apache.tamaya.base.FormatUtils; +import org.apache.tamaya.spi.*; + +import javax.config.Config; +import javax.config.spi.ConfigSource; +import javax.config.spi.ConfigSourceProvider; +import java.util.*; +import java.util.logging.Logger; + +/** + * Implementation of the Configuration API. This class uses the current {@link Config} to evaluate the + * chain of {@link javax.config.spi.ConfigSource} and {@link Filter} + * instance to evaluate the current Configuration. + */ +public class ConfigSourceManager { + /** + * The logger. + */ + private static final Logger LOG = Logger.getLogger(ConfigSourceManager.class.getName()); + /** + * The current list of loaded {@link ConfigSource} instances. + */ + private final List<ConfigSource> configSources = new ArrayList<>(); + + /** + * The overriding policy used when combining PropertySources registered to evalute the final configuration + * values. + */ + ConfigValueCombinationPolicy configValueCombinationPolicy; + + private ClassLoader classloader = ServiceContext.defaultClassLoader(); + + /** + * Create a new filter manager. + */ + public ConfigSourceManager(){ + configValueCombinationPolicy = ServiceContextManager.getServiceContext().getService( + ConfigValueCombinationPolicy.class); + if(configValueCombinationPolicy == null){ + configValueCombinationPolicy = ConfigValueCombinationPolicy.DEFAULT_OVERRIDING_COLLECTOR; + } + LOG.info("Using PropertyValueCombinationPolicy: " + configValueCombinationPolicy); + } + + /** + * Create a new filter manager. + * @param configSources the config sources to be used, not null. + */ + public ConfigSourceManager(List<ConfigSource> configSources){ + this.configSources.addAll(configSources); + LOG.info("Registered " + configSources.size() + " config sources: " + configSources); + } + + /** + * Get the classloader used for instance creation. + * @return the classloader, never null. + */ + public ClassLoader getClassloader(){ + return classloader; + } + + /** + * Sets the classloader to use for loading of instances. + * @param ClassLoader the classloader, not null. + * @return this instance for chaining. + */ + public ConfigSourceManager setClassloader(ClassLoader ClassLoader){ + this.classloader = Objects.requireNonNull(classloader); + return this; + } + + /** + * The current unmodifiable list of loaded {@link ConfigSource} instances. + */ + public List<ConfigSource> getSources(){ + return Collections.unmodifiableList(configSources); + } + + /** + * Registers a new ConfigSource instance. + * + * @param configSources the config source, not {@code null}. + */ + public ConfigSourceManager addSources(Collection<ConfigSource> configSources) { + Objects.requireNonNull(configSources); + for(ConfigSource configSource:configSources) { + if (!this.configSources.contains(configSource)) { + this.configSources.add(configSource); + } + } + return this; + } + + public ConfigSourceManager addDiscoveredSources() { + List<ConfigSource> propertySources = new ArrayList<>(); + addCoreSources(propertySources); + for(ConfigSource ps: ServiceContextManager.getServiceContext().getServices(ConfigSource.class)) { + if(!propertySources.contains(ps)){ + propertySources.add(ps); + } + } + for(ConfigSourceProvider provider: + ServiceContextManager.getServiceContext().getServices(ConfigSourceProvider.class)){ + provider.getConfigSources(classloader).forEach(propertySources::add); + } + Collections.sort(propertySources, ConfigSourceComparator.getInstance()); + return addSources(propertySources); + } + + protected ConfigSourceManager addCoreSources(List<ConfigSource> propertySources) { + for(ConfigSource ps: new ConfigSource[]{ + new EnvironmentConfigSource(), + new JavaConfigurationConfigSource(), + new CLIConfigSource(), + new SystemConfigSource() + }){ + if(!propertySources.contains(ps)){ + propertySources.add(ps); + } + } + return this; + } + + /** + * Registers a new ConfigSource instance. + * + * @param configSources the config source, not {@code null}. + */ + public ConfigSourceManager addSources(ConfigSource... configSources) { + return addSources(Arrays.asList(configSources)); + } + + + /** + * Access a {@link ConfigSource} using its (unique) name. + * @param name the propoerty source's name, not {@code null}. + * @return the propoerty source found, or {@code null}. + */ + public ConfigSource getSource(String name) { + for(ConfigSource ps: getSources()){ + if(name.equals(ps.getName())){ + return ps; + } + } + return null; + } + + /** + * Removes the given config sources, if existing. The existing order of config + * sources is preserved. + * + * @param configSources the config sources to remove, not {@code null}. + * @return the builder for chaining. + */ + public final ConfigSourceManager removeSources(ConfigSource... configSources) { + return removeSources(Arrays.asList(configSources)); + } + /** + * Removes the given config sources, if existing. The existing order of config + * sources is preserved. + * + * @param configSources the config sources to remove, not {@code null}. + * @return the builder for chaining. + */ + public ConfigSourceManager removeSources(Collection<ConfigSource> configSources) { + this.configSources.removeAll(configSources); + return this; + } + + /** + * Increases the priority of the given property source, by moving it towards the end + * of the chain of property sources. If the property source given is already at the end + * this method has no effect. This operation does not change any ordinal values. + * + * @param configSource the config source to be incresed regarding its significance. + * @return the builder for chaining. + * @throws IllegalArgumentException If no such property source exists in the current + * chain. + */ + public ConfigSourceManager increasePriority(ConfigSource configSource){ + int index = configSources.indexOf(configSource); + if(index<0){ + throw new IllegalArgumentException("No such ConfigSource: " + configSource); + } + if(index<(configSources.size()-1)){ + configSources.remove(configSource); + configSources.add(index+1, configSource); + } + return this; + } + + /** + * Decreases the priority of the given property source, by moving it towards the start + * of the chain of property sources. If the property source given is already the first + * this method has no effect. This operation does not change any ordinal values. + * + * @param configSource the config source to be decresed regarding its significance. + * @return the builder for chaining. + * @throws IllegalArgumentException If no such property source exists in the current + * chain. + */ + public ConfigSourceManager decreasePriority(ConfigSource configSource){ + int index = configSources.indexOf(configSource); + if(index<0){ + throw new IllegalArgumentException("No such ConfigSource: " + configSource); + } + if(index>0){ + configSources.remove(configSource); + configSources.add(index-1, configSource); + } + return this; + } + + /** + * Increases the priority of the given property source to be maximal, by moving it to + * the tail of the of property source chain. If the property source given is + * already the last item this method has no effect. This operation does not change + * any ordinal values. + * + * @param configSource the config source to be maximized regarding its significance. + * @return the builder for chaining. + * @throws IllegalArgumentException If no such property source exists in the current + * chain. + */ + public ConfigSourceManager highestPriority(ConfigSource configSource){ + int index = configSources.indexOf(configSource); + if(index<0){ + throw new IllegalArgumentException("No such ConfigSource: " + configSource); + } + if(index<(configSources.size()-1)){ + configSources.remove(configSource); + configSources.add(configSource); + } + return this; + } + + /** + * Decreases the priority of the given property source to be minimal, by moving it to + * the start of the chain of property source chain. If the property source given is + * already the first item this method has no effect. This operation does not change + * any ordinal values. + * + * @param configSource the config source to be minimized regarding its significance. + * @return the builder for chaining. + * @throws IllegalArgumentException If no such property source exists in the current + * chain. + */ + public ConfigSourceManager lowestPriority(ConfigSource configSource){ + int index = configSources.indexOf(configSource); + if(index<0){ + throw new IllegalArgumentException("No such PropertySource: " + configSource); + } + if(index>0){ + configSources.remove(configSource); + configSources.add(0, configSource); + } + return this; + } + + public ConfigSourceManager sortSources(Comparator<ConfigSource> comparator) { + Collections.sort(configSources, comparator); + return this; + } + + /** + * Access the {@link ConfigValueCombinationPolicy} used to evaluate the final + * property values. + * @return the {@link ConfigValueCombinationPolicy} used, never null. + */ + public ConfigValueCombinationPolicy getConfigValueCombinationPolicy(){ + return configValueCombinationPolicy; + } + + /** + * Removes all contained items. + * @return this instance for chaining. + */ + public ConfigSourceManager clear() { + this.configSources.clear(); + this.configValueCombinationPolicy = ConfigValueCombinationPolicy.DEFAULT_OVERRIDING_POLICY; + return this; + } + + /** + * Evaluates the raw value using the context's PropertyValueCombinationPolicy. + * @param key the key, not null. + * @return the value, before filtering is applied. + */ + public ConfigValue evaluteRawValue(String key) { + List<ConfigSource> configSources = getSources(); + ConfigValue unfilteredValue = null; + ConfigValueCombinationPolicy combinationPolicy = getConfigValueCombinationPolicy(); + for (ConfigSource propertySource : configSources) { + unfilteredValue = combinationPolicy.collect(unfilteredValue, key, propertySource); + } + return unfilteredValue; + } + + public Map<String, ConfigValue> evaluateRawValues() { + List<ConfigSource> configSources = getSources(); + ConfigValueCombinationPolicy combinationPolicy = getConfigValueCombinationPolicy(); + Map<String, ConfigValue> result = new HashMap<>(); + for (ConfigSource propertySource : configSources) { + for (Map.Entry<String,String> propEntry: propertySource.getProperties().entrySet()) { + ConfigValue unfilteredValue = result.get(propEntry.getKey()); + unfilteredValue = combinationPolicy. + collect(unfilteredValue, propEntry.getKey(), propertySource); + if(unfilteredValue!=null){ + result.put(unfilteredValue.getKey(), unfilteredValue); + } + } + } + return result; + } + + + @Override + public String toString() { + StringBuilder b = new StringBuilder("Config Sources\n"); + b.append("--------------\n"); + if(configSources.isEmpty()){ + b.append(" No config sources loaded.\n\n"); + }else { + b.append(" CLASS NAME ORDINAL SCANNABLE SIZE STATE ERROR\n\n"); + for (ConfigSource ps : configSources) { + b.append(" "); + FormatUtils.appendFormatted(b, ps.getClass().getSimpleName(), 30); + FormatUtils.appendFormatted(b, ps.getName(), 70); + FormatUtils.appendFormatted(b, String.valueOf(ConfigSourceComparator.getOrdinal(ps)), 8); + String state = ps.getValue("_state"); + if(state==null){ + FormatUtils.appendFormatted(b, "OK", 10); + }else { + FormatUtils.appendFormatted(b, state, 10); + if("ERROR".equals(state)){ + String val = ps.getValue("_exception"); + if(val!=null) { + FormatUtils.appendFormatted(b, val, 30); + } + } + } + b.append('\n'); + } + b.append("\n"); + } + b.append("\n"); + b.append(" ConfigValueCombinationPolicy: " + configValueCombinationPolicy.getClass().getName()).append('\n'); + return b.toString(); + } + + public void setConfigValueCombinationPolicy(ConfigValueCombinationPolicy configValueCombinationPolicy) { + this.configValueCombinationPolicy = Objects.requireNonNull(configValueCombinationPolicy); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/base/configsource/EnvironmentConfigSource.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/base/configsource/EnvironmentConfigSource.java b/code/base/src/main/java/org/apache/tamaya/base/configsource/EnvironmentConfigSource.java new file mode 100644 index 0000000..2e45bb0 --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/base/configsource/EnvironmentConfigSource.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.base.configsource; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * <p>{@link javax.config.spi.ConfigSource} 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 EnvironmentConfigSource} 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 EnvironmentConfigSource} 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 EnvironmentConfigSource extends BaseConfigSource { + 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 EnvironmentConfigSource} + */ + 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 EnvironmentConfigSource(){ + 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 EnvironmentConfigSource(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 EnvironmentConfigSource(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 EnvironmentConfigSource(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 String getValue(String key) { + if (isDisabled()) { + return null; + } + String effectiveKey = hasPrefix() ? getPrefix() + "." + key + : key; + return getPropertiesProvider().getenv(effectiveKey); + } + + private boolean hasPrefix() { + return null != prefix; + } + + @Override + public Map<String, String> getProperties() { + if(disabled){ + return Collections.emptyMap(); + } + String prefix = this.prefix; + if(prefix==null) { + Map<String, String> entries = new HashMap<>(System.getenv().size()); + for (Map.Entry<String, String> entry : System.getenv().entrySet()) { + entries.put(entry.getKey(), entry.getValue()); + } + return entries; + }else{ + Map<String, String> entries = new HashMap<>(System.getenv().size()); + for (Map.Entry<String, String> entry : System.getenv().entrySet()) { + entries.put(prefix + entry.getKey(), entry.getValue()); + } + 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 EnvironmentConfigSource}.</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(); + } + } +}
