You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tamaya.apache.org by an...@apache.org on 2014/12/01 10:43:44 UTC
[1/8] incubator-tamaya git commit: TAMAYA-14: added resource support.
TAMAYA-15: Moved PropertyProviders to API,
added SPI. TAMAYA-8: Added/improved Javadoc.
Repository: incubator-tamaya
Updated Branches:
refs/heads/master bed10573d -> a55d1c973
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/docs/design/0_UseCases.adoc
----------------------------------------------------------------------
diff --git a/docs/design/0_UseCases.adoc b/docs/design/0_UseCases.adoc
index a7719ab..8177c14 100644
--- a/docs/design/0_UseCases.adoc
+++ b/docs/design/0_UseCases.adoc
@@ -2,20 +2,132 @@
[[UseCases]]
== Use Cases
-This section describes some, but not all, of the use cases that should be covered with this JSR.
+This section describes some, but not all, of the use cases that should be covered by Tamaya.
+
+
+[[UCSimpleAccess]]
+=== Simple Property Access (UC 1)
+
+Tamaya should provide a simple Java API for accessing configuration. Hereby
+
+* Configuration is organized as key/value pairs. This basically can be modeled as +Map<String,String>+
+* Configuration should be as simple as possible. A +Map<String,String>+ instance has methods that may not
+ be used in many use cases and/or are not easy to implement. Currently the following functionality
+ must be supported:
+ ** access a value by key (+get+)
+ ** check if a value is present (+containsKey+)
+ ** get a set of all defined keys (+keySet+)
+ ** a property provider must be convertable to a +Map+, by calling +toMap()+
+ ** a property provider must get access to its meta information.
+* The API must never return null.
+* The API should support undefined values.
+* The API must support passing default values, to be returned if a value is undefined.
+* The API must allow to throw exceptions, when a value is undefined.
+ Customized exceptions hereby should be supported.
+* Properties can be stored in the classpath, on a file.
+* Properties can be stored in properties, xml-properties or ini-format.
+* Properties can also be provided as properties, or as a Map<String,String>
+
+
+[[UCAutoConfig]]
+=== Automatic Configuration
+
+* Tamaya should provide a feature for automatic configuration, where properties can be annotated.
+* Hereby the lifecycle of the instances configured should not be managed by Tamaya.
+* String as well as other types should be supported.
+* It should be possible to define default values to be used, if no valid value is present.
+* It should be possible to define dynamic expressions, at least for default values.
+* The values configured should be reinjected, if the underlying configuration changes.
+* Reinjection should be controllable by an injection policy.
+* It should be possible to evaluate multiple keys, e.g. current keys, and as a backup deprecated keys
+ from former application releases.
+* The type conversion of the properties injected should be configurable.
+* The value evaluated for a property (before type conversion) may be adaptable as well.
+* It should be possible to observe configuration changes.
+
+The most simplest way is using injection, e.g. a POJO can be written as follows:
+
+[source, java]
+.Configured POJO Example
+----------------------------------------------------
+public class MyPojo {
+ @ConfigProperty("myCurrency")
+ @DefaultValue("CHF")
+ private String currency;
+
+ @ConfigProperty("myCurrencyRate")
+ private Long currencyRate;
+}
+----------------------------------------------------
+
+The instance then can be passed for being configured:
+
+[source, java]
+.Configuring a POJO
+----------------------------------------------------
+MyPojo instance = new MyPojo();
+Configuration.configure(instance);
+----------------------------------------------------
+
+[[UCTemplates]]
+=== Configuration Templates
+
+For type safe configuration clients should be able to define an interface that is implemented by the
+configuration system:
+
+* clients define an interface and annotate it as required
+* the interface methods must not take any arguments
+* the configuration system can be called to return such an interface implementation.
+* the configuration system returns a proxy hereby providing the values required.
+* similar to configured types also templates support multiple values and custom adapters.
+* It is possible to listen on configuration changes for templates, so users of the templates
+ may react on configuration changes.
+
+A template hereby is modelled by annotating an interface with the same annotations as for
+configured classes:
+
+[source, java]
+.Type Safe Configuration Template Example
+----------------------------------------------------
+public interface MyConfig {
+ @ConfiguredProperty("myCurrency")
+ @DefaultValue("CHF")
+ String getCurrency();
+
+ @ConfiguredProperty("myCurrencyRate")
+ Long getCurrencyRate();
+
+}
+----------------------------------------------------
+
+The configuration system will then provide the interface as follows:
+
+[source, java]
+.Accessing a type safe Configuration Template
+----------------------------------------------------
+MyConfig config = Configuration.of(MyConfig.class);
+----------------------------------------------------
+
+Finally a +Configuration+ itself can be accessed as template as well, which
+provides full access to all features:
+
+[source, java]
+.Accessing a Configuration
+----------------------------------------------------
+Configuration config = Configuration.of(Configuration.class);
+----------------------------------------------------
+
[[UCSimpleConfiguration]]
=== Simple Property Based Configuration
-In this most simple usage scenario an application is created that is preconfigured by some property files contained in the
-Java archive. Using the command line it is possible to redefine/override some of the properties, e.g. by using system properties.
-Typical example are small command line tools.
-
--> It must be possible to define default configuration and package it with the (SE) application.
+In this most simple usage scenario an application is configured by some property files contained in the
+Java archive.
--> It must be possible to consider system properties or other command line arguments for usage with configuration.
+* It provides default property files in the formats defined by the JDK within its application archive.
+* It allows to override settings by system properties.
+* It is able to consider command line arguments as well.
--> It must be possible that command line arguments can override defaults configured.
[[UCAdvancedPropertyBasedConfiguration]]
=== Advanced Property Based Configuration
@@ -25,35 +137,30 @@ must be improved, since
* some environment settings should not be overridable
* some defaults should be overridden by environment or system properties, whereas others may not
+* Additionally the user may have an option, where he is allowed to define an external configuration file that should be used to configure
+ the application. This is especially useful for applications with lots of command line options (under windows even command
+ execution may fail die to exceeding command length).
+* Finally application developers may have their own formats in place, so the system should be able to support these formats.
-Additionally the user may have an option, where he is allowed to define an external configuration file that should be used to configure
-the application. This is especially useful for applications with lots of command line options (under windows even command
-execution may fail die to exceeding command length). Finally application developers may have their own formats in place, so the
-system should be able to support these formats.
-
--> Environment properties must be considered as well.
-
--> It must be possible to control overriding.
-
--> It must be possible to dynamically add configuration locations to be considered.
-
--> It must be possible to define customized configuration formats.
[[UCModularizedConfiguration]]
=== Modularized Configuration
-When systems grow they must be modularized to keep control. Whereas that sounds not really fancy, it leads to additional things
-to be considered by a configuration system:
+When systems grow they must be modularized to keep control. Whereas that sounds not really fancy, it leads to additional aspects
+to be considered by a configuration system.
-* The different modules must have access to their own "module configuration".
-* Modules may want to define a contract, which properties may be overriden.
+* Different code modules want to have their own "module configuration".
+* Some modules require a certain subset of keys to be read at once into a Map.
+* Products contain multiple modules, which per product are configured separately.
-Consequently
--> Parts of Configuration must be identifiable and accessible in a isolated way.
+[[UCTypeSupport]]
+=== Extended Type Support
--> Module configuration requires partial isolation or other mechanisms to ensure only configuration aspects
- that are allowed to be overriden can be overriden.
+Application configuration must also support non String types such as primitives, wrapper types, math types
+and date/time values. Basically each type that can be created from a String in more standardized way should
+supported. This should be even possible for types not known at build time of possible. Type conversion hereby
+should be flexible.
[[UCDynamicProvisioning]]
=== Dynamic Provisioning
@@ -155,67 +262,7 @@ to be configured is managed:
* If the lifecycle is managed by some container such as a DI container, the configuration
system should leverage the functionality of the container, where possible.
-The most simplest way is using injection, e.g. a POJO can be written as follows:
-
-[source, java]
-.Configured POJO Example
-----------------------------------------------------
-public class MyPojo {
- @ConfigProperty("myCurrency")
- @DefaultValue("CHF")
- private String currency;
-
- @ConfigProperty("myCurrencyRate")
- private Long currencyRate;
-
- // complex algorithm based on the currency
-}
-----------------------------------------------------
-
-The instance then can be passed for being configured:
-
-[source, java]
-.Configuring a POJO
-----------------------------------------------------
-MyPojo instance = new MyPojo();
-Configuration.configure(instance);
-----------------------------------------------------
-
-Another way of accessing configuration would be by defining a type safe template
-providing access to the configured values and let the configuration system implement
-the interface:
-
-[source, java]
-.Type Safe Configuration Template Example
-----------------------------------------------------
-public interface MyConfig {
- @ConfigProperty("myCurrency")
- @DefaultValue("CHF")
- String getCurrency();
-
- @ConfigProperty("myCurrencyRate")
- Long getCurrencyRate();
-
-}
-----------------------------------------------------
-
-The configuration system will then implement the
-interface using configuration as follows:
-
-[source, java]
-.Accessing a type safe Configuration Template
-----------------------------------------------------
-MyConfig config = Configuration.of(MyConfig.class);
-----------------------------------------------------
-
-Finally there is a generic +Configuration+ type that can be used as well, which
-provides full access to all features:
-[source, java]
-.Accessing Configuration
-----------------------------------------------------
-Configuration config = Configuration.of(Configuration.class);
-----------------------------------------------------
[[UCTesting]]
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/modules/managed/src/main/java/org/apache/tamaya/management/ManagedConfig.java
----------------------------------------------------------------------
diff --git a/modules/managed/src/main/java/org/apache/tamaya/management/ManagedConfig.java b/modules/managed/src/main/java/org/apache/tamaya/management/ManagedConfig.java
index 0f7c651..681c02b 100644
--- a/modules/managed/src/main/java/org/apache/tamaya/management/ManagedConfig.java
+++ b/modules/managed/src/main/java/org/apache/tamaya/management/ManagedConfig.java
@@ -19,7 +19,7 @@
package org.apache.tamaya.management;
import org.apache.tamaya.ConfigException;
-import org.apache.tamaya.core.properties.AggregationPolicy;
+import org.apache.tamaya.AggregationPolicy;
import java.util.Map;
import java.util.Set;
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/modules/managed/src/main/java/org/apache/tamaya/management/ManagedConfigMBean.java
----------------------------------------------------------------------
diff --git a/modules/managed/src/main/java/org/apache/tamaya/management/ManagedConfigMBean.java b/modules/managed/src/main/java/org/apache/tamaya/management/ManagedConfigMBean.java
index 5e57111..0555697 100644
--- a/modules/managed/src/main/java/org/apache/tamaya/management/ManagedConfigMBean.java
+++ b/modules/managed/src/main/java/org/apache/tamaya/management/ManagedConfigMBean.java
@@ -20,7 +20,7 @@ package org.apache.tamaya.management;
import org.apache.tamaya.ConfigException;
-import org.apache.tamaya.core.properties.AggregationPolicy;
+import org.apache.tamaya.AggregationPolicy;
import java.util.Map;
import java.util.Set;
[2/8] incubator-tamaya git commit: TAMAYA-14: added resource support.
TAMAYA-15: Moved PropertyProviders to API,
added SPI. TAMAYA-8: Added/improved Javadoc.
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/properties/FilteredPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/FilteredPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/properties/FilteredPropertyProvider.java
deleted file mode 100644
index bf6c9e2..0000000
--- a/core/src/main/java/org/apache/tamaya/core/properties/FilteredPropertyProvider.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.tamaya.core.properties;
-
-import org.apache.tamaya.ConfigChangeSet;
-import org.apache.tamaya.MetaInfoBuilder;
-import org.apache.tamaya.PropertyProvider;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-import java.util.function.Predicate;
-
-class FilteredPropertyProvider extends AbstractPropertyProvider{
-
- private static final long serialVersionUID = 4301042530074932562L;
- private PropertyProvider unit;
- private Predicate<String> filter;
-
- public FilteredPropertyProvider(PropertyProvider configuration, Predicate<String> filter){
- super(MetaInfoBuilder.of(configuration.getMetaInfo()).setType("filtered").set("filter", filter.toString()).build());
- Objects.requireNonNull(configuration);
- this.unit = configuration;
- this.filter = filter;
- }
-
- @Override
- public Map<String,String> toMap(){
- final Map<String,String> result = new HashMap<>();
- this.unit.toMap().entrySet().forEach(e -> {
- if(filter.test(e.getKey())){
- result.put(e.getKey(), e.getValue());
- }
- });
- return result;
- }
-
- @Override
- public ConfigChangeSet load(){
- unit.load();
- return super.load();
- }
-
- /**
- * Apply a config change to this item. Hereby the change must be related to the same instance.
- * @param change the config change
- * @throws org.apache.tamaya.ConfigException if an unrelated change was passed.
- * @throws UnsupportedOperationException when the configuration is not writable.
- */
- @Override
- public void apply(ConfigChangeSet change){
- this.unit.apply(change);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/properties/FreezedPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/FreezedPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/properties/FreezedPropertyProvider.java
deleted file mode 100644
index 83b230e..0000000
--- a/core/src/main/java/org/apache/tamaya/core/properties/FreezedPropertyProvider.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.tamaya.core.properties;
-
-import org.apache.tamaya.ConfigChangeSet;
-import org.apache.tamaya.MetaInfo;
-import org.apache.tamaya.MetaInfoBuilder;
-import org.apache.tamaya.PropertyProvider;
-
-import java.io.Serializable;
-import java.time.Instant;
-import java.util.*;
-
-/**
- * This class models a freezed instance of an {@link org.apache.tamaya.PropertyProvider}.
- * Created by Anatole on 28.03.14.
- */
-final class FreezedPropertyProvider implements PropertyProvider, Serializable{
-
- private static final long serialVersionUID = 3365413090311267088L;
- private Map<String,Map<String,String>> fieldMMetaInfo = new HashMap<>();
- private MetaInfo metaInfo;
- private Map<String,String> properties = new HashMap<>();
-
- private FreezedPropertyProvider(PropertyProvider propertyMap){
- Map<String,String> map = propertyMap.toMap();
- this.properties.putAll(map);
- this.properties = Collections.unmodifiableMap(this.properties);
- this.metaInfo =
- MetaInfoBuilder.of(propertyMap.getMetaInfo()).set("freezedAt", Instant.now().toString()).build();
- }
-
- public static PropertyProvider of(PropertyProvider propertyProvider){
- if(propertyProvider instanceof FreezedPropertyProvider){
- return propertyProvider;
- }
- return new FreezedPropertyProvider(propertyProvider);
- }
-
- @Override
- public ConfigChangeSet load(){
- return ConfigChangeSet.emptyChangeSet(this);
- }
-
- public int size(){
- return properties.size();
- }
-
- public boolean isEmpty(){
- return properties.isEmpty();
- }
-
- @Override
- public boolean containsKey(String key){
- return properties.containsKey(key);
- }
-
- @Override
- public Map<String,String> toMap(){
- return Collections.unmodifiableMap(this.properties);
- }
-
- @Override
- public MetaInfo getMetaInfo(){
- return this.metaInfo;
- }
-
- @Override
- public Optional<String> get(String key){
- return Optional.ofNullable(properties.get(key));
- }
-
- @Override
- public Set<String> keySet(){
- return properties.keySet();
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/properties/IntersectingPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/IntersectingPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/properties/IntersectingPropertyProvider.java
deleted file mode 100644
index 6f98a4b..0000000
--- a/core/src/main/java/org/apache/tamaya/core/properties/IntersectingPropertyProvider.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.tamaya.core.properties;
-
-import org.apache.tamaya.MetaInfo;
-import org.apache.tamaya.MetaInfoBuilder;
-import org.apache.tamaya.PropertyProvider;
-
-import java.util.*;
-import java.util.stream.Collectors;
-
-/**
- * Created by Anatole on 22.10.2014.
- */
-class IntersectingPropertyProvider extends AbstractPropertyProvider {
-
- private Collection<PropertyProvider> providers;
- private PropertyProvider union;
-
- public IntersectingPropertyProvider(AggregationPolicy policy, PropertyProvider... providers) {
- super(MetaInfoBuilder.of().setType("intersection").set("providers", Arrays.toString(providers)).build());
- this.providers = Arrays.asList(Objects.requireNonNull(providers));
- union = PropertyProviders.union(policy, providers);
- }
-
- public IntersectingPropertyProvider(MetaInfo metaInfo, AggregationPolicy policy, PropertyProvider... providers) {
- super(metaInfo);
- this.providers = Arrays.asList(Objects.requireNonNull(providers));
- union = PropertyProviders.union(policy, providers);
- }
-
-
- @Override
- public Optional<String> get(String key) {
- if (containsKey(key))
- return union.get(key);
- return Optional.empty();
- }
-
- private boolean filter(Map.Entry<String, String> entry) {
- return containsKey(entry.getKey());
- }
-
- @Override
- public boolean containsKey(String key) {
- for (PropertyProvider prov : this.providers) {
- if (!prov.containsKey(key)) {
- return false;
- }
- }
- return true;
- }
-
- @Override
- public Map<String, String> toMap() {
- return union.toMap().entrySet().stream().filter(en -> containsKey(en.getKey())).collect(
- Collectors.toConcurrentMap(en -> en.getKey(), en -> en.getValue()));
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/properties/MapBasedPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/MapBasedPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/properties/MapBasedPropertyProvider.java
deleted file mode 100644
index 83ba5bb..0000000
--- a/core/src/main/java/org/apache/tamaya/core/properties/MapBasedPropertyProvider.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.tamaya.core.properties;
-
-import org.apache.tamaya.ConfigChangeSet;
-import org.apache.tamaya.MetaInfo;
-
-import java.beans.PropertyChangeEvent;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.logging.Logger;
-
-/**
- * Models a {@link org.apache.tamaya.PropertyProvider} that can be build using a builder pattern.
- */
-class MapBasedPropertyProvider extends AbstractPropertyProvider{
-
- private static final long serialVersionUID = 7601389831472839249L;
-
- private static final Logger LOG = Logger.getLogger(MapBasedPropertyProvider.class.getName());
- /**
- * The unit's entries.
- */
- private Map<String,String> entries = new ConcurrentHashMap<>();
-
- /**
- * Constructor used by {@link MapBasedPropertyProviderBuilder}, or subclasses.
- *
- * @param entries the config entries, not null.
- */
- MapBasedPropertyProvider(MetaInfo metaInfo, Map<String,String> entries){
- super(metaInfo);
- Objects.requireNonNull(entries, "entries required.");
- this.entries.putAll(entries);
- }
-
-
- /**
- * Constructor used by {@link MapBasedPropertyProviderBuilder}, or subclasses.
- *
- * @param entries the entries
- * @param sources the sources
- * @param errors the errors
- */
- MapBasedPropertyProvider(MetaInfo metaInfo, Map<String,String> entries, Set<String> sources,
- Collection<Throwable> errors){
- super(metaInfo);
- Objects.requireNonNull(entries, "entries required.");
- this.entries.putAll(entries);
- addSources(sources);
- }
-
- MapBasedPropertyProvider(MetaInfo metaInfo, Set<String> sources){
- super(metaInfo);
- addSources(sources);
- }
-
- @Override
- public Map<String, String> toMap() {
- return new HashMap<>(this.entries);
- }
-
- @Override
- public ConfigChangeSet load(){
- // Can not reload...
- return ConfigChangeSet.emptyChangeSet(this);
- }
-
- /**
- * Apply a config change to this item. Hereby the change must be related to the same instance.
- * @param change the config change
- * @throws org.apache.tamaya.ConfigException if an unrelated change was passed.
- * @throws UnsupportedOperationException when the configuration is not writable.
- */
- @Override
- public void apply(ConfigChangeSet change){
- change.getEvents().forEach(this::applyChange);
- }
-
- private void applyChange(PropertyChangeEvent propertyChangeEvent) {
- LOG.finest(() -> "Applying change to map provider: " + propertyChangeEvent);
- if(propertyChangeEvent.getNewValue()==null){
- this.entries.remove(propertyChangeEvent.getPropertyName());
- }
- else{
- this.entries.put(propertyChangeEvent.getPropertyName(), propertyChangeEvent.getNewValue().toString());
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/properties/MapBasedPropertyProviderBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/MapBasedPropertyProviderBuilder.java b/core/src/main/java/org/apache/tamaya/core/properties/MapBasedPropertyProviderBuilder.java
index 38c41bd..ec67721 100644
--- a/core/src/main/java/org/apache/tamaya/core/properties/MapBasedPropertyProviderBuilder.java
+++ b/core/src/main/java/org/apache/tamaya/core/properties/MapBasedPropertyProviderBuilder.java
@@ -21,11 +21,13 @@ package org.apache.tamaya.core.properties;
import org.apache.tamaya.MetaInfo;
import org.apache.tamaya.MetaInfoBuilder;
import org.apache.tamaya.PropertyProvider;
+import org.apache.tamaya.PropertyProviders;
+
import java.util.*;
/**
* Builder class for creating a new instance of
- * {@link MapBasedPropertyProvider}-
+ * {@link org.apache.tamaya.core.internal.MapBasedPropertyProvider}-
*/
public final class MapBasedPropertyProviderBuilder {
protected MetaInfoBuilder metaInfoBuilder = MetaInfoBuilder.of();
@@ -106,8 +108,8 @@ public final class MapBasedPropertyProviderBuilder {
}
public PropertyProvider build(){
- MetaInfo finalMetaInfo = metaInfoBuilder.setTimestamp(configReadDT).build();
- return new MapBasedPropertyProvider(finalMetaInfo, entries);
+ MetaInfo metaInfo = metaInfoBuilder.setTimestamp(configReadDT).build();
+ return PropertyProviders.fromMap(metaInfo, entries);
}
/*
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/properties/PathBasedPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/PathBasedPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/properties/PathBasedPropertyProvider.java
deleted file mode 100644
index b929c30..0000000
--- a/core/src/main/java/org/apache/tamaya/core/properties/PathBasedPropertyProvider.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.tamaya.core.properties;
-
-import org.apache.tamaya.MetaInfo;
-import org.apache.tamaya.MetaInfoBuilder;
-import org.apache.tamaya.PropertyProvider;
-import org.apache.tamaya.core.config.ConfigurationFormats;
-import org.apache.tamaya.spi.Bootstrap;
-import org.apache.tamaya.core.spi.ConfigurationFormat;
-import org.apache.tamaya.core.spi.ResourceLoader;
-
-import java.net.URI;
-import java.util.*;
-
-/**
- * Created by Anatole on 16.10.2014.
- */
-final class PathBasedPropertyProvider extends AbstractPropertyProvider {
-
- private List<String> paths = new ArrayList<>();
- private Map<String,String> properties = new HashMap<>();
-
- public PathBasedPropertyProvider(MetaInfo metaInfo, Collection<String> paths) {
- super(metaInfo);
- Objects.requireNonNull(paths);
- this.paths.addAll(paths);
- init();
- }
-
- @Override
- public Map<String, String> toMap() {
- return this.properties;
- }
-
- private void init() {
- List<String> sources = new ArrayList<>();
- List<String> effectivePaths = new ArrayList<>();
- paths.forEach((path) -> {
- effectivePaths.add(path);
- for(URI uri : Bootstrap.getService(ResourceLoader.class).getResources(path)){
- ConfigurationFormat format = ConfigurationFormats.getFormat(uri);
- if(format != null){
- try{
- properties.putAll(format.readConfiguration(uri));
- sources.add(uri.toString());
- }
- catch(Exception e){
- e.printStackTrace();
- }
- }
- }
- });
- MetaInfoBuilder metaInfoBuilder = MetaInfoBuilder.of(getMetaInfo());
- super.metaInfo = metaInfoBuilder.setSourceExpressions(new String[effectivePaths.size()])
- .set("sources", sources.toString()).build();
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/properties/PropertyProviderManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/PropertyProviderManager.java b/core/src/main/java/org/apache/tamaya/core/properties/PropertyProviderManager.java
index d48b637..1e3d567 100644
--- a/core/src/main/java/org/apache/tamaya/core/properties/PropertyProviderManager.java
+++ b/core/src/main/java/org/apache/tamaya/core/properties/PropertyProviderManager.java
@@ -24,7 +24,7 @@ import java.util.Collection;
/**
* Service for accessing configuration. A configuration service is always base
* on the environment definition provided by one instance of
- * {@link org.apache.tamaya.core.spi.EnvironmentManagerSingletonSpi}. It is possible to define multiple
+ * {@link org.apache.tamaya.spi.EnvironmentManagerSingletonSpi}. It is possible to define multiple
* {@link org.apache.tamaya.core.properties.PropertyProviderManager} instances, if required. <h3>Implementation
* PropertyMapSpec</h3> Implementations of this interface must be
* <ul>
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/properties/PropertyProviders.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/PropertyProviders.java b/core/src/main/java/org/apache/tamaya/core/properties/PropertyProviders.java
deleted file mode 100644
index 7a8649b..0000000
--- a/core/src/main/java/org/apache/tamaya/core/properties/PropertyProviders.java
+++ /dev/null
@@ -1,457 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.tamaya.core.properties;
-
-import org.apache.tamaya.MetaInfo;
-import org.apache.tamaya.MetaInfoBuilder;
-import org.apache.tamaya.PropertyProvider;
-
-import java.net.URI;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Predicate;
-import java.util.function.Supplier;
-import java.util.logging.Logger;
-
-/**
- * Accessor factory for several standard {@link org.apache.tamaya.PropertyProvider} instances, usable for creating {@code Configuration}
- * parts.
- */
-public final class PropertyProviders {
-
- private static final PropertyProvider EMPTY_PROPERTYPROVIDER = from(Collections.emptyMap());
- private static final PropertyProvider ENV_PROPERTYPROVIDER = new EnvironmentPropertyProvider();
-
- private static final Logger LOG = Logger.getLogger(PropertyProviders.class.getName());
-
- /**
- * Private singleton constructor.
- */
- private PropertyProviders() {
- }
-
- /**
- * Creates a new {@link }PropertyMap} using the given command line arguments
- *
- * @param args the command line arguments, not null.
- * @return a new {@link }PropertyMap} instance with the given arguments contained as properties.
- */
- public static PropertyProvider fromArgs(String... args) {
- return fromArgs(MetaInfo.of("Built from Args"), args);
- }
-
- /**
- * Creates a new {@link }PropertyMap} using the given command line arguments
- *
- * @param metaInfo the meta information to be provided additionally.
- * @param args the command line arguments, not null.
- * @return a new {@link }PropertyMap} instance with the given arguments contained as properties.
- */
- public static PropertyProvider fromArgs(MetaInfo metaInfo, String... args) {
- Objects.requireNonNull(metaInfo);
- // TODO read the CLI with some better library, e.g. move parsing service to ext. service SPI
- Map<String, String> properties = new HashMap<>();
- for (int base = 0; base < args.length; base++) {
- if (args[base].startsWith("--")) {
- String argKey = args[base].substring(2);
- String value = "true"; // flag only
- if (base != args.length - 1) {
- if (args[base + 1].startsWith("-")) {
- base++;
- int eqIndex = argKey.indexOf('=');
- if (eqIndex > 0) {
- value = argKey.substring(eqIndex + 1);
- argKey = argKey.substring(0, eqIndex);
- }
- } else {
- value = args[base + 1];
- base += 2;
- }
- }
- properties.put(argKey, value);
- } else if (args[base].startsWith("-")) {
- String argKey = args[base].substring(1);
- String value = "true"; // flag only
- if (base != args.length - 1) {
- if (args[base + 1].startsWith("-")) {
- base++;
- int eqIndex = argKey.indexOf('=');
- if (eqIndex > 0) {
- value = argKey.substring(eqIndex + 1);
- argKey = argKey.substring(0, eqIndex);
- }
- } else {
- value = args[base + 1];
- base += 2;
- }
- }
- properties.put(argKey, value);
- }
- }
- return from(metaInfo, properties);
- }
-
- /**
- * Creates a new read-only {@link org.apache.tamaya.PropertyProvider} by reading the according path resources. The effective resources read
- * hereby are determined by the {@code PathResolverService} configured into the {@code Bootstrap} SPI.
- * Properties read from resources evaluated on
- * paths with lower order are overriding any duplicate values from previous paths hereby.
- *
- * @param paths the paths to be resolved by the {@code PathResolverService} , not null.
- * @return a new {@link }PropertyMap} instance with the given paths contained as properties.
- */
- public static PropertyProvider fromPaths(String... paths) {
- return fromPaths(MetaInfo.of("Built from Paths"), paths);
- }
-
-
- /**
- * Creates a new read-only {@link org.apache.tamaya.PropertyProvider} by reading the according path resources. The effective resources read
- * hereby are determined by the {@code PathResolverService} configured into the {@code Bootstrap} SPI.
- * Properties read from resources evaluated on
- * paths with lower order are overriding any duplicate values from previous paths hereby.
- *
- * @param paths the paths to be resolved by the {@code PathResolverService} , not null.
- * @return a new {@link }PropertyMap} instance with the given paths contained as properties.
- */
- public static PropertyProvider fromPaths(Collection<String> paths) {
- return fromPaths(MetaInfo.of("Built from Paths"), paths);
- }
-
- /**
- * Creates a new read-only {@link org.apache.tamaya.PropertyProvider} by reading the according path resources. The effective resources read
- * hereby are determined by the {@code PathResolverService} configured into the {@code Bootstrap} SPI.
- * Properties read from resources evaluated on
- * paths with lower order are overriding any duplicate values from previous paths hereby.
- *
- * @param metaInfo the meat information to be provided additionally.
- * @param paths the paths to be resolved by the {@code PathResolverService} , not null.
- * @return a new {@link }PropertyMap} instance with the given paths contained as properties.
- */
- public static PropertyProvider fromPaths(MetaInfo metaInfo, String... paths) {
- return fromPaths(metaInfo, Arrays.asList(paths));
- }
-
- /**
- * Creates a new read-only {@link org.apache.tamaya.PropertyProvider} by reading the according path resources. The effective resources read
- * hereby are determined by the {@code PathResolverService} configured into the {@code Bootstrap} SPI.
- * Properties read from resources evaluated on
- * paths with lower order are overriding any duplicate values from previous paths hereby.
- *
- * @param metaInfo the meat information to be provided additionally.
- * @param paths the paths to be resolved by the {@code PathResolverService} , not null.
- * @return a new {@link }PropertyMap} instance with the given paths contained as properties.
- */
- public static PropertyProvider fromPaths(MetaInfo metaInfo, Collection<String> paths) {
- return new PathBasedPropertyProvider(metaInfo, paths);
- }
-
- /**
- * Creates a new read-only {@link org.apache.tamaya.PropertyProvider} based on the resources defined by the given paths. The effective resources
- * read hereby are determined by the {@code PathResolverService} configured into the {@code Bootstrap} SPI.
- *
- * @param uris the uris to be read, not null.
- * @return a new {@link }PropertyMap} instance based on the given paths/resources found.
- */
- public static PropertyProvider fromUris(URI... uris) {
- return fromUris(MetaInfo.of("Built from URIs"), uris);
- }
-
- /**
- * Creates a new read-only {@link org.apache.tamaya.PropertyProvider} based on the resources defined by the given paths. The effective resources
- * read hereby are determined by the {@code PathResolverService} configured into the {@code Bootstrap} SPI.
- *
- * @param uris the uris to be read, not null.
- * @return a new {@link }PropertyMap} instance based on the given paths/resources found.
- */
- public static PropertyProvider fromUris(Collection<URI> uris) {
- return fromUris(MetaInfo.of("Built from URIs"), uris);
- }
-
- /**
- * Creates a new read-only {@link org.apache.tamaya.PropertyProvider} based on the resources defined by the given paths. The effective resources
- * read hereby are determined by the {@code PathResolverService} configured into the {@code Bootstrap} SPI.
- *
- * @param metaInfo the meat information to be provided additionally.
- * @param uris the uris to be read, not null.
- * @return a new {@link }PropertyMap} instance based on the given paths/resources found.
- */
- public static PropertyProvider fromUris(MetaInfo metaInfo, URI... uris) {
- Objects.requireNonNull(metaInfo);
- return fromUris(metaInfo, Arrays.asList(uris));
- }
-
- /**
- * Creates a new read-only {@link org.apache.tamaya.PropertyProvider} based on the resources defined by the given paths. The effective resources
- * read hereby are determined by the {@code PathResolverService} configured into the {@code Bootstrap} SPI.
- *
- * @param metaInfo the meat information to be provided additionally.
- * @param uris the uris to be read, not null.
- * @return a new {@link }PropertyMap} instance based on the given paths/resources found.
- */
- public static PropertyProvider fromUris(MetaInfo metaInfo, Collection<URI> uris) {
- return new URIBasedPropertyProvider(metaInfo, uris);
- }
-
- /**
- * Creates a new read-only {@link PropertyProvider} by using the given Map.
- *
- * @param map the properties to be included, not null.
- * @return a new {@link }PropertyMap} instance with the given properties from the Map instance passed.
- */
- public static PropertyProvider from(Map<String, String> map) {
- return from(MetaInfo.of("Built from Map"), map);
- }
-
-
- /**
- * Creates a new read-only {@link PropertyProvider} by using the given Map.
- *
- * @param metaInfo the meat information to be provided additionally.
- * @param map the properties to be included, not null.
- * @return a new {@link }PropertyMap} instance with the given properties from the Map instance passed.
- */
- public static PropertyProvider from(MetaInfo metaInfo, Map<String, String> map) {
- return new MapBasedPropertyProvider(metaInfo, map);
- }
-
- /**
- * Create meta information for CLI arguments passed.
- *
- * @param args the CLI arguments, not null.
- * @return the corresponding meta information.
- */
- public static MetaInfo createArgsMetaInfo(String... args) {
- MetaInfoBuilder metaBuilder = MetaInfoBuilder.of();
- return metaBuilder.setType("cli").set("args", Arrays.toString(args)).build();
- }
-
-
- /**
- * Get an empty and immutable PropertyProvider instance.
- *
- * @return an empty and immutable PropertyProvider instance, never null.
- */
- public static PropertyProvider empty() {
- return EMPTY_PROPERTYPROVIDER;
- }
-
- /**
- * Get an empty and immutable PropertyProvider instance.
- *
- * @return an empty and mutable PropertyProvider instance, never null.
- */
- public static PropertyProvider emptyMutable() {
- return PropertyProviders.from(new ConcurrentHashMap<>());
- }
-
- /**
- * Get an empty and immutable PropertyProvider instance. The meta-information contains the given String
- * under the key 'info'.
- *
- * @return an empty and immutable PropertyProvider instance, never null, with the given Strings as info meta-data..
- */
- public static PropertyProvider empty(MetaInfo metaInfo) {
- return from(metaInfo, Collections.emptyMap());
- }
-
- /**
- * Get an empty and mutable PropertyProvider instance. The meta-information contains the given String
- * under the key 'info'.
- *
- * @return an empty and immutable PropertyProvider instance, never null, with the given Strings as info meta-data..
- */
- public static PropertyProvider emptyMutable(MetaInfo metaInfo) {
- return from(metaInfo, new ConcurrentHashMap<>());
- }
-
- /**
- * Returns a read-only {@link PropertyProvider} reflecting the current runtime environment properties.
- *
- * @return a new read-only {@link PropertyProvider} instance based on the current runtime environment properties.
- */
- public static PropertyProvider fromEnvironmentProperties() {
- return ENV_PROPERTYPROVIDER;
- }
-
- /**
- * Creates a new read-only {@link PropertyProvider} reflecting the current system properties.
- *
- * @return a new read-only {@link PropertyProvider} instance based on the current system properties.
- */
- public static PropertyProvider fromSystemProperties() {
- return new SystemPropertiesPropertyProvider();
- }
-
- /**
- * Converts a given {@link org.apache.tamaya.PropertyProvider} instance into a serializable and immutable form,
- * so it can be sent over a network connection.
- *
- * @param provider the PropertyProvider to be freezed.
- * @return the serializable instance.
- */
- public static PropertyProvider freezed(PropertyProvider provider) {
- return FreezedPropertyProvider.of(provider);
- }
-
- /**
- * Creates a new {@link PropertyProvider} containing all property maps given, hereby later maps in the array override
- * properties from previous instances.
- *
- * @param propertyMaps the maps to be included, not null.
- * @return the union instance containing all given maps.
- */
- public static PropertyProvider union(PropertyProvider... propertyMaps) {
- return union(AggregationPolicy.OVERRIDE, propertyMaps);
- }
-
- /**
- * Creates a new {@link PropertyProvider} containing all property maps given, hereby using the given AggregationPolicy.
- *
- * @param policy the AggregationPolicy to be used, not null.
- * @param propertyMaps the maps to be included, not null.
- * @return the aggregated instance containing all given maps.
- */
- public static PropertyProvider union(AggregationPolicy policy, PropertyProvider... propertyMaps) {
- return new AggregatedPropertyProvider(null, policy, propertyMaps);
- }
-
- /**
- * Creates a new {@link PropertyProvider} that is mutable by adding a map based instance that overrides
- * values from the original map.
- * @param provider the provider to be made mutable, not null.
- * @return the mutable instance.
- */
- public static PropertyProvider mutable(PropertyProvider provider) {
- PropertyProvider mutableProvider = PropertyProviders.emptyMutable();
- return mutableUnion(mutableProvider, AggregationPolicy.OVERRIDE, provider, mutableProvider);
- }
-
- /**
- * Creates a new {@link PropertyProvider} containing all property maps given, hereby later maps in the array override
- * properties from previous instances.
- * @param mutableProvider the provider used for delegating change requests.
- * @param propertyMaps the maps to be included, not null.
- * @return the union instance containing all given maps.
- */
- public static PropertyProvider mutableUnion(PropertyProvider mutableProvider, PropertyProvider... propertyMaps) {
- return mutableUnion(mutableProvider, AggregationPolicy.OVERRIDE, propertyMaps);
- }
-
- /**
- * Creates a new {@link PropertyProvider} containing all property maps given, hereby using the given AggregationPolicy.
- * @param mutableProvider the provider used for delegating change requests.
- * @param policy the AggregationPolicy to be used, not null.
- * @param propertyMaps the maps to be included, not null.
- * @return the aggregated instance containing all given maps.
- */
- public static PropertyProvider mutableUnion(PropertyProvider mutableProvider, AggregationPolicy policy, PropertyProvider... propertyMaps) {
- return new AggregatedPropertyProvider(mutableProvider, policy, propertyMaps);
- }
-
- /**
- * Creates a new {@link PropertyProvider} containing only properties that are shared by all given maps,
- * hereby later maps in the array override properties from previous instances.
- *
- * @param propertyMaps the maps to be included, not null.
- * @return the intersecting instance containing all given maps.
- */
- public static PropertyProvider intersected(AggregationPolicy policy, PropertyProvider... propertyMaps) {
- return new IntersectingPropertyProvider(policy, propertyMaps);
- }
-
- /**
- * Creates a new {@link PropertyProvider} containing only properties that are shared by all given maps,
- * hereby later maps in the array override properties from previous instances.
- *
- * @param propertyMaps the maps to be included, not null.
- * @return the intersecting instance containing all given maps.
- */
- public static PropertyProvider intersected(PropertyProvider... propertyMaps) {
- return new IntersectingPropertyProvider(AggregationPolicy.OVERRIDE, propertyMaps);
- }
-
- /**
- * Creates a new {@link PropertyProvider} containing only properties from the target instance, that are not contained
- * in one of the other maps passed.
- *
- * @param target the base map, not null.
- * @param subtrahendSets the maps to be subtracted, not null.
- * @return the intersecting instance containing all given maps.
- */
- public static PropertyProvider subtracted(PropertyProvider target, PropertyProvider... subtrahendSets) {
- return new SubtractingPropertyProvider(target, subtrahendSets);
- }
-
-
- /**
- * Creates a filtered {@link PropertyProvider} (a view) of a given base {@link }PropertyMap}. The filter hereby is
- * applied dynamically on access, so also runtime changes of the base map are reflected appropriately.
- *
- * @param propertyMap the base map instance, not null.
- * @param filter the filtger to be applied, not null.
- * @return the new filtering instance.
- */
- public static PropertyProvider filtered(Predicate<String> filter, PropertyProvider propertyMap) {
- return new FilteredPropertyProvider(propertyMap, filter);
- }
-
- /**
- * Creates a new contextual {@link PropertyProvider}. Contextual maps delegate to different instances of PropertyMap depending
- * on the keys returned from the isolationP
- *
- * @param mapSupplier the supplier creating new provider instances
- * @param isolationKeySupplier the supplier providing contextual keys based on the current environment.
- */
- public static PropertyProvider contextual(Supplier<PropertyProvider> mapSupplier,
- Supplier<String> isolationKeySupplier) {
- return new ContextualPropertyProvider(mapSupplier, isolationKeySupplier);
- }
-
-
- /**
- * Creates a filtered {@link PropertyProvider} (a view) of a given base {@link }PropertyMap}. The filter hereby is
- * applied dynamically on access, so also runtime changes of the base map are reflected appropriately.
- *
- * @param mainMap the main map instance, not null.
- * @param parentMap the delegated parent map instance, not null.
- * @return the new delegating instance.
- */
- public static PropertyProvider delegating(PropertyProvider mainMap, Map<String, String> parentMap) {
- return new DelegatingPropertyProvider(mainMap, parentMap);
- }
-
- /**
- * Creates a {@link org.apache.tamaya.PropertyProvider} where all keys of a current map,
- * existing in another map are replaced
- * with the ones from the other {@link org.apache.tamaya.PropertyProvider}. The filter hereby is
- * applied dynamically on access, so also runtime changes of the base map are reflected appropriately.
- * Keys not existing in the {@code mainMap}, but present in {@code replacementMao} will be hidden.
- *
- * @param mainMap the main map instance, which keys, present in {@code replacementMap} will be replaced
- * with the ones
- * in {@code replacementMap}, not null.
- * @param replacementMap the map instance, that will replace all corresponding entries in {@code mainMap}, not null.
- * @return the new delegating instance.
- */
- public static PropertyProvider replacing(PropertyProvider mainMap, Map<String, String> replacementMap) {
- return new ReplacingPropertyProvider(mainMap, replacementMap);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/properties/ReplacingPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/ReplacingPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/properties/ReplacingPropertyProvider.java
deleted file mode 100644
index 7eb69fa..0000000
--- a/core/src/main/java/org/apache/tamaya/core/properties/ReplacingPropertyProvider.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.tamaya.core.properties;
-
-import org.apache.tamaya.ConfigChangeSet;
-import org.apache.tamaya.MetaInfo;
-import org.apache.tamaya.MetaInfoBuilder;
-import org.apache.tamaya.PropertyProvider;
-
-import java.util.*;
-
-/**
- * Implementation for a {@link org.apache.tamaya.PropertyProvider} that is an aggregate of
- * multiple child instances, where all existing key/values in a replacementMap will
- * replace values in a main map, if present there.
- */
-class ReplacingPropertyProvider implements PropertyProvider{
-
- private static final long serialVersionUID = -1419376385695224799L;
- private PropertyProvider mainMap;
- private Map<String,String> replacingMap;
- private MetaInfo metaInfo;
-
- /**
- * Creates a mew instance, with aggregation polilcy
- * {@code AggregationPolicy.OVERRIDE}.
- *
- * @param mainMap The main ConfigMap.
- * @param replacingMap The replacing ConfigMap.
- */
- public ReplacingPropertyProvider(PropertyProvider mainMap, Map<String,String> replacingMap){
- Objects.requireNonNull(replacingMap);
- this.replacingMap = replacingMap;
- Objects.requireNonNull(mainMap);
- this.mainMap = mainMap;
- this.metaInfo = MetaInfoBuilder.of().setType("replacing").set("mainProvider", mainMap.toString())
- .set("replacing", replacingMap.toString()).build();
- }
-
- @Override
- public ConfigChangeSet load(){
- return mainMap.load();
- }
-
- @Override
- public boolean containsKey(String key){
- return mainMap.containsKey(key);
- }
-
- @Override
- public Map<String,String> toMap(){
- Map<String,String> result = new HashMap<>(replacingMap);
- for(Map.Entry<String,String> en : mainMap.toMap().entrySet()){
- if(!replacingMap.containsKey(en.getKey())){
- result.put(en.getKey(), en.getValue());
- }
- }
- return result;
- }
-
- @Override
- public MetaInfo getMetaInfo(){
- return this.metaInfo;
- }
-
- @Override
- public Optional<String> get(String key){
- String val = replacingMap.get(key);
- if(val == null){
- return mainMap.get(key);
- }
- return Optional.ofNullable(val);
- }
-
- @Override
- public Set<String> keySet(){
- return mainMap.keySet();
- }
-
- /**
- * Apply a config change to this item. Hereby the change must be related to the same instance.
- * @param change the config change
- * @throws org.apache.tamaya.ConfigException if an unrelated change was passed.
- * @throws UnsupportedOperationException when the configuration is not writable.
- */
- @Override
- public void apply(ConfigChangeSet change){
- this.mainMap.apply(change);
- }
-
- @Override
- public String toString(){
- return super.toString() + "(mainMap=" + mainMap + ", replacingMap=" + replacingMap + ")";
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/properties/Store.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/Store.java b/core/src/main/java/org/apache/tamaya/core/properties/Store.java
index 711824e..0c1d775 100644
--- a/core/src/main/java/org/apache/tamaya/core/properties/Store.java
+++ b/core/src/main/java/org/apache/tamaya/core/properties/Store.java
@@ -23,7 +23,7 @@ import java.util.*;
/**
* This store encapsulates an list of WeakReferences to items.
- * It cleans up the list from null references, when an item is removed, or an Iterator is created.
+ * It cleans up the list fromMap null references, when an item is removed, or an Iterator is created.
* Created by Anatole on 10.04.2014.
*/
public class Store<T>implements Iterable<T> {
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/properties/SubtractingPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/SubtractingPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/properties/SubtractingPropertyProvider.java
deleted file mode 100644
index 0ecb837..0000000
--- a/core/src/main/java/org/apache/tamaya/core/properties/SubtractingPropertyProvider.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.tamaya.core.properties;
-
-import org.apache.tamaya.ConfigChangeSet;
-import org.apache.tamaya.MetaInfoBuilder;
-import org.apache.tamaya.PropertyProvider;
-
-import java.util.*;
-import java.util.stream.Collectors;
-
-class SubtractingPropertyProvider extends AbstractPropertyProvider{
-
- private static final long serialVersionUID = 4301042530074932562L;
- private PropertyProvider unit;
- private Collection<PropertyProvider> subtrahends;
-
- public SubtractingPropertyProvider(PropertyProvider configuration, PropertyProvider... subtrahends){
- super(MetaInfoBuilder.of(configuration.getMetaInfo()).setType("sutracted").set("subtrahends", Arrays.toString(subtrahends)).build());
- Objects.requireNonNull(configuration);
- this.unit = configuration;
- this.subtrahends = Arrays.asList(subtrahends);
- }
-
- private boolean filter(Map.Entry<String,String> entry){
- for(PropertyProvider prov: subtrahends){
- if(prov.containsKey(entry.getKey())){
- return false;
- }
- }
- return true;
- }
-
- @Override
- public Map<String,String> toMap(){
- return this.unit.toMap().entrySet().stream().filter(this::filter).collect(Collectors.toMap(
- (en) -> en.getKey(),
- (en) -> en.getValue()
- ));
- }
-
- @Override
- public ConfigChangeSet load(){
- unit.load();
- return super.load();
- }
-
- /**
- * Apply a config change to this item. Hereby the change must be related to the same instance.
- * @param change the config change
- * @throws org.apache.tamaya.ConfigException if an unrelated change was passed.
- * @throws UnsupportedOperationException when the configuration is not writable.
- */
- @Override
- public void apply(ConfigChangeSet change){
- this.unit.apply(change);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/properties/SystemPropertiesPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/SystemPropertiesPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/properties/SystemPropertiesPropertyProvider.java
deleted file mode 100644
index 8f186be..0000000
--- a/core/src/main/java/org/apache/tamaya/core/properties/SystemPropertiesPropertyProvider.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.tamaya.core.properties;
-
-import org.apache.tamaya.MetaInfoBuilder;
-import org.apache.tamaya.core.env.ConfiguredSystemProperties;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-
-class SystemPropertiesPropertyProvider extends AbstractPropertyProvider{
-
-
- private static final long serialVersionUID = -5935940312707001199L;
-
- public SystemPropertiesPropertyProvider(){
- super(MetaInfoBuilder.of().setType("sys-properties").build());
- }
-
- @Override
- public Map<String,String> toMap(){
- Properties sysProps = System.getProperties();
- if(sysProps instanceof ConfiguredSystemProperties){
- sysProps = ((ConfiguredSystemProperties)sysProps).getInitialProperties();
- }
- Map<String,String> props = new HashMap<>();
- for (Map.Entry<Object,Object> en : sysProps.entrySet()) {
- props.put(en.getKey().toString(), en.getValue().toString());
- }
- return Collections.unmodifiableMap(props);
- }
-
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/properties/UriBasedPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/UriBasedPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/properties/UriBasedPropertyProvider.java
deleted file mode 100644
index 0e81769..0000000
--- a/core/src/main/java/org/apache/tamaya/core/properties/UriBasedPropertyProvider.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.tamaya.core.properties;
-
-import org.apache.tamaya.MetaInfo;
-import org.apache.tamaya.MetaInfoBuilder;
-import org.apache.tamaya.core.config.ConfigurationFormats;
-import org.apache.tamaya.spi.Bootstrap;
-import org.apache.tamaya.core.spi.ConfigurationFormat;
-
-import java.net.URI;
-import java.util.*;
-
-/**
- * Created by Anatole on 16.10.2014.
- */
-final class URIBasedPropertyProvider extends AbstractPropertyProvider {
-
- private List<URI> uris = new ArrayList<>();
- private Map<String,String> properties = new HashMap<>();
-
- public URIBasedPropertyProvider(MetaInfo metaInfo, Collection<URI> uris) {
- super(metaInfo);
- Objects.requireNonNull(uris);
- this.uris.addAll(uris);
- init();
- }
-
- private void init(){
- List<String> sources = new ArrayList<>();
- for(URI uri : uris){
- ConfigurationFormat format = ConfigurationFormats.getFormat(uri);
- if(format != null){
- try{
- properties.putAll(format.readConfiguration(uri));
- sources.add(uri.toString());
- }
- catch(Exception e){
- e.printStackTrace();
- }
- }
- }
- MetaInfoBuilder metaInfoBuilder = MetaInfoBuilder.of(getMetaInfo());
- super.metaInfo = metaInfoBuilder
- .setSources(sources.toString()).build();
- }
-
- @Override
- public Map<String, String> toMap() {
- return properties;
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/spi/ConfigurationFormat.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/spi/ConfigurationFormat.java b/core/src/main/java/org/apache/tamaya/core/spi/ConfigurationFormat.java
index 1ebe566..f06e224 100644
--- a/core/src/main/java/org/apache/tamaya/core/spi/ConfigurationFormat.java
+++ b/core/src/main/java/org/apache/tamaya/core/spi/ConfigurationFormat.java
@@ -46,7 +46,7 @@ public interface ConfigurationFormat{
boolean isAccepted(URI resource);
/**
- * Reads a {@link javax.config.PropertyMap} from the given URI, using this format.
+ * Reads a {@link org.apache.tamaya.PropertyProvider} fromMap the given URI, using this format.
*
* @param resource the configuration location, not null
* @return the corresponding {@link java.util.Map}, never {@code null}.
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/spi/EnvironmentProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/spi/EnvironmentProvider.java b/core/src/main/java/org/apache/tamaya/core/spi/EnvironmentProvider.java
index 992b337..e57730d 100644
--- a/core/src/main/java/org/apache/tamaya/core/spi/EnvironmentProvider.java
+++ b/core/src/main/java/org/apache/tamaya/core/spi/EnvironmentProvider.java
@@ -52,7 +52,7 @@ public interface EnvironmentProvider {
/**
* Get all currently known environment contexts for this environment type.
* @return all currently known environment contexts, never null. Environment
- * providers may prevent abritrary access to environment from outside of the
+ * providers may prevent abritrary access to environment fromMap outside of the
* regarding runtime context by just not including the context information
* in this call's result.
* @return all currently known environment contexts, never null.
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/spi/ExpressionResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/spi/ExpressionResolver.java b/core/src/main/java/org/apache/tamaya/core/spi/ExpressionResolver.java
index f2a32b7..3edf23e 100644
--- a/core/src/main/java/org/apache/tamaya/core/spi/ExpressionResolver.java
+++ b/core/src/main/java/org/apache/tamaya/core/spi/ExpressionResolver.java
@@ -37,7 +37,7 @@ public interface ExpressionResolver {
String getResolverId();
/**
- * Resolve the expression. The expression should be stripped from any surrounding parts.
+ * Resolve the expression. The expression should be stripped fromMap any surrounding parts.
* E.g. <code>${myresolver:blabla to be interpreted AND executed.}</code> should be passed
* as {@code blabla to be interpreted AND executed.} only.
*
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/spi/ResourceLoader.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/spi/ResourceLoader.java b/core/src/main/java/org/apache/tamaya/core/spi/ResourceLoader.java
index 2cca547..e2727d3 100644
--- a/core/src/main/java/org/apache/tamaya/core/spi/ResourceLoader.java
+++ b/core/src/main/java/org/apache/tamaya/core/spi/ResourceLoader.java
@@ -24,7 +24,7 @@ import java.util.List;
import java.util.stream.Stream;
/**
- * Interface to be implemented by containers that decouples loading of classpath resources from the effective
+ * Interface to be implemented by containers that decouples loading of classpath resources fromMap the effective
* classloader architecture of a runtime environment. Implementations of this class encapsulate the mechanism of
* determining the
* concrete resources available base on an expression defining the configuration
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/resources/META-INF/services/org.apache.tamaya.spi.PropertyProvidersSingletonSpi
----------------------------------------------------------------------
diff --git a/core/src/main/resources/META-INF/services/org.apache.tamaya.spi.PropertyProvidersSingletonSpi b/core/src/main/resources/META-INF/services/org.apache.tamaya.spi.PropertyProvidersSingletonSpi
new file mode 100644
index 0000000..092a7c2
--- /dev/null
+++ b/core/src/main/resources/META-INF/services/org.apache.tamaya.spi.PropertyProvidersSingletonSpi
@@ -0,0 +1,19 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy 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.
+#
+org.apache.tamaya.core.internal.DefaultPropertyProvidersSingletonSpi
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/test/java/org/apache/tamaya/DefaultConfigurationManagerSingletonSpiSingletonSpiTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/tamaya/DefaultConfigurationManagerSingletonSpiSingletonSpiTest.java b/core/src/test/java/org/apache/tamaya/DefaultConfigurationManagerSingletonSpiSingletonSpiTest.java
index 6ca1063..f8ac835 100644
--- a/core/src/test/java/org/apache/tamaya/DefaultConfigurationManagerSingletonSpiSingletonSpiTest.java
+++ b/core/src/test/java/org/apache/tamaya/DefaultConfigurationManagerSingletonSpiSingletonSpiTest.java
@@ -86,13 +86,13 @@ public class DefaultConfigurationManagerSingletonSpiSingletonSpiTest {
@Test
public void testAddRemoveGlobalConfigChangeListener() {
- Configuration.addGlobalPropertyChangeListener(LISTENER);
- Configuration.removeGlobalPropertyChangeListener(LISTENER);
- Configuration.addGlobalPropertyChangeListener(LISTENER);
- Configuration.addGlobalPropertyChangeListener(LISTENER);
- Configuration.removeGlobalPropertyChangeListener(LISTENER);
- Configuration.removeGlobalPropertyChangeListener(LISTENER);
- Configuration.removeGlobalPropertyChangeListener(LISTENER);
+ Configuration.addConfigChangeListener(LISTENER);
+ Configuration.removeConfigChangeListener(LISTENER);
+ Configuration.addConfigChangeListener(LISTENER);
+ Configuration.addConfigChangeListener(LISTENER);
+ Configuration.removeConfigChangeListener(LISTENER);
+ Configuration.removeConfigChangeListener(LISTENER);
+ Configuration.removeConfigChangeListener(LISTENER);
}
@Test
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/test/java/org/apache/tamaya/JavaOneDemo.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/tamaya/JavaOneDemo.java b/core/src/test/java/org/apache/tamaya/JavaOneDemo.java
index 9cd271c..8719693 100644
--- a/core/src/test/java/org/apache/tamaya/JavaOneDemo.java
+++ b/core/src/test/java/org/apache/tamaya/JavaOneDemo.java
@@ -20,9 +20,6 @@ package org.apache.tamaya;
import org.apache.tamaya.core.config.ConfigurationBuilder;
import org.apache.tamaya.core.config.ConfigurationFormats;
-import org.apache.tamaya.core.properties.AggregationPolicy;
-import org.apache.tamaya.core.properties.PropertyProviders;
-import org.apache.tamaya.samples.annotations.ConfiguredClass;
import org.apache.tamaya.core.spi.ConfigurationFormat;
import org.junit.Test;
@@ -62,7 +59,7 @@ public class JavaOneDemo{
PropertyProviders
.fromPaths("classpath:cfg/test.xml"),
PropertyProviders.fromArgs(new String[]{"-arg1", "--fullarg", "fullValue", "-myflag"}),
- PropertyProviders.from(cfgMap)).build();
+ PropertyProviders.fromMap(cfgMap)).build();
System.out.println(config.getAreas());
System.out.println("---");
System.out.println(config.getAreas(s -> s.startsWith("another")));
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/test/java/org/apache/tamaya/core/properties/PropertyProvidersTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/tamaya/core/properties/PropertyProvidersTest.java b/core/src/test/java/org/apache/tamaya/core/properties/PropertyProvidersTest.java
index 3e3435c..825f895 100644
--- a/core/src/test/java/org/apache/tamaya/core/properties/PropertyProvidersTest.java
+++ b/core/src/test/java/org/apache/tamaya/core/properties/PropertyProvidersTest.java
@@ -18,6 +18,7 @@
*/
package org.apache.tamaya.core.properties;
+import org.apache.tamaya.PropertyProviders;
import org.junit.Test;
import org.apache.tamaya.PropertyProvider;
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/test/java/org/apache/tamaya/internal/MutableTestConfigProvider.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/tamaya/internal/MutableTestConfigProvider.java b/core/src/test/java/org/apache/tamaya/internal/MutableTestConfigProvider.java
index 91d99dd..c42e926 100644
--- a/core/src/test/java/org/apache/tamaya/internal/MutableTestConfigProvider.java
+++ b/core/src/test/java/org/apache/tamaya/internal/MutableTestConfigProvider.java
@@ -21,8 +21,8 @@ package org.apache.tamaya.internal;
import org.apache.tamaya.Configuration;
import org.apache.tamaya.MetaInfoBuilder;
import org.apache.tamaya.PropertyProvider;
+import org.apache.tamaya.PropertyProviders;
import org.apache.tamaya.core.config.Configurations;
-import org.apache.tamaya.core.properties.PropertyProviders;
import org.apache.tamaya.core.spi.ConfigurationProviderSpi;
import java.util.Map;
@@ -43,7 +43,7 @@ public class MutableTestConfigProvider implements ConfigurationProviderSpi{
dataMap.put("sons.1", "Robin");
dataMap.put("sons.2", "Luke");
dataMap.put("sons.3", "Benjamin");
- PropertyProvider provider = PropertyProviders.from(MetaInfoBuilder.of().setName(CONFIG_NAME).build(),
+ PropertyProvider provider = PropertyProviders.fromMap(MetaInfoBuilder.of().setName(CONFIG_NAME).build(),
dataMap);
testConfig = Configurations.getConfiguration(provider);
}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/test/java/org/apache/tamaya/simple/SimplePropertiesAndCLISample.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/tamaya/simple/SimplePropertiesAndCLISample.java b/core/src/test/java/org/apache/tamaya/simple/SimplePropertiesAndCLISample.java
index f4905d5..63a9aaa 100644
--- a/core/src/test/java/org/apache/tamaya/simple/SimplePropertiesAndCLISample.java
+++ b/core/src/test/java/org/apache/tamaya/simple/SimplePropertiesAndCLISample.java
@@ -16,12 +16,12 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.tamaya.simple.simple;
+package org.apache.tamaya.simple;
+import org.apache.tamaya.PropertyProviders;
import org.apache.tamaya.core.config.ConfigurationBuilder;
import org.apache.tamaya.core.config.ConfigurationFormats;
-import org.apache.tamaya.core.properties.AggregationPolicy;
-import org.apache.tamaya.core.properties.PropertyProviders;
+import org.apache.tamaya.AggregationPolicy;
import org.apache.tamaya.core.spi.ConfigurationFormat;
import org.junit.Test;
@@ -55,7 +55,7 @@ public class SimplePropertiesAndCLISample{
PropertyProviders
.fromPaths("classpath:cfg/test.xml"),
PropertyProviders.fromArgs(new String[]{"-arg1", "--fullarg", "fullValue", "-myflag"}),
- PropertyProviders.from(cfgMap)).build();
+ PropertyProviders.fromMap(cfgMap)).build();
System.out.println(config.getAreas());
System.out.println("---");
System.out.println(config.getAreas(s -> s.startsWith("another")));
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/test/java/org/apache/tamaya/ucs/UC1ReadProperties/UC1ReadPropertiesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/tamaya/ucs/UC1ReadProperties/UC1ReadPropertiesTest.java b/core/src/test/java/org/apache/tamaya/ucs/UC1ReadProperties/UC1ReadPropertiesTest.java
new file mode 100644
index 0000000..2105c94
--- /dev/null
+++ b/core/src/test/java/org/apache/tamaya/ucs/UC1ReadProperties/UC1ReadPropertiesTest.java
@@ -0,0 +1,218 @@
+/**
+ * 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.ucs.UC1ReadProperties;
+
+import org.apache.tamaya.*;
+import org.junit.Test;
+
+import java.math.BigDecimal;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * Configuration is organized as key/value pairs. This basically can be modeled as {@code Map<String,String>}
+ * Configuration should be as simple as possible. A {@code Map<String,String>} instance has methods that may not
+ * be used in many use cases and/or are not easy to implement. Currently the following functionality
+ * must be supported:
+ * <ul>
+ * <li>access a value by key (+get+)</li>
+ * <li>check if a value is present (+containsKey+)</li>
+ * <li>get a set of all defined keys (+keySet+)</li>
+ * <li>a property provider must be convertible to a +Map+, by calling +toMap()+</li>
+ * <li>a property provider must get access to its meta information.</li>
+ * </ul>
+ * Additionally there are other requirement important for ease of use:
+ * <ul>
+ * <li>The API must never return null.</li>
+ * <li>The API should support undefined values.</li>
+ * <li>The API must support passing default values, to be returned if a value is undefined.</li>
+ * <li>The API must allow to throw exceptions, when a value is undefined.
+ * Customized exceptions hereby should be supported.</li>
+ * <li>Properties can be stored in the classpath, on a file.</li>
+ * <li>Properties can be stored as properties, xml-properties or as ini-files.</li>
+ * <li>Properties can also be provided as properties, or as a Map<String,String></li>
+ * </ul>
+ */
+public class UC1ReadPropertiesTest {
+
+ @Test
+ public void example() {
+ Configuration config = PropertyProviders.fromPaths("classpath:ucs/UC1ReadProperties/UC1ReadPropertiesTest.properties").toConfiguration();
+ String name = config.get("name").orElse("Anatole");
+ BigDecimal bigNum = config.get("num.BD", BigDecimal.class).orElseThrow(() -> new IllegalStateException("Sorry"));
+ double anotherNum = config.getDouble("num.Double").getAsDouble();
+ long longNum = config.getLong("num.Long").orElse(288900L);
+
+ // or more simpler use area function
+ Configuration areaConfig2 = config.with(ConfigFunctions.selectArea("num"));
+ System.out.println(areaConfig2);
+
+ // iterator over an area, using streams only
+ Map<String, String> areaMap = config.toMap().entrySet().stream()
+ .filter((e) -> e.getKey().startsWith("num."))
+ .collect(Collectors.toMap((e) -> e.getKey().substring("num.".length()), (e) -> e.getValue()));
+ Configuration areaConfig = PropertyProviders.fromMap(areaMap).toConfiguration();
+ System.out.println(areaConfig);
+ }
+
+ @Test
+ public void getConfigurationTest() {
+ PropertyProvider provider = PropertyProviders.fromPaths("classpath:barFoo.properties");
+ Configuration config = provider.toConfiguration();
+ assertNotNull(config);
+ assertTrue(config.isEmpty());
+ assertTrue(config.keySet().isEmpty());
+ assertFalse(config.isMutable());
+ }
+
+ @Test
+ public void readBadPropertiesTest() {
+ PropertyProvider provider = PropertyProviders.fromPaths("classpath:barFoo.properties");
+ assertNotNull(provider);
+ assertTrue(provider.isEmpty());
+ assertTrue(provider.keySet().isEmpty());
+ assertFalse(provider.isMutable());
+ }
+
+ @Test
+ public void readPropertiesTest() {
+ PropertyProvider provider = PropertyProviders.fromPaths("classpath:ucs/UC1ReadProperties/UC1ReadPropertiesTest.properties");
+ assertNotNull(provider);
+ assertEquals(provider.get("a").get(), "aValue");
+ assertEquals(provider.get("b").get(), "bValue");
+ assertEquals(provider.get("c").get(), "cValue");
+ assertEquals(provider.get("a.b.c").get(), "abcValue");
+ assertEquals(provider.get("a.b.a").get(), "abaValue");
+ assertEquals(provider.get("a.b.b").get(), "abbValue");
+ assertEquals(provider.get("a.b").get(), "abValue");
+ assertEquals(provider.get("a.a.a").get(), "aaaValue");
+ assertEquals(provider.get("b.b.b").get(), "bbbValue");
+ assertEquals(provider.get("c.c.c").get(), "cccValue");
+ assertEquals(provider.get("numInt").get(), "9999");
+ assertEquals(provider.get("num.Int").get(), "123");
+ assertEquals(provider.get("num.Byte").get(), "100");
+ assertEquals(provider.get("boolean").get(), "true");
+ assertEquals(provider.get("num.BD").get(), "2376523725372653.287362836283628362863");
+ assertEquals(provider.get("num.Double").get(), "21334.43254");
+ assertTrue(!provider.get("blabla").isPresent());
+ assertTrue(provider.get("num.BD").isPresent());
+ }
+
+ @Test
+ public void readXmlPropertiesTest() {
+ PropertyProvider provider = PropertyProviders.fromPaths("classpath:ucs/UC1ReadProperties/UC1ReadPropertiesTest.xml");
+ assertNotNull(provider);
+ assertEquals(provider.get("a-xml").get(), "aFromXml");
+ assertEquals(provider.get("b-xml").get(), "bFromXml");
+ assertEquals(provider.get("a.b.c-xml").get(), "abcFromXml");
+ }
+
+ @Test
+ public void readIniPropertiesTest() {
+ PropertyProvider provider = PropertyProviders.fromPaths("classpath:ucs/UC1ReadProperties/UC1ReadPropertiesTest.ini");
+ assertNotNull(provider);
+ assertEquals(provider.get("a.b.c").get(), "abcValue-fromIni");
+ assertEquals(provider.get("a.b.b").get(), "abbValue-fromIni");
+ assertEquals(provider.get("a.b.a").get(), "abaValue-fromIni");
+ assertEquals(provider.get("mixed.a.b").get(), "abValue");
+ assertFalse(provider.get("mixed.foo").isPresent());
+ assertTrue(provider.get("num.BD").isPresent());
+ }
+
+ @Test
+ public void readAllPropertiesTest() {
+ PropertyProvider provider = PropertyProviders.fromPaths(AggregationPolicy.IGNORE, "classpath:ucs/UC1ReadProperties/UC1ReadPropertiesTest.*");
+ assertNotNull(provider);
+ // fromMap ini file
+ assertEquals(provider.get("a.b.c").get(), "abcValue-fromIni");
+ assertEquals(provider.get("a.b.b").get(), "abbValue-fromIni");
+ assertEquals(provider.get("a.b.a").get(), "abaValue-fromIni");
+ // fromMap properties
+ assertTrue(provider.containsKey("num.BD"));
+ // fromMap xml properties
+ assertEquals(provider.get("a-xml").get(), "aFromXml");
+ assertEquals(provider.get("b-xml").get(), "bFromXml");
+ }
+
+ @Test
+ public void checkForAValue() {
+ PropertyProvider provider = PropertyProviders.fromPaths("classpath:ucs/UC1ReadProperties/UC1ReadPropertiesTest.properties");
+ assertFalse(provider.containsKey("blabla"));
+ assertTrue(provider.containsKey("num.BD"));
+ assertFalse(provider.get("blabla").isPresent());
+ assertTrue(provider.get("num.BD").isPresent());
+ }
+
+ @Test
+ public void checkKeys() {
+ PropertyProvider provider = PropertyProviders.fromPaths("classpath:ucs/UC1ReadProperties/UC1ReadPropertiesTest.properties");
+ assertEquals(provider.keySet().size(), 16);
+ assertTrue(provider.keySet().contains("boolean"));
+ assertFalse(provider.keySet().contains("blabla"));
+ }
+
+ @Test
+ public void checkToMap() {
+ PropertyProvider provider = PropertyProviders.fromPaths("classpath:ucs/UC1ReadProperties/UC1ReadPropertiesTest.properties");
+ Map<String, String> map = provider.toMap();
+ assertNotNull(map);
+ assertEquals(map.size(), 16);
+ assertEquals(provider.keySet(), map.keySet());
+ assertTrue(map.keySet().contains("boolean"));
+ assertFalse(map.keySet().contains("blabla"));
+ }
+
+ @Test
+ public void checkMetaInfo() {
+ PropertyProvider provider = PropertyProviders.fromPaths("classpath:ucs/UC1ReadProperties/UC1ReadPropertiesTest.properties");
+ MetaInfo meta = provider.getMetaInfo();
+ assertNotNull(meta);
+ }
+
+ @Test
+ public void checkNeverNull() {
+ PropertyProvider provider = PropertyProviders.fromPaths("classpath:ucs/UC1ReadProperties/UC1ReadPropertiesTest.properties");
+ assertNotNull(provider.get("blabla"));
+ assertNotNull(provider.get("a.b.c"));
+ }
+
+ @Test
+ public void checkUndefined() {
+ PropertyProvider provider = PropertyProviders.fromPaths("classpath:ucs/UC1ReadProperties/UC1ReadPropertiesTest.properties");
+ assertFalse(provider.get("blabla").isPresent());
+ assertTrue(provider.get("a.b.c").isPresent());
+ }
+
+ @Test
+ public void checkPassDefaultValues() {
+ PropertyProvider provider = PropertyProviders.fromPaths("classpath:ucs/UC1ReadProperties/UC1ReadPropertiesTest.properties");
+ assertEquals("myDefaultValue", provider.get("blabla").orElse("myDefaultValue"));
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void checkThrowCustomException() {
+ PropertyProvider provider = PropertyProviders.fromPaths("classpath:ucs/UC1ReadProperties/UC1ReadPropertiesTest.properties");
+ provider.get("blabla").orElseThrow(() -> new IllegalStateException("checkThrowCustomException"));
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/test/resources/ucs/UC1ReadProperties/UC1ReadPropertiesTest.ini
----------------------------------------------------------------------
diff --git a/core/src/test/resources/ucs/UC1ReadProperties/UC1ReadPropertiesTest.ini b/core/src/test/resources/ucs/UC1ReadProperties/UC1ReadPropertiesTest.ini
new file mode 100644
index 0000000..2e19f08
--- /dev/null
+++ b/core/src/test/resources/ucs/UC1ReadProperties/UC1ReadPropertiesTest.ini
@@ -0,0 +1,23 @@
+[a.b]
+c=abcValue-fromIni
+a=abaValue-fromIni
+b=abbValue-fromIni
+
+[mixed]
+a.b=abValue
+a.a.a=aaaValue
+b.b.b=bbbValue
+c.c.c=cccValue
+
+[num]
+Int=123
+Byte=100
+BD=2376523725372653.287362836283628362863
+Double=21334.43254
+
+[other]
+boolean=true
+a=aValue
+b=bValue
+c=cValue
+numInt=9999s
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/test/resources/ucs/UC1ReadProperties/UC1ReadPropertiesTest.properties
----------------------------------------------------------------------
diff --git a/core/src/test/resources/ucs/UC1ReadProperties/UC1ReadPropertiesTest.properties b/core/src/test/resources/ucs/UC1ReadProperties/UC1ReadPropertiesTest.properties
new file mode 100644
index 0000000..3fca7fd
--- /dev/null
+++ b/core/src/test/resources/ucs/UC1ReadProperties/UC1ReadPropertiesTest.properties
@@ -0,0 +1,16 @@
+a=aValue
+b=bValue
+c=cValue
+a.b.c=abcValue
+a.b.a=abaValue
+a.b.b=abbValue
+a.b=abValue
+a.a.a=aaaValue
+b.b.b=bbbValue
+c.c.c=cccValue
+numInt=9999
+num.Int=123
+num.Byte=100
+boolean=true
+num.BD=2376523725372653.287362836283628362863
+num.Double=21334.43254
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/test/resources/ucs/UC1ReadProperties/UC1ReadPropertiesTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/ucs/UC1ReadProperties/UC1ReadPropertiesTest.xml b/core/src/test/resources/ucs/UC1ReadProperties/UC1ReadPropertiesTest.xml
new file mode 100644
index 0000000..425233c
--- /dev/null
+++ b/core/src/test/resources/ucs/UC1ReadProperties/UC1ReadPropertiesTest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+<properties version="1.0">
+ <entry key="a-xml">aFromXml</entry>
+ <entry key="b-xml">bFromXml</entry>
+ <entry key="a.b.c-xml">abcFromXml</entry>
+</properties>
\ No newline at end of file
[6/8] incubator-tamaya git commit: TAMAYA-14: added resource support.
TAMAYA-15: Moved PropertyProviders to API,
added SPI. TAMAYA-8: Added/improved Javadoc.
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/resources/io/AbstractFileResolvingResource.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/resources/io/AbstractFileResolvingResource.java b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/AbstractFileResolvingResource.java
new file mode 100644
index 0000000..7993527
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/AbstractFileResolvingResource.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2002-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tamaya.core.internal.resources.io;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+
+/**
+ * Abstract base class for resources which resolve URLs into File references,
+ * such as {@code UrlResource} or {@link ClassPathResource}.
+ *
+ * <p>Detects the "file" protocol as well as the JBoss "vfs" protocol in URLs,
+ * resolving file system references accordingly.
+ *
+ * @author Juergen Hoeller
+ * @since 3.0
+ */
+abstract class AbstractFileResolvingResource extends AbstractResource {
+
+ /**
+ * This implementation returns a File reference for the underlying class path
+ * resource, provided that it refers to a file in the file system.
+ */
+ @Override
+ public File getFile() throws IOException {
+ URL url = getURL();
+ if (url.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
+ return VfsResourceDelegate.getResource(url).getFile();
+ }
+ return ResourceUtils.getFile(url, getDescription());
+ }
+
+ /**
+ * This implementation determines the underlying File
+ * (or jar file, in case of a resource in a jar/zip).
+ */
+ @Override
+ protected File getFileForLastModifiedCheck() throws IOException {
+ URL url = getURL();
+ if (ResourceUtils.isJarURL(url)) {
+ URL actualUrl = ResourceUtils.extractJarFileURL(url);
+ if (actualUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
+ return VfsResourceDelegate.getResource(actualUrl).getFile();
+ }
+ return ResourceUtils.getFile(actualUrl, "Jar URL");
+ }
+ else {
+ return getFile();
+ }
+ }
+
+ /**
+ * This implementation returns a File reference for the underlying class path
+ * resource, provided that it refers to a file in the file system.
+ * @see ResourceUtils#getFile(java.net.URI, String)
+ */
+ protected File getFile(URI uri) throws IOException {
+ if (uri.getScheme().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
+ return VfsResourceDelegate.getResource(uri).getFile();
+ }
+ return ResourceUtils.getFile(uri, getDescription());
+ }
+
+
+ @Override
+ public boolean exists() {
+ try {
+ URL url = getURL();
+ if (ResourceUtils.isFileURL(url)) {
+ // Proceed with file system resolution...
+ return getFile().exists();
+ }
+ else {
+ // Try a URL connection content-length header...
+ URLConnection con = url.openConnection();
+ customizeConnection(con);
+ HttpURLConnection httpCon =
+ (con instanceof HttpURLConnection ? (HttpURLConnection) con : null);
+ if (httpCon != null) {
+ int code = httpCon.getResponseCode();
+ if (code == HttpURLConnection.HTTP_OK) {
+ return true;
+ }
+ else if (code == HttpURLConnection.HTTP_NOT_FOUND) {
+ return false;
+ }
+ }
+ if (con.getContentLength() >= 0) {
+ return true;
+ }
+ if (httpCon != null) {
+ // no HTTP OK status, and no content-length header: give up
+ httpCon.disconnect();
+ return false;
+ }
+ else {
+ // Fall back to stream existence: can we open the stream?
+ InputStream is = getInputStream();
+ is.close();
+ return true;
+ }
+ }
+ }
+ catch (IOException ex) {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isReadable() {
+ try {
+ URL url = getURL();
+ if (ResourceUtils.isFileURL(url)) {
+ // Proceed with file system resolution...
+ File file = getFile();
+ return (file.canRead() && !file.isDirectory());
+ }
+ else {
+ return true;
+ }
+ }
+ catch (IOException ex) {
+ return false;
+ }
+ }
+
+ @Override
+ public long contentLength() throws IOException {
+ URL url = getURL();
+ if (ResourceUtils.isFileURL(url)) {
+ // Proceed with file system resolution...
+ return getFile().length();
+ }
+ else {
+ // Try a URL connection content-length header...
+ URLConnection con = url.openConnection();
+ customizeConnection(con);
+ return con.getContentLength();
+ }
+ }
+
+ @Override
+ public long lastModified() throws IOException {
+ URL url = getURL();
+ if (ResourceUtils.isFileURL(url) || ResourceUtils.isJarURL(url)) {
+ // Proceed with file system resolution...
+ return super.lastModified();
+ }
+ else {
+ // Try a URL connection last-modified header...
+ URLConnection con = url.openConnection();
+ customizeConnection(con);
+ return con.getLastModified();
+ }
+ }
+
+
+ /**
+ * Customize the given {@link URLConnection}, obtained in the course of an
+ * {@link #exists()}, {@link #contentLength()} or {@link #lastModified()} call.
+ * <p>Calls {@link ResourceUtils#useCachesIfNecessary(URLConnection)} and
+ * delegates to {@link #customizeConnection(HttpURLConnection)} if possible.
+ * Can be overridden in subclasses.
+ * @param con the URLConnection to customize
+ * @throws IOException if thrown from URLConnection methods
+ */
+ protected void customizeConnection(URLConnection con) throws IOException {
+ ResourceUtils.useCachesIfNecessary(con);
+ if (con instanceof HttpURLConnection) {
+ customizeConnection((HttpURLConnection) con);
+ }
+ }
+
+ /**
+ * Customize the given {@link HttpURLConnection}, obtained in the course of an
+ * {@link #exists()}, {@link #contentLength()} or {@link #lastModified()} call.
+ * <p>Sets request method "HEAD" by default. Can be overridden in subclasses.
+ * @param con the HttpURLConnection to customize
+ * @throws IOException if thrown from HttpURLConnection methods
+ */
+ protected void customizeConnection(HttpURLConnection con) throws IOException {
+ con.setRequestMethod("HEAD");
+ }
+
+
+ /**
+ * Inner delegate class, avoiding a hard JBoss VFS API dependency at runtime.
+ */
+ private static class VfsResourceDelegate {
+
+ public static Resource getResource(URL url) throws IOException {
+ return new VfsResource(VfsUtils.getRoot(url));
+ }
+
+ public static Resource getResource(URI uri) throws IOException {
+ return new VfsResource(VfsUtils.getRoot(uri));
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/resources/io/AbstractResource.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/resources/io/AbstractResource.java b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/AbstractResource.java
new file mode 100644
index 0000000..cffe204
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/AbstractResource.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tamaya.core.internal.resources.io;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.Objects;
+
+
+/**
+ * Convenience base class for {@link Resource} implementations,
+ * pre-implementing typical behavior.
+ *
+ * <p>The "exists" method will check whether a File or InputStream can
+ * be opened; "isOpen" will always return false; "getURL" and "getFile"
+ * throw an exception; and "toString" will return the description.
+ *
+ * @author Juergen Hoeller
+ * @since 28.12.2003
+ */
+public abstract class AbstractResource implements Resource {
+
+ /**
+ * This implementation checks whether a File can be opened,
+ * falling back to whether an InputStream can be opened.
+ * This will cover both directories and content resources.
+ */
+ @Override
+ public boolean exists() {
+ // Try file existence: can we find the file in the file system?
+ try {
+ return getFile().exists();
+ }
+ catch (IOException ex) {
+ // Fall back to stream existence: can we open the stream?
+ try {
+ InputStream is = getInputStream();
+ is.close();
+ return true;
+ }
+ catch (Throwable isEx) {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * This implementation always returns {@code true}.
+ */
+ @Override
+ public boolean isReadable() {
+ return true;
+ }
+
+ /**
+ * This implementation always returns {@code false}.
+ */
+ @Override
+ public boolean isOpen() {
+ return false;
+ }
+
+ /**
+ * This implementation throws a FileNotFoundException, assuming
+ * that the resource cannot be resolved to a URL.
+ */
+ @Override
+ public URL getURL() throws IOException {
+ throw new FileNotFoundException(getDescription() + " cannot be resolved to URL");
+ }
+
+ /**
+ * This implementation builds a URI based on the URL returned
+ * by {@link #getURL()}.
+ */
+ @Override
+ public URI getURI() throws IOException {
+ URL url = getURL();
+ try {
+ return ResourceUtils.toURI(url);
+ }
+ catch (URISyntaxException ex) {
+ throw new IllegalStateException("Invalid URI [" + url + "]", ex);
+ }
+ }
+
+ /**
+ * This implementation throws a FileNotFoundException, assuming
+ * that the resource cannot be resolved to an absolute file path.
+ */
+ @Override
+ public File getFile() throws IOException {
+ throw new FileNotFoundException(getDescription() + " cannot be resolved to absolute file path");
+ }
+
+ /**
+ * This implementation reads the entire InputStream to calculate the
+ * content length. Subclasses will almost always be able to provide
+ * a more optimal version of this, e.g. checking a File length.
+ * @throws IllegalStateException if {@code #getInputStream()} returns null.
+ */
+ @Override
+ public long contentLength() throws IOException {
+ InputStream is = this.getInputStream();
+ Objects.requireNonNull(is, "resource input stream must not be null");
+ try {
+ long size = 0;
+ byte[] buf = new byte[255];
+ int read;
+ while ((read = is.read(buf)) != -1) {
+ size += read;
+ }
+ return size;
+ }
+ finally {
+ try {
+ is.close();
+ }
+ catch (IOException ex) {
+ }
+ }
+ }
+
+ /**
+ * This implementation checks the timestamp of the underlying File,
+ * if available.
+ * @see #getFileForLastModifiedCheck()
+ */
+ @Override
+ public long lastModified() throws IOException {
+ long lastModified = getFileForLastModifiedCheck().lastModified();
+ if (lastModified == 0L) {
+ throw new FileNotFoundException(getDescription() +
+ " cannot be resolved in the file system for resolving its last-modified timestamp");
+ }
+ return lastModified;
+ }
+
+ /**
+ * Determine the File to use for timestamp checking.
+ * <p>The default implementation delegates to {@link #getFile()}.
+ * @return the File to use for timestamp checking (never {@code null})
+ * @throws IOException if the resource cannot be resolved as absolute
+ * file path, i.e. if the resource is not available in a file system
+ */
+ protected File getFileForLastModifiedCheck() throws IOException {
+ return getFile();
+ }
+
+ /**
+ * This implementation throws a FileNotFoundException, assuming
+ * that relative resources cannot be created for this resource.
+ */
+ @Override
+ public Resource createRelative(String relativePath) throws IOException {
+ throw new FileNotFoundException("Cannot create a relative resource for " + getDescription());
+ }
+
+ /**
+ * This implementation always returns {@code null},
+ * assuming that this resource type does not have a filename.
+ */
+ @Override
+ public String getFilename() {
+ return null;
+ }
+
+
+ /**
+ * This implementation returns the description of this resource.
+ * @see #getDescription()
+ */
+ @Override
+ public String toString() {
+ return getDescription();
+ }
+
+ /**
+ * This implementation compares description strings.
+ * @see #getDescription()
+ */
+ @Override
+ public boolean equals(Object obj) {
+ return (obj == this ||
+ (obj instanceof Resource && ((Resource) obj).getDescription().equals(getDescription())));
+ }
+
+ /**
+ * This implementation returns the description's hash code.
+ * @see #getDescription()
+ */
+ @Override
+ public int hashCode() {
+ return getDescription().hashCode();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/resources/io/AntPathMatcher.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/resources/io/AntPathMatcher.java b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/AntPathMatcher.java
new file mode 100644
index 0000000..af534cd
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/AntPathMatcher.java
@@ -0,0 +1,777 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tamaya.core.internal.resources.io;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * PathMatcher implementation for Ant-style path patterns. Examples are provided below.
+ *
+ * <p>Part of this mapping code has been kindly borrowed from <a href="http://ant.apache.org">Apache Ant</a>.
+ *
+ * <p>The mapping matches URLs using the following rules:<br> <ul> <li>? matches one character</li> <li>* matches zero
+ * or more characters</li> <li>** matches zero or more 'directories' in a path</li> </ul>
+ *
+ * <p>Some examples:<br> <ul> <li>{@code com/t?st.jsp} - matches {@code com/test.jsp} but also
+ * {@code com/tast.jsp} or {@code com/txst.jsp}</li> <li>{@code com/*.jsp} - matches all
+ * {@code .jsp} files in the {@code com} directory</li> <li>{@code com/**/test.jsp} - matches all
+ * {@code test.jsp} files underneath the {@code com} path</li> <li>{@code org/springframework/**/*.jsp}
+ * - matches all {@code .jsp} files underneath the {@code org/springframework} path</li>
+ * <li>{@code org/**/servlet/bla.jsp} - matches {@code org/springframework/servlet/bla.jsp} but also
+ * {@code org/springframework/testing/servlet/bla.jsp} and {@code org/servlet/bla.jsp}</li> </ul>
+ *
+ * @author Alef Arendsen
+ * @author Juergen Hoeller
+ * @author Rob Harrop
+ * @author Arjen Poutsma
+ * @author Rossen Stoyanchev
+ * @since 16.07.2003
+ */
+class AntPathMatcher {
+
+ /** Default path separator: "/" */
+ public static final String DEFAULT_PATH_SEPARATOR = "/";
+
+ private static final int CACHE_TURNOFF_THRESHOLD = 65536;
+
+ private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\{[^/]+?\\}");
+
+
+ private String pathSeparator;
+
+ private PathSeparatorPatternCache pathSeparatorPatternCache;
+
+ private boolean trimTokens = true;
+
+ private volatile Boolean cachePatterns;
+
+ private final Map<String, String[]> tokenizedPatternCache = new ConcurrentHashMap<String, String[]>(256);
+
+ final Map<String, AntPathStringMatcher> stringMatcherCache = new ConcurrentHashMap<String, AntPathStringMatcher>(256);
+
+
+ /**
+ * Create a new instance with the {@link #DEFAULT_PATH_SEPARATOR}.
+ */
+ public AntPathMatcher() {
+ this.pathSeparator = DEFAULT_PATH_SEPARATOR;
+ this.pathSeparatorPatternCache = new PathSeparatorPatternCache(DEFAULT_PATH_SEPARATOR);
+ }
+
+ /**
+ * A convenience alternative constructor to use with a custom path separator.
+ * @param pathSeparator the path separator to use, must not be {@code null}.
+ * @since 4.1
+ */
+ public AntPathMatcher(String pathSeparator) {
+ Objects.requireNonNull(pathSeparator, "'pathSeparator' is required");
+ this.pathSeparator = pathSeparator;
+ this.pathSeparatorPatternCache = new PathSeparatorPatternCache(pathSeparator);
+ }
+
+
+ /**
+ * Set the path separator to use for pattern parsing.
+ * Default is "/", as in Ant.
+ */
+ public void setPathSeparator(String pathSeparator) {
+ this.pathSeparator = (pathSeparator != null ? pathSeparator : DEFAULT_PATH_SEPARATOR);
+ this.pathSeparatorPatternCache = new PathSeparatorPatternCache(this.pathSeparator);
+ }
+
+ /**
+ * Specify whether to trim tokenized paths and patterns.
+ * Default is {@code true}.
+ */
+ public void setTrimTokens(boolean trimTokens) {
+ this.trimTokens = trimTokens;
+ }
+
+ /**
+ * Specify whether to cache parsed pattern metadata for patterns passed
+ * into this matcher's {@link #match} method. A value of {@code true}
+ * activates an unlimited pattern cache; a value of {@code false} turns
+ * the pattern cache off completely.
+ * <p>Default is for the cache to be on, but with the variant to automatically
+ * turn it off when encountering too many patterns to cache at runtime
+ * (the threshold is 65536), assuming that arbitrary permutations of patterns
+ * are coming in, with little chance for encountering a reoccurring pattern.
+ * @see #getStringMatcher(String)
+ */
+ public void setCachePatterns(boolean cachePatterns) {
+ this.cachePatterns = cachePatterns;
+ }
+
+ private void deactivatePatternCache() {
+ this.cachePatterns = false;
+ this.tokenizedPatternCache.clear();
+ this.stringMatcherCache.clear();
+ }
+
+
+ public boolean isPattern(String path) {
+ return (path.indexOf('*') != -1 || path.indexOf('?') != -1);
+ }
+
+ public boolean match(String pattern, String path) {
+ return doMatch(pattern, path, true, null);
+ }
+
+ public boolean matchStart(String pattern, String path) {
+ return doMatch(pattern, path, false, null);
+ }
+
+ /**
+ * Actually match the given {@code path} against the given {@code pattern}.
+ * @param pattern the pattern to match against
+ * @param path the path String to test
+ * @param fullMatch whether a full pattern match is required (else a pattern match
+ * as far as the given base path goes is sufficient)
+ * @return {@code true} if the supplied {@code path} matched, {@code false} if it didn't
+ */
+ protected boolean doMatch(String pattern, String path, boolean fullMatch, Map<String, String> uriTemplateVariables) {
+ if (path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) {
+ return false;
+ }
+
+ String[] pattDirs = tokenizePattern(pattern);
+ String[] pathDirs = tokenizePath(path);
+
+ int pattIdxStart = 0;
+ int pattIdxEnd = pattDirs.length - 1;
+ int pathIdxStart = 0;
+ int pathIdxEnd = pathDirs.length - 1;
+
+ // Match all elements up to the first **
+ while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
+ String pattDir = pattDirs[pattIdxStart];
+ if ("**".equals(pattDir)) {
+ break;
+ }
+ if (!matchStrings(pattDir, pathDirs[pathIdxStart], uriTemplateVariables)) {
+ return false;
+ }
+ pattIdxStart++;
+ pathIdxStart++;
+ }
+
+ if (pathIdxStart > pathIdxEnd) {
+ // Path is exhausted, only match if rest of pattern is * or **'s
+ if (pattIdxStart > pattIdxEnd) {
+ return (pattern.endsWith(this.pathSeparator) ? path.endsWith(this.pathSeparator) :
+ !path.endsWith(this.pathSeparator));
+ }
+ if (!fullMatch) {
+ return true;
+ }
+ if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") && path.endsWith(this.pathSeparator)) {
+ return true;
+ }
+ for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
+ if (!pattDirs[i].equals("**")) {
+ return false;
+ }
+ }
+ return true;
+ }
+ else if (pattIdxStart > pattIdxEnd) {
+ // String not exhausted, but pattern is. Failure.
+ return false;
+ }
+ else if (!fullMatch && "**".equals(pattDirs[pattIdxStart])) {
+ // Path start definitely matches due to "**" part in pattern.
+ return true;
+ }
+
+ // up to last '**'
+ while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
+ String pattDir = pattDirs[pattIdxEnd];
+ if (pattDir.equals("**")) {
+ break;
+ }
+ if (!matchStrings(pattDir, pathDirs[pathIdxEnd], uriTemplateVariables)) {
+ return false;
+ }
+ pattIdxEnd--;
+ pathIdxEnd--;
+ }
+ if (pathIdxStart > pathIdxEnd) {
+ // String is exhausted
+ for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
+ if (!pattDirs[i].equals("**")) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ while (pattIdxStart != pattIdxEnd && pathIdxStart <= pathIdxEnd) {
+ int patIdxTmp = -1;
+ for (int i = pattIdxStart + 1; i <= pattIdxEnd; i++) {
+ if (pattDirs[i].equals("**")) {
+ patIdxTmp = i;
+ break;
+ }
+ }
+ if (patIdxTmp == pattIdxStart + 1) {
+ // '**/**' situation, so skip one
+ pattIdxStart++;
+ continue;
+ }
+ // Find the pattern between padIdxStart & padIdxTmp in str between
+ // strIdxStart & strIdxEnd
+ int patLength = (patIdxTmp - pattIdxStart - 1);
+ int strLength = (pathIdxEnd - pathIdxStart + 1);
+ int foundIdx = -1;
+
+ strLoop:
+ for (int i = 0; i <= strLength - patLength; i++) {
+ for (int j = 0; j < patLength; j++) {
+ String subPat = pattDirs[pattIdxStart + j + 1];
+ String subStr = pathDirs[pathIdxStart + i + j];
+ if (!matchStrings(subPat, subStr, uriTemplateVariables)) {
+ continue strLoop;
+ }
+ }
+ foundIdx = pathIdxStart + i;
+ break;
+ }
+
+ if (foundIdx == -1) {
+ return false;
+ }
+
+ pattIdxStart = patIdxTmp;
+ pathIdxStart = foundIdx + patLength;
+ }
+
+ for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
+ if (!pattDirs[i].equals("**")) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Tokenize the given path pattern into parts, based on this matcher's settings.
+ * <p>Performs caching based on {@link #setCachePatterns}, delegating to
+ * {@link #tokenizePath(String)} for the actual tokenization algorithm.
+ * @param pattern the pattern to tokenize
+ * @return the tokenized pattern parts
+ */
+ protected String[] tokenizePattern(String pattern) {
+ String[] tokenized = null;
+ Boolean cachePatterns = this.cachePatterns;
+ if (cachePatterns == null || cachePatterns.booleanValue()) {
+ tokenized = this.tokenizedPatternCache.get(pattern);
+ }
+ if (tokenized == null) {
+ tokenized = tokenizePath(pattern);
+ if (cachePatterns == null && this.tokenizedPatternCache.size() >= CACHE_TURNOFF_THRESHOLD) {
+ // Try to adapt to the runtime situation that we're encountering:
+ // There are obviously too many different patterns coming in here...
+ // So let's turn off the cache since the patterns are unlikely to be reoccurring.
+ deactivatePatternCache();
+ return tokenized;
+ }
+ if (cachePatterns == null || cachePatterns.booleanValue()) {
+ this.tokenizedPatternCache.put(pattern, tokenized);
+ }
+ }
+ return tokenized;
+ }
+
+ /**
+ * Tokenize the given path String into parts, based on this matcher's settings.
+ * @param path the path to tokenize
+ * @return the tokenized path parts
+ */
+ protected String[] tokenizePath(String path) {
+ return StringUtils.tokenizeToStringArray(path, this.pathSeparator, this.trimTokens, true);
+ }
+
+ /**
+ * Tests whether or not a string matches against a pattern.
+ * @param pattern the pattern to match against (never {@code null})
+ * @param str the String which must be matched against the pattern (never {@code null})
+ * @return {@code true} if the string matches against the pattern, or {@code false} otherwise
+ */
+ private boolean matchStrings(String pattern, String str, Map<String, String> uriTemplateVariables) {
+ return getStringMatcher(pattern).matchStrings(str, uriTemplateVariables);
+ }
+
+ /**
+ * Build or retrieve an {@link AntPathStringMatcher} for the given pattern.
+ * <p>The default implementation checks this AntPathMatcher's internal cache
+ * (see {@link #setCachePatterns}), creating a new AntPathStringMatcher instance
+ * if no cached copy is found.
+ * When encountering too many patterns to cache at runtime (the threshold is 65536),
+ * it turns the default cache off, assuming that arbitrary permutations of patterns
+ * are coming in, with little chance for encountering a reoccurring pattern.
+ * <p>This method may get overridden to implement a custom cache strategy.
+ * @param pattern the pattern to match against (never {@code null})
+ * @return a corresponding AntPathStringMatcher (never {@code null})
+ * @see #setCachePatterns
+ */
+ protected AntPathStringMatcher getStringMatcher(String pattern) {
+ AntPathStringMatcher matcher = null;
+ Boolean cachePatterns = this.cachePatterns;
+ if (cachePatterns == null || cachePatterns.booleanValue()) {
+ matcher = this.stringMatcherCache.get(pattern);
+ }
+ if (matcher == null) {
+ matcher = new AntPathStringMatcher(pattern);
+ if (cachePatterns == null && this.stringMatcherCache.size() >= CACHE_TURNOFF_THRESHOLD) {
+ // Try to adapt to the runtime situation that we're encountering:
+ // There are obviously too many different patterns coming in here...
+ // So let's turn off the cache since the patterns are unlikely to be reoccurring.
+ deactivatePatternCache();
+ return matcher;
+ }
+ if (cachePatterns == null || cachePatterns.booleanValue()) {
+ this.stringMatcherCache.put(pattern, matcher);
+ }
+ }
+ return matcher;
+ }
+
+ /**
+ * Given a pattern and a full path, determine the pattern-mapped part. <p>For example: <ul>
+ * <li>'{@code /docs/cvs/commit.html}' and '{@code /docs/cvs/commit.html} -> ''</li>
+ * <li>'{@code /docs/*}' and '{@code /docs/cvs/commit} -> '{@code cvs/commit}'</li>
+ * <li>'{@code /docs/cvs/*.html}' and '{@code /docs/cvs/commit.html} -> '{@code commit.html}'</li>
+ * <li>'{@code /docs/**}' and '{@code /docs/cvs/commit} -> '{@code cvs/commit}'</li>
+ * <li>'{@code /docs/**\/*.html}' and '{@code /docs/cvs/commit.html} -> '{@code cvs/commit.html}'</li>
+ * <li>'{@code /*.html}' and '{@code /docs/cvs/commit.html} -> '{@code docs/cvs/commit.html}'</li>
+ * <li>'{@code *.html}' and '{@code /docs/cvs/commit.html} -> '{@code /docs/cvs/commit.html}'</li>
+ * <li>'{@code *}' and '{@code /docs/cvs/commit.html} -> '{@code /docs/cvs/commit.html}'</li> </ul>
+ * <p>Assumes that {@link #match} returns {@code true} for '{@code pattern}' and '{@code path}', but
+ * does <strong>not</strong> enforce this.
+ */
+ public String extractPathWithinPattern(String pattern, String path) {
+ String[] patternParts = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator, this.trimTokens, true);
+ String[] pathParts = StringUtils.tokenizeToStringArray(path, this.pathSeparator, this.trimTokens, true);
+ StringBuilder builder = new StringBuilder();
+ boolean pathStarted = false;
+
+ for (int segment = 0; segment < patternParts.length; segment++) {
+ String patternPart = patternParts[segment];
+ if (patternPart.indexOf('*') > -1 || patternPart.indexOf('?') > -1) {
+ for (; segment < pathParts.length; segment++) {
+ if (pathStarted || (segment == 0 && !pattern.startsWith(this.pathSeparator))) {
+ builder.append(this.pathSeparator);
+ }
+ builder.append(pathParts[segment]);
+ pathStarted = true;
+ }
+ }
+ }
+
+ return builder.toString();
+ }
+
+ public Map<String, String> extractUriTemplateVariables(String pattern, String path) {
+ Map<String, String> variables = new LinkedHashMap<String, String>();
+ boolean result = doMatch(pattern, path, true, variables);
+ if(!result){
+ throw new IllegalArgumentException("Pattern \"" + pattern + "\" is not a match for \"" + path + "\"");
+ };
+ return variables;
+ }
+
+ /**
+ * Combines two patterns into a new pattern that is returned.
+ * <p>This implementation simply concatenates the two patterns, unless the first pattern
+ * contains a file extension match (such as {@code *.html}. In that case, the second pattern
+ * should be included in the first, or an {@code IllegalArgumentException} is thrown.
+ * <p>For example: <table>
+ * <tr><th>Pattern 1</th><th>Pattern 2</th><th>Result</th></tr> <tr><td>/hotels</td><td>{@code
+ * null}</td><td>/hotels</td></tr> <tr><td>{@code null}</td><td>/hotels</td><td>/hotels</td></tr>
+ * <tr><td>/hotels</td><td>/bookings</td><td>/hotels/bookings</td></tr> <tr><td>/hotels</td><td>bookings</td><td>/hotels/bookings</td></tr>
+ * <tr><td>/hotels/*</td><td>/bookings</td><td>/hotels/bookings</td></tr> <tr><td>/hotels/**</td><td>/bookings</td><td>/hotels/**/bookings</td></tr>
+ * <tr><td>/hotels</td><td>{hotel}</td><td>/hotels/{hotel}</td></tr> <tr><td>/hotels/*</td><td>{hotel}</td><td>/hotels/{hotel}</td></tr>
+ * <tr><td>/hotels/**</td><td>{hotel}</td><td>/hotels/**/{hotel}</td></tr>
+ * <tr><td>/*.html</td><td>/hotels.html</td><td>/hotels.html</td></tr> <tr><td>/*.html</td><td>/hotels</td><td>/hotels.html</td></tr>
+ * <tr><td>/*.html</td><td>/*.txt</td><td>IllegalArgumentException</td></tr> </table>
+ * @param pattern1 the first pattern
+ * @param pattern2 the second pattern
+ * @return the combination of the two patterns
+ * @throws IllegalArgumentException when the two patterns cannot be combined
+ */
+ public String combine(String pattern1, String pattern2) {
+ if (!StringUtils.hasText(pattern1) && !StringUtils.hasText(pattern2)) {
+ return "";
+ }
+ if (!StringUtils.hasText(pattern1)) {
+ return pattern2;
+ }
+ if (!StringUtils.hasText(pattern2)) {
+ return pattern1;
+ }
+
+ boolean pattern1ContainsUriVar = pattern1.indexOf('{') != -1;
+ if (!pattern1.equals(pattern2) && !pattern1ContainsUriVar && match(pattern1, pattern2)) {
+ // /* + /hotel -> /hotel ; "/*.*" + "/*.html" -> /*.html
+ // However /user + /user -> /usr/user ; /{foo} + /bar -> /{foo}/bar
+ return pattern2;
+ }
+
+ // /hotels/* + /booking -> /hotels/booking
+ // /hotels/* + booking -> /hotels/booking
+ if (pattern1.endsWith(this.pathSeparatorPatternCache.getEndsOnWildCard())) {
+ return concat(pattern1.substring(0, pattern1.length() - 2), pattern2);
+ }
+
+ // /hotels/** + /booking -> /hotels/**/booking
+ // /hotels/** + booking -> /hotels/**/booking
+ if (pattern1.endsWith(this.pathSeparatorPatternCache.getEndsOnDoubleWildCard())) {
+ return concat(pattern1, pattern2);
+ }
+
+ int starDotPos1 = pattern1.indexOf("*.");
+ if (pattern1ContainsUriVar || starDotPos1 == -1 || this.pathSeparator.equals(".")) {
+ // simply concatenate the two patterns
+ return concat(pattern1, pattern2);
+ }
+ String extension1 = pattern1.substring(starDotPos1 + 1);
+ int dotPos2 = pattern2.indexOf('.');
+ String fileName2 = (dotPos2 == -1 ? pattern2 : pattern2.substring(0, dotPos2));
+ String extension2 = (dotPos2 == -1 ? "" : pattern2.substring(dotPos2));
+ String extension = extension1.startsWith("*") ? extension2 : extension1;
+ return fileName2 + extension;
+ }
+
+ private String concat(String path1, String path2) {
+ if (path1.endsWith(this.pathSeparator) || path2.startsWith(this.pathSeparator)) {
+ return path1 + path2;
+ }
+ return path1 + this.pathSeparator + path2;
+ }
+
+ /**
+ * Given a full path, returns a {@link Comparator} suitable for sorting patterns in order of explicitness.
+ * <p>The returned {@code Comparator} will {@linkplain java.util.Collections#sort(java.util.List,
+ * java.util.Comparator) sort} a list so that more specific patterns (without uri templates or wild cards) come before
+ * generic patterns. So given a list with the following patterns: <ol> <li>{@code /hotels/new}</li>
+ * <li>{@code /hotels/{hotel}}</li> <li>{@code /hotels/*}</li> </ol> the returned comparator will sort this
+ * list so that the order will be as indicated.
+ * <p>The full path given as parameter is used to test for exact matches. So when the given path is {@code /hotels/2},
+ * the pattern {@code /hotels/2} will be sorted before {@code /hotels/1}.
+ * @param path the full path to use for comparison
+ * @return a comparator capable of sorting patterns in order of explicitness
+ */
+ public Comparator<String> getPatternComparator(String path) {
+ return new AntPatternComparator(path);
+ }
+
+
+ /**
+ * Tests whether or not a string matches against a pattern via a {@link Pattern}.
+ * <p>The pattern may contain special characters: '*' means zero or more characters; '?' means one and
+ * only one character; '{' and '}' indicate a URI template pattern. For example <tt>/users/{user}</tt>.
+ */
+ protected static class AntPathStringMatcher {
+
+ private static final Pattern GLOB_PATTERN = Pattern.compile("\\?|\\*|\\{((?:\\{[^/]+?\\}|[^/{}]|\\\\[{}])+?)\\}");
+
+ private static final String DEFAULT_VARIABLE_PATTERN = "(.*)";
+
+ private final Pattern pattern;
+
+ private final List<String> variableNames = new LinkedList<String>();
+
+ public AntPathStringMatcher(String pattern) {
+ StringBuilder patternBuilder = new StringBuilder();
+ Matcher m = GLOB_PATTERN.matcher(pattern);
+ int end = 0;
+ while (m.find()) {
+ patternBuilder.append(quote(pattern, end, m.start()));
+ String match = m.group();
+ if ("?".equals(match)) {
+ patternBuilder.append('.');
+ }
+ else if ("*".equals(match)) {
+ patternBuilder.append(".*");
+ }
+ else if (match.startsWith("{") && match.endsWith("}")) {
+ int colonIdx = match.indexOf(':');
+ if (colonIdx == -1) {
+ patternBuilder.append(DEFAULT_VARIABLE_PATTERN);
+ this.variableNames.add(m.group(1));
+ }
+ else {
+ String variablePattern = match.substring(colonIdx + 1, match.length() - 1);
+ patternBuilder.append('(');
+ patternBuilder.append(variablePattern);
+ patternBuilder.append(')');
+ String variableName = match.substring(1, colonIdx);
+ this.variableNames.add(variableName);
+ }
+ }
+ end = m.end();
+ }
+ patternBuilder.append(quote(pattern, end, pattern.length()));
+ this.pattern = Pattern.compile(patternBuilder.toString());
+ }
+
+ private String quote(String s, int start, int end) {
+ if (start == end) {
+ return "";
+ }
+ return Pattern.quote(s.substring(start, end));
+ }
+
+ /**
+ * Main entry point.
+ * @return {@code true} if the string matches against the pattern, or {@code false} otherwise.
+ */
+ public boolean matchStrings(String str, Map<String, String> uriTemplateVariables) {
+ Matcher matcher = this.pattern.matcher(str);
+ if (matcher.matches()) {
+ if (uriTemplateVariables != null) {
+ // SPR-8455
+ if(!(this.variableNames.size() == matcher.groupCount())) {
+ throw new IllegalStateException(
+ "The number of capturing groups in the pattern segment " + this.pattern +
+ " does not match the number of URI template variables it defines, which can occur if " +
+ " capturing groups are used in a URI template regex. Use non-capturing groups instead.");
+ }
+ for (int i = 1; i <= matcher.groupCount(); i++) {
+ String name = this.variableNames.get(i - 1);
+ String value = matcher.group(i);
+ uriTemplateVariables.put(name, value);
+ }
+ }
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+ }
+
+
+ /**
+ * The default {@link Comparator} implementation returned by
+ * {@link #getPatternComparator(String)}.
+ * <p>In order, the most "generic" pattern is determined by the following:
+ * <ul>
+ * <li>if it's null or a capture all pattern (i.e. it is equal to "/**")</li>
+ * <li>if the other pattern is an actual match</li>
+ * <li>if it's a catch-all pattern (i.e. it ends with "**"</li>
+ * <li>if it's got more "*" than the other pattern</li>
+ * <li>if it's got more "{foo}" than the other pattern</li>
+ * <li>if it's shorter than the other pattern</li>
+ * </ul>
+ */
+ protected static class AntPatternComparator implements Comparator<String> {
+
+ private final String path;
+
+ public AntPatternComparator(String path) {
+ this.path = path;
+ }
+
+ /**
+ * Compare two patterns to determine which should match first, i.e. which
+ * is the most specific regarding the current path.
+ * @return a negative integer, zero, or a positive integer as pattern1 is
+ * more specific, equally specific, or less specific than pattern2.
+ */
+ @Override
+ public int compare(String pattern1, String pattern2) {
+ PatternInfo info1 = new PatternInfo(pattern1);
+ PatternInfo info2 = new PatternInfo(pattern2);
+
+ if (info1.isLeastSpecific() && info2.isLeastSpecific()) {
+ return 0;
+ }
+ else if (info1.isLeastSpecific()) {
+ return 1;
+ }
+ else if (info2.isLeastSpecific()) {
+ return -1;
+ }
+
+ boolean pattern1EqualsPath = pattern1.equals(path);
+ boolean pattern2EqualsPath = pattern2.equals(path);
+ if (pattern1EqualsPath && pattern2EqualsPath) {
+ return 0;
+ }
+ else if (pattern1EqualsPath) {
+ return -1;
+ }
+ else if (pattern2EqualsPath) {
+ return 1;
+ }
+
+ if (info1.isPrefixPattern() && info2.getDoubleWildcards() == 0) {
+ return 1;
+ }
+ else if (info2.isPrefixPattern() && info1.getDoubleWildcards() == 0) {
+ return -1;
+ }
+
+ if (info1.getTotalCount() != info2.getTotalCount()) {
+ return info1.getTotalCount() - info2.getTotalCount();
+ }
+
+ if (info1.getLength() != info2.getLength()) {
+ return info2.getLength() - info1.getLength();
+ }
+
+ if (info1.getSingleWildcards() < info2.getSingleWildcards()) {
+ return -1;
+ }
+ else if (info2.getSingleWildcards() < info1.getSingleWildcards()) {
+ return 1;
+ }
+
+ if (info1.getUriVars() < info2.getUriVars()) {
+ return -1;
+ }
+ else if (info2.getUriVars() < info1.getUriVars()) {
+ return 1;
+ }
+
+ return 0;
+ }
+
+
+ /**
+ * Value class that holds information about the pattern, e.g. number of
+ * occurrences of "*", "**", and "{" pattern elements.
+ */
+ private static class PatternInfo {
+
+ private final String pattern;
+
+ private int uriVars;
+
+ private int singleWildcards;
+
+ private int doubleWildcards;
+
+ private boolean catchAllPattern;
+
+ private boolean prefixPattern;
+
+ private Integer length;
+
+ public PatternInfo(String pattern) {
+ this.pattern = pattern;
+ if (this.pattern != null) {
+ initCounters();
+ this.catchAllPattern = this.pattern.equals("/**");
+ this.prefixPattern = !this.catchAllPattern && this.pattern.endsWith("/**");
+ }
+ if (this.uriVars == 0) {
+ this.length = (this.pattern != null ? this.pattern.length() : 0);
+ }
+ }
+
+ protected void initCounters() {
+ int pos = 0;
+ while (pos < this.pattern.length()) {
+ if (this.pattern.charAt(pos) == '{') {
+ this.uriVars++;
+ pos++;
+ }
+ else if (this.pattern.charAt(pos) == '*') {
+ if (pos + 1 < this.pattern.length() && this.pattern.charAt(pos + 1) == '*') {
+ this.doubleWildcards++;
+ pos += 2;
+ }
+ else if (!this.pattern.substring(pos - 1).equals(".*")) {
+ this.singleWildcards++;
+ pos++;
+ }
+ else {
+ pos++;
+ }
+ }
+ else {
+ pos++;
+ }
+ }
+ }
+
+ public int getUriVars() {
+ return this.uriVars;
+ }
+
+ public int getSingleWildcards() {
+ return this.singleWildcards;
+ }
+
+ public int getDoubleWildcards() {
+ return this.doubleWildcards;
+ }
+
+ public boolean isLeastSpecific() {
+ return (this.pattern == null || this.catchAllPattern);
+ }
+
+ public boolean isPrefixPattern() {
+ return this.prefixPattern;
+ }
+
+ public int getTotalCount() {
+ return this.uriVars + this.singleWildcards + (2 * this.doubleWildcards);
+ }
+
+ /**
+ * Returns the length of the given pattern, where template variables are considered to be 1 long.
+ */
+ public int getLength() {
+ if (this.length == null) {
+ this.length = VARIABLE_PATTERN.matcher(this.pattern).replaceAll("#").length();
+ }
+ return this.length;
+ }
+ }
+ }
+
+
+ /**
+ * A simple cache for patterns that depend on the configured path separator.
+ */
+ private static class PathSeparatorPatternCache {
+
+ private final String endsOnWildCard;
+
+ private final String endsOnDoubleWildCard;
+
+ public PathSeparatorPatternCache(String pathSeparator) {
+ this.endsOnWildCard = pathSeparator + "*";
+ this.endsOnDoubleWildCard = pathSeparator + "**";
+ }
+
+ public String getEndsOnWildCard() {
+ return this.endsOnWildCard;
+ }
+
+ public String getEndsOnDoubleWildCard() {
+ return this.endsOnDoubleWildCard;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/resources/io/ClassPathResource.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/resources/io/ClassPathResource.java b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/ClassPathResource.java
new file mode 100644
index 0000000..4d80f44
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/ClassPathResource.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tamaya.core.internal.resources.io;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Objects;
+
+/**
+ * {@link Resource} implementation for class path resources.
+ * Uses either a given ClassLoader or a given Class for loading resources.
+ *
+ * <p>Supports resolution as {@code java.io.File} if the class path
+ * resource resides in the file system, but not for resources in a JAR.
+ * Always supports resolution as URL.
+ *
+ * @author Juergen Hoeller
+ * @author Sam Brannen
+ * @since 28.12.2003
+ * @see ClassLoader#getResourceAsStream(String)
+ * @see Class#getResourceAsStream(String)
+ */
+public class ClassPathResource extends AbstractFileResolvingResource {
+
+ private final String path;
+
+ private ClassLoader classLoader;
+
+ private Class<?> clazz;
+
+
+ /**
+ * Create a new {@code ClassPathResource} for {@code ClassLoader} usage.
+ * A leading slash will be removed, as the ClassLoader resource access
+ * methods will not accept it.
+ * <p>The thread context class loader will be used for
+ * loading the resource.
+ * @param path the absolute path within the class path
+ * @see java.lang.ClassLoader#getResourceAsStream(String)
+ */
+ public ClassPathResource(String path) {
+ this(path, (ClassLoader) null);
+ }
+
+ /**
+ * Create a new {@code ClassPathResource} for {@code ClassLoader} usage.
+ * A leading slash will be removed, as the ClassLoader resource access
+ * methods will not accept it.
+ * @param path the absolute path within the classpath
+ * @param classLoader the class loader to load the resource with,
+ * or {@code null} for the thread context class loader
+ * @see ClassLoader#getResourceAsStream(String)
+ */
+ public ClassPathResource(String path, ClassLoader classLoader) {
+ Objects.requireNonNull(path, "Path must not be null");
+ String pathToUse = StringUtils.cleanPath(path);
+ if (pathToUse.startsWith("/")) {
+ pathToUse = pathToUse.substring(1);
+ }
+ this.path = pathToUse;
+ this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
+ }
+
+ /**
+ * Create a new {@code ClassPathResource} for {@code Class} usage.
+ * The path can be relative to the given class, or absolute within
+ * the classpath via a leading slash.
+ * @param path relative or absolute path within the class path
+ * @param clazz the class to load resources with
+ * @see java.lang.Class#getResourceAsStream
+ */
+ public ClassPathResource(String path, Class<?> clazz) {
+ Objects.requireNonNull(path, "Path must not be null");
+ this.path = StringUtils.cleanPath(path);
+ this.clazz = clazz;
+ }
+
+ /**
+ * Create a new {@code ClassPathResource} with optional {@code ClassLoader}
+ * and {@code Class}. Only for internal usage.
+ * @param path relative or absolute path within the classpath
+ * @param classLoader the class loader to load the resource with, if any
+ * @param clazz the class to load resources with, if any
+ */
+ protected ClassPathResource(String path, ClassLoader classLoader, Class<?> clazz) {
+ this.path = StringUtils.cleanPath(path);
+ this.classLoader = classLoader;
+ this.clazz = clazz;
+ }
+
+
+ /**
+ * Return the path for this resource (as resource path within the class path).
+ */
+ public final String getPath() {
+ return this.path;
+ }
+
+ /**
+ * Return the ClassLoader that this resource will be obtained from.
+ */
+ public final ClassLoader getClassLoader() {
+ return (this.clazz != null ? this.clazz.getClassLoader() : this.classLoader);
+ }
+
+
+ /**
+ * This implementation checks for the resolution of a resource URL.
+ * @see java.lang.ClassLoader#getResource(String)
+ * @see java.lang.Class#getResource(String)
+ */
+ @Override
+ public boolean exists() {
+ return (resolveURL() != null);
+ }
+
+ /**
+ * Resolves a URL for the underlying class path resource.
+ * @return the resolved URL, or {@code null} if not resolvable
+ */
+ protected URL resolveURL() {
+ if (this.clazz != null) {
+ return this.clazz.getResource(this.path);
+ }
+ else if (this.classLoader != null) {
+ return this.classLoader.getResource(this.path);
+ }
+ else {
+ return ClassLoader.getSystemResource(this.path);
+ }
+ }
+
+ /**
+ * This implementation opens an InputStream for the given class path resource.
+ * @see java.lang.ClassLoader#getResourceAsStream(String)
+ * @see java.lang.Class#getResourceAsStream(String)
+ */
+ @Override
+ public InputStream getInputStream()throws IOException {
+ InputStream is;
+ if (this.clazz != null) {
+ is = this.clazz.getResourceAsStream(this.path);
+ }
+ else if (this.classLoader != null) {
+ is = this.classLoader.getResourceAsStream(this.path);
+ }
+ else {
+ is = ClassLoader.getSystemResourceAsStream(this.path);
+ }
+ if (is == null) {
+ throw new IOException(getDescription() + " cannot be opened because it does not exist");
+ }
+ return is;
+ }
+
+ /**
+ * This implementation returns a URL for the underlying class path resource,
+ * if available.
+ * @see java.lang.ClassLoader#getResource(String)
+ * @see java.lang.Class#getResource(String)
+ */
+ @Override
+ public URL getURL() throws IOException {
+ URL url = resolveURL();
+ if (url == null) {
+ throw new FileNotFoundException(getDescription() + " cannot be resolved to URL because it does not exist");
+ }
+ return url;
+ }
+
+ /**
+ * This implementation creates a ClassPathResource, applying the given path
+ * relative to the path of the underlying resource of this descriptor.
+ */
+ @Override
+ public Resource createRelative(String relativePath) {
+ String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
+ return new ClassPathResource(pathToUse, this.classLoader, this.clazz);
+ }
+
+ /**
+ * This implementation returns the name of the file that this class path
+ * resource refers to.
+ */
+ @Override
+ public String getFilename() {
+ return StringUtils.getFilename(this.path);
+ }
+
+ /**
+ * This implementation returns a description that includes the class path location.
+ */
+ @Override
+ public String getDescription() {
+ StringBuilder builder = new StringBuilder("class path resource [");
+ String pathToUse = path;
+ if (this.clazz != null && !pathToUse.startsWith("/")) {
+ builder.append(ClassUtils.classPackageAsResourcePath(this.clazz));
+ builder.append('/');
+ }
+ if (pathToUse.startsWith("/")) {
+ pathToUse = pathToUse.substring(1);
+ }
+ builder.append(pathToUse);
+ builder.append(']');
+ return builder.toString();
+ }
+
+ /**
+ * This implementation compares the underlying class path locations.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj instanceof ClassPathResource) {
+ ClassPathResource otherRes = (ClassPathResource) obj;
+ return (this.path.equals(otherRes.path) &&
+ Objects.equals(this.classLoader, otherRes.classLoader) &&
+ Objects.equals(this.clazz, otherRes.clazz));
+ }
+ return false;
+ }
+
+ /**
+ * This implementation returns the hash code of the underlying
+ * class path location.
+ */
+ @Override
+ public int hashCode() {
+ return this.path.hashCode();
+ }
+
+}
[8/8] incubator-tamaya git commit: TAMAYA-14: added resource support.
TAMAYA-15: Moved PropertyProviders to API,
added SPI. TAMAYA-8: Added/improved Javadoc.
Posted by an...@apache.org.
TAMAYA-14: added resource support.
TAMAYA-15: Moved PropertyProviders to API, added SPI.
TAMAYA-8: Added/improved Javadoc.
Project: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/commit/a55d1c97
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/tree/a55d1c97
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/diff/a55d1c97
Branch: refs/heads/master
Commit: a55d1c973f66068cfd93a671c85001a95343cde6
Parents: bed1057
Author: anatole <an...@apache.org>
Authored: Mon Dec 1 10:43:33 2014 +0100
Committer: anatole <an...@apache.org>
Committed: Mon Dec 1 10:43:34 2014 +0100
----------------------------------------------------------------------
NOTICE | 1 +
.../org/apache/tamaya/AggregationPolicy.java | 38 +
.../apache/tamaya/ConfigChangeSetBuilder.java | 4 +-
.../java/org/apache/tamaya/ConfigFunctions.java | 107 ++
.../java/org/apache/tamaya/ConfigOperator.java | 2 +-
.../java/org/apache/tamaya/ConfigQuery.java | 2 +-
.../java/org/apache/tamaya/Configuration.java | 38 +-
.../org/apache/tamaya/ConfigurationManager.java | 11 +-
.../main/java/org/apache/tamaya/MetaInfo.java | 2 +-
.../java/org/apache/tamaya/PropertyAdapter.java | 2 +-
.../org/apache/tamaya/PropertyAdapters.java | 2 +-
.../org/apache/tamaya/PropertyProvider.java | 34 +-
.../org/apache/tamaya/PropertyProviders.java | 535 ++++++++
api/src/main/java/org/apache/tamaya/Stages.java | 2 +-
.../apache/tamaya/annot/ConfiguredProperty.java | 2 +-
.../spi/ConfigurationManagerSingletonSpi.java | 3 +
.../spi/PropertyProvidersSingletonSpi.java | 198 +++
.../core/config/ConfigurationBuilder.java | 11 +-
.../core/config/ConfigurationFormats.java | 10 +-
.../config/EnvPropertiesConfigProvider.java | 7 +-
.../core/config/FreezedConfiguration.java | 10 +-
.../config/SystemPropertiesConfigProvider.java | 7 +-
.../internal/AggregatedPropertyProvider.java | 137 ++
.../internal/ContextualPropertyProvider.java | 174 +++
.../core/internal/DefaultConfigProvider.java | 4 +-
.../DefaultPropertyProvidersSingletonSpi.java | 286 ++++
.../internal/DelegatingPropertyProvider.java | 103 ++
.../internal/EnvironmentPropertyProvider.java | 45 +
.../core/internal/FilteredPropertyProvider.java | 72 +
.../core/internal/FreezedPropertyProvider.java | 98 ++
.../internal/IntersectingPropertyProvider.java | 77 ++
.../core/internal/MapBasedPropertyProvider.java | 107 ++
.../apache/tamaya/core/internal/MetaConfig.java | 4 +-
.../internal/PathBasedPropertyProvider.java | 93 ++
.../internal/ReplacingPropertyProvider.java | 111 ++
.../internal/SubtractingPropertyProvider.java | 76 ++
.../SystemPropertiesPropertyProvider.java | 53 +
.../core/internal/UriBasedPropertyProvider.java | 92 ++
...DependentApplicationEnvironmentProvider.java | 2 +-
...ssLoaderDependentEarEnvironmentProvider.java | 2 +-
.../SystemClassLoaderEnvironmentProvider.java | 4 +-
.../tamaya/core/internal/format/IniFormat.java | 1 +
.../core/internal/inject/ConfiguredField.java | 4 +-
.../inject/ConfiguredInstancesManager.java | 2 +-
.../core/internal/inject/ConfiguredMethod.java | 2 +-
.../core/internal/logging/Slf4jLogger.java | 2 +-
.../resources/AntPathClasspathResolver.java | 37 +-
.../resources/AntPathClasspathsResolver.java | 59 +-
.../internal/resources/AntPathFileResolver.java | 22 +-
.../resources/AntPathFilesResolver.java | 22 +-
.../io/AbstractFileResolvingResource.java | 219 ++++
.../internal/resources/io/AbstractResource.java | 215 +++
.../internal/resources/io/AntPathMatcher.java | 777 +++++++++++
.../resources/io/ClassPathResource.java | 250 ++++
.../core/internal/resources/io/ClassUtils.java | 1232 ++++++++++++++++++
.../resources/io/DefaultResourceLoader.java | 136 ++
.../resources/io/FileSystemResource.java | 218 ++++
.../resources/io/InputStreamResource.java | 123 ++
.../resources/io/InputStreamSource.java | 55 +
.../io/PathMatchingResourcePatternResolver.java | 728 +++++++++++
.../internal/resources/io/ReflectionUtils.java | 191 +++
.../core/internal/resources/io/Resource.java | 135 ++
.../internal/resources/io/ResourceUtils.java | 350 +++++
.../core/internal/resources/io/StringUtils.java | 1173 +++++++++++++++++
.../core/internal/resources/io/UrlResource.java | 261 ++++
.../core/internal/resources/io/VfsResource.java | 132 ++
.../core/internal/resources/io/VfsUtils.java | 209 +++
.../internal/resources/io/WritableResource.java | 52 +
.../properties/AbstractPropertyProvider.java | 2 +-
.../properties/AggregatedPropertyProvider.java | 137 --
.../core/properties/AggregationPolicy.java | 38 -
.../properties/ContextualPropertyProvider.java | 176 ---
.../properties/DelegatingPropertyProvider.java | 103 --
.../properties/EnvironmentPropertyProvider.java | 43 -
.../properties/FilteredPropertyProvider.java | 71 -
.../properties/FreezedPropertyProvider.java | 94 --
.../IntersectingPropertyProvider.java | 76 --
.../properties/MapBasedPropertyProvider.java | 106 --
.../MapBasedPropertyProviderBuilder.java | 8 +-
.../properties/PathBasedPropertyProvider.java | 74 --
.../properties/PropertyProviderManager.java | 2 +-
.../core/properties/PropertyProviders.java | 457 -------
.../properties/ReplacingPropertyProvider.java | 111 --
.../apache/tamaya/core/properties/Store.java | 2 +-
.../properties/SubtractingPropertyProvider.java | 75 --
.../SystemPropertiesPropertyProvider.java | 52 -
.../properties/UriBasedPropertyProvider.java | 68 -
.../tamaya/core/spi/ConfigurationFormat.java | 2 +-
.../tamaya/core/spi/EnvironmentProvider.java | 2 +-
.../tamaya/core/spi/ExpressionResolver.java | 2 +-
.../apache/tamaya/core/spi/ResourceLoader.java | 2 +-
...che.tamaya.spi.PropertyProvidersSingletonSpi | 19 +
...tionManagerSingletonSpiSingletonSpiTest.java | 14 +-
.../java/org/apache/tamaya/JavaOneDemo.java | 5 +-
.../core/properties/PropertyProvidersTest.java | 1 +
.../internal/MutableTestConfigProvider.java | 4 +-
.../simple/SimplePropertiesAndCLISample.java | 8 +-
.../UC1ReadPropertiesTest.java | 218 ++++
.../UC1ReadProperties/UC1ReadPropertiesTest.ini | 23 +
.../UC1ReadPropertiesTest.properties | 16 +
.../UC1ReadProperties/UC1ReadPropertiesTest.xml | 25 +
docs/design/0_UseCases.adoc | 223 ++--
.../apache/tamaya/management/ManagedConfig.java | 2 +-
.../tamaya/management/ManagedConfigMBean.java | 2 +-
104 files changed, 9536 insertions(+), 1902 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/NOTICE
----------------------------------------------------------------------
diff --git a/NOTICE b/NOTICE
index 1be4b73..716885f 100644
--- a/NOTICE
+++ b/NOTICE
@@ -9,3 +9,4 @@ Juven Xu (juvenshun at gmail dot com)
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
+The Spring Framework (http://spring.io/projects).
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/api/src/main/java/org/apache/tamaya/AggregationPolicy.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/AggregationPolicy.java b/api/src/main/java/org/apache/tamaya/AggregationPolicy.java
new file mode 100644
index 0000000..1b02a6c
--- /dev/null
+++ b/api/src/main/java/org/apache/tamaya/AggregationPolicy.java
@@ -0,0 +1,38 @@
+/*
+ * 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;
+
+/**
+ * Policy that defines how the different aggregates should be combined.
+ */
+public enum AggregationPolicy{
+ /** Ignore overrides, only extend (additive). */
+ IGNORE,
+ /**
+ * Interpret later keys as override (additive and override), replacing
+ * the key loaded earlier/fromMap previous contained
+ * {@link org.apache.tamaya.PropertyProvider}.
+ */
+ OVERRIDE,
+ /**
+ * Throw an exception, when keys are not disjunctive (strictly
+ * additive).
+ */
+ EXCEPTION
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/api/src/main/java/org/apache/tamaya/ConfigChangeSetBuilder.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/ConfigChangeSetBuilder.java b/api/src/main/java/org/apache/tamaya/ConfigChangeSetBuilder.java
index be6b906..8f21228 100644
--- a/api/src/main/java/org/apache/tamaya/ConfigChangeSetBuilder.java
+++ b/api/src/main/java/org/apache/tamaya/ConfigChangeSetBuilder.java
@@ -113,7 +113,7 @@ public final class ConfigChangeSetBuilder {
}
/**
- * Marks the given key(s) from the configuration/properties to be removed.
+ * Marks the given key(s) fromMap the configuration/properties to be removed.
* @param key the key of the entry, not null.
* @param otherKeys additional keys to be removed (convenience), not null.
* @return the builder for chaining.
@@ -256,7 +256,7 @@ public final class ConfigChangeSetBuilder {
}
/**
- * This method will create a change set that clears all entries from the given base configuration/properties.
+ * This method will create a change set that clears all entries fromMap the given base configuration/properties.
* @return the builder for chaining.
*/
public ConfigChangeSetBuilder clear() {
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/api/src/main/java/org/apache/tamaya/ConfigFunctions.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/ConfigFunctions.java b/api/src/main/java/org/apache/tamaya/ConfigFunctions.java
new file mode 100644
index 0000000..a466b63
--- /dev/null
+++ b/api/src/main/java/org/apache/tamaya/ConfigFunctions.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tamaya;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * Accessor that provides useful functions along with configuration.
+ */
+public final class ConfigFunctions {
+ /**
+ * Private singleton constructor.
+ */
+ private ConfigFunctions(){}
+
+ /**
+ * Creates a ConfigOperator that creates a Configuration containing only keys
+ * that are contained in the given area (non recursive). Hereby
+ * the area key is stripped away fromMap the resulting key.
+ * @param areaKey the area key, not null
+ * @return the area configuration, with the areaKey stripped away.
+ */
+ public static ConfigOperator selectArea(String areaKey) {
+ return selectArea(areaKey, true);
+ }
+
+ /**
+ * Creates a ConfigOperator that creates a Configuration containing only keys
+ * that are contained in the given area (non recursive).
+ * @param areaKey the area key, not null
+ * @param stripKeys if set to true, the area key is stripped away fromMap the resulting key.
+ * @return the area configuration, with the areaKey stripped away.
+ */
+ public static ConfigOperator selectArea(String areaKey, boolean stripKeys){
+ return config -> {
+ Map<String, String> area = new HashMap<>();
+ area.putAll(
+ config.toMap().entrySet().stream()
+ .filter(e -> isKeyInArea(e.getKey(), areaKey))
+ .collect(Collectors.toMap(
+ e -> stripKeys? e.getKey().substring(areaKey.length()+1) : e.getKey(),
+ e -> e.getValue())));
+ return PropertyProviders.fromMap(area).toConfiguration();
+ };
+ }
+
+ /**
+ * Calculates the current area key and compares it with the given key.
+ * @param key the fully qualified entry key, not null
+ * @param areaKey the area key, not null
+ * @return true, if the entry is exact in this area
+ */
+ public static boolean isKeyInArea(String key, String areaKey){
+ int lastIndex = key.lastIndexOf('.');
+ String curAreaKey = lastIndex > 0 ? key.substring(0, lastIndex) : "";
+ return curAreaKey.equals(areaKey);
+ }
+
+ /**
+ * Creates a ConfigOperator that creates a Configuration containing only keys
+ * that are contained in the given area (recursive). Hereby
+ * the area key is stripped away fromMap the resulting key.
+ * @param areaKey the area key, not null
+ * @return the area configuration, with the areaKey stripped away.
+ */
+ public static ConfigOperator selectAreaRecursive(String areaKey) {
+ return selectAreaRecursive(areaKey, true);
+ }
+
+ /**
+ * Creates a ConfigOperator that creates a Configuration containing only keys
+ * that are contained in the given area (recursive).
+ * @param areaKey the area key, not null
+ * @param stripKeys if set to true, the area key is stripped away fromMap the resulting key.
+ * @return the area configuration, with the areaKey stripped away.
+ */
+ public static ConfigOperator selectAreaRecursive(String areaKey, boolean stripKeys){
+ return config -> {
+ Map<String, String> area = new HashMap<>();
+ String lookupKey = areaKey + '.';
+ area.putAll(
+ config.toMap().entrySet().stream()
+ .filter(e -> e.getKey().startsWith(lookupKey))
+ .collect(Collectors.toMap(
+ e -> stripKeys? e.getKey().substring(areaKey.length()+1) : e.getKey(),
+ e -> e.getValue())));
+ return PropertyProviders.fromMap(area).toConfiguration();
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/api/src/main/java/org/apache/tamaya/ConfigOperator.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/ConfigOperator.java b/api/src/main/java/org/apache/tamaya/ConfigOperator.java
index a59712b..b76fa31 100644
--- a/api/src/main/java/org/apache/tamaya/ConfigOperator.java
+++ b/api/src/main/java/org/apache/tamaya/ConfigOperator.java
@@ -27,7 +27,7 @@ package org.apache.tamaya;
public interface ConfigOperator{
/**
- * Method that creates a Configuration from another Configuration. This can be used for implementing
+ * Method that creates a Configuration fromMap another Configuration. This can be used for implementing
* views, security constraints or for overriding/inheriting of configuration.
* @param config The target configuration to be operated, never nnull.
* @return the operated configuration, never null.
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/api/src/main/java/org/apache/tamaya/ConfigQuery.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/ConfigQuery.java b/api/src/main/java/org/apache/tamaya/ConfigQuery.java
index c296d12..58d8742 100644
--- a/api/src/main/java/org/apache/tamaya/ConfigQuery.java
+++ b/api/src/main/java/org/apache/tamaya/ConfigQuery.java
@@ -21,7 +21,7 @@ package org.apache.tamaya;
/**
* Interface for an query that converts a Configuration into another object. One typical
- * use cases would creating a complex configuration parameter type from a Configuration instance or
+ * use cases would creating a complex configuration parameter type fromMap a Configuration instance or
* constraint views on configuration.
*/
@FunctionalInterface
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/api/src/main/java/org/apache/tamaya/Configuration.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/Configuration.java b/api/src/main/java/org/apache/tamaya/Configuration.java
index a23f44c..7d5500c 100644
--- a/api/src/main/java/org/apache/tamaya/Configuration.java
+++ b/api/src/main/java/org/apache/tamaya/Configuration.java
@@ -118,12 +118,12 @@ public interface Configuration extends PropertyProvider{
* If {@code Class<T>} is not one of
* {@code Boolean, Short, Integer, Long, Float, Double, BigInteger,
* BigDecimal, String} , an according adapter must be
- * available to perform the conversion from {@link String} to
+ * available to perform the conversion fromMap {@link String} to
* {@code Class<T>}.
*
* @param key the property's absolute, or relative path, e.g. @code
* a/b/c/d.myProperty}.
- * @param adapter the PropertyAdapter to perform the conversion from
+ * @param adapter the PropertyAdapter to perform the conversion fromMap
* {@link String} to {@code Class<T>}, not {@code null}.
* @return the property's value.
* @throws IllegalArgumentException if the value could not be converted to the required target
@@ -141,7 +141,7 @@ public interface Configuration extends PropertyProvider{
/**
* Get the property value as type T. This will implicitly require a corresponding {@link
* PropertyAdapter} to be available that is capable of providing type T
- * from the given String value.
+ * fromMap the given String value.
*
* @param key the property's absolute, or relative path, e.g. @code
* a/b/c/d.myProperty}.
@@ -150,7 +150,9 @@ public interface Configuration extends PropertyProvider{
* @throws IllegalArgumentException if the value could not be converted to the required target
* type.
*/
- <T> Optional<T> get(String key, Class<T> type);
+ default <T> Optional<T> get(String key, Class<T> type){
+ return getAdapted(key, PropertyAdapters.getAdapter(type));
+ }
/**
* Return a set with all fully qualifies area names.
@@ -219,11 +221,11 @@ public interface Configuration extends PropertyProvider{
/**
* Allows to evaluate if an area exists.
*
- * @param key the configuration area (sub)path.
+ * @param areaKey the configuration area (sub)path.
* @return {@code true}, if such a node exists.
*/
- default boolean containsArea(String key){
- return getAreas().contains(key);
+ default boolean containsArea(String areaKey){
+ return getAreas().contains(areaKey);
}
/**
@@ -238,7 +240,7 @@ public interface Configuration extends PropertyProvider{
}
/**
- * Query some value from a configuration.
+ * Query some value fromMap a configuration.
*
* @param query the query, never {@code null}.
* @return the result
@@ -259,13 +261,21 @@ public interface Configuration extends PropertyProvider{
* Add a ConfigChangeListener to this configuration instance.
* @param l the listener, not null.
*/
- void addPropertyChangeListener(PropertyChangeListener l);
+ default void addPropertyChangeListener(PropertyChangeListener l){
+ addConfigChangeListener((p) -> {
+ if (p.getSource() == this) l.propertyChange(p);
+ });
+ }
/**
* Removes a ConfigChangeListener to this configuration instance.
* @param l the listener, not null.
*/
- void removePropertyChangeListener(PropertyChangeListener l);
+ default void removePropertyChangeListener(PropertyChangeListener l){
+ removeConfigChangeListener((p) -> {
+ if (p.getSource() == this) l.propertyChange(p);
+ });
+ }
/**
* Allows to check if a configuration with a given name is defined.
@@ -361,8 +371,8 @@ public interface Configuration extends PropertyProvider{
* Adds a (global) {@link java.beans.PropertyChangeListener} instance that listens to all kind of config changes.
* @param listener the {@link java.beans.PropertyChangeListener} instance to be added, not null.
*/
- public static void addGlobalPropertyChangeListener(PropertyChangeListener listener){
- ConfigurationManager.addPropertyChangeListener(listener);
+ public static void addConfigChangeListener(PropertyChangeListener listener){
+ ConfigurationManager.addConfigChangeListener(listener);
}
/**
@@ -370,7 +380,7 @@ public interface Configuration extends PropertyProvider{
* if one is currently registered.
* @param listener the {@link java.beans.PropertyChangeListener} instance to be removed, not null.
*/
- public static void removeGlobalPropertyChangeListener(PropertyChangeListener listener){
- ConfigurationManager.removePropertyChangeListener(listener);
+ public static void removeConfigChangeListener(PropertyChangeListener listener){
+ ConfigurationManager.removeConfigChangeListener(listener);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/api/src/main/java/org/apache/tamaya/ConfigurationManager.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/ConfigurationManager.java b/api/src/main/java/org/apache/tamaya/ConfigurationManager.java
index 67d4ccb..62cd984 100644
--- a/api/src/main/java/org/apache/tamaya/ConfigurationManager.java
+++ b/api/src/main/java/org/apache/tamaya/ConfigurationManager.java
@@ -22,7 +22,9 @@ import org.apache.tamaya.spi.Bootstrap;
import org.apache.tamaya.spi.ConfigurationManagerSingletonSpi;
import java.beans.PropertyChangeListener;
-import java.util.Optional;
+import java.util.*;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
/**
* Singleton accessor for accessing {@link Configuration} instances and
@@ -41,7 +43,7 @@ final class ConfigurationManager{
}
/**
- * Method to initially load the singleton SPI from the {@link org.apache.tamaya.spi.Bootstrap} mechanism.
+ * Method to initially load the singleton SPI fromMap the {@link org.apache.tamaya.spi.Bootstrap} mechanism.
* The instance loaded will be used until the VM is shutdown. In case use cases require more flexibility
* it should be transparently implemented in the SPI implementation. This singleton will simply delegate calls
* and not cache any responses.
@@ -146,7 +148,7 @@ final class ConfigurationManager{
* Adds a (global) {@link java.beans.PropertyChangeListener} instance that listens to all kind of config changes.
* @param listener the {@link java.beans.PropertyChangeListener} instance to be added, not null.
*/
- public static void addPropertyChangeListener(PropertyChangeListener listener){
+ public static void addConfigChangeListener(PropertyChangeListener listener){
Optional.of(configManagerSingletonSpi).get().addPropertyChangeListener(listener);
}
@@ -155,7 +157,8 @@ final class ConfigurationManager{
* if one is currently registered.
* @param listener the {@link java.beans.PropertyChangeListener} instance to be removed, not null.
*/
- public static void removePropertyChangeListener(PropertyChangeListener listener){
+ public static void removeConfigChangeListener(PropertyChangeListener listener){
Optional.of(configManagerSingletonSpi).get().removePropertyChangeListener(listener);
}
+
}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/api/src/main/java/org/apache/tamaya/MetaInfo.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/MetaInfo.java b/api/src/main/java/org/apache/tamaya/MetaInfo.java
index 3d20723..702d4be 100644
--- a/api/src/main/java/org/apache/tamaya/MetaInfo.java
+++ b/api/src/main/java/org/apache/tamaya/MetaInfo.java
@@ -31,7 +31,7 @@ public final class MetaInfo{
private final Map<String, String> metaInfo = new HashMap<>();
/**
- * Constructor, used from the builder.
+ * Constructor, used fromMap the builder.
* @param builder the builder, not null.
*/
MetaInfo(MetaInfoBuilder builder){
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/api/src/main/java/org/apache/tamaya/PropertyAdapter.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/PropertyAdapter.java b/api/src/main/java/org/apache/tamaya/PropertyAdapter.java
index 8a238b4..96d1426 100644
--- a/api/src/main/java/org/apache/tamaya/PropertyAdapter.java
+++ b/api/src/main/java/org/apache/tamaya/PropertyAdapter.java
@@ -21,7 +21,7 @@ package org.apache.tamaya;
/**
* Interface for an adapter that converts a configured String into something else.
- * This is typically used for implementing type conversion from String to a certain target
+ * This is typically used for implementing type conversion fromMap String to a certain target
* type of the configured property.
*/
@FunctionalInterface
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/api/src/main/java/org/apache/tamaya/PropertyAdapters.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/PropertyAdapters.java b/api/src/main/java/org/apache/tamaya/PropertyAdapters.java
index d967e3f..1934591 100644
--- a/api/src/main/java/org/apache/tamaya/PropertyAdapters.java
+++ b/api/src/main/java/org/apache/tamaya/PropertyAdapters.java
@@ -32,7 +32,7 @@ public final class PropertyAdapters{
private static final PropertyAdaptersSingletonSpi propertyAdaptersSingletonSpi = loadConfigAdapterProviderSpi();
/**
- * Method that loads the singleton backing bean from the {@link org.apache.tamaya.spi.Bootstrap} component.
+ * Method that loads the singleton backing bean fromMap the {@link org.apache.tamaya.spi.Bootstrap} component.
* @return the PropertyAdaptersSingletonSpi, never null.
*/
private static PropertyAdaptersSingletonSpi loadConfigAdapterProviderSpi(){
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/api/src/main/java/org/apache/tamaya/PropertyProvider.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/PropertyProvider.java b/api/src/main/java/org/apache/tamaya/PropertyProvider.java
index faf263e..44032c9 100644
--- a/api/src/main/java/org/apache/tamaya/PropertyProvider.java
+++ b/api/src/main/java/org/apache/tamaya/PropertyProvider.java
@@ -24,7 +24,7 @@ import java.util.Set;
/**
* This interface models a provider that serves configuration properties. The contained
- * properties may be read from single or several sources (composite).<br/>
+ * properties may be read fromMap single or several sources (composite).<br/>
* Property providers are the building blocks out of which complex
* configuration is setup.
* <p/>
@@ -114,4 +114,36 @@ public interface PropertyProvider {
throw new UnsupportedOperationException("Config/properties not mutable: "+ this);
}
+ /**
+ * Allows to quickly check, if a provider is empty.
+ * @return true, if the provier is empty.
+ */
+ default boolean isEmpty(){
+ return keySet().isEmpty();
+ }
+
+ /**
+ * Convert the this PropertyProvider instance to a {@link org.apache.tamaya.Configuration}.
+ * @return the configuration, never null.
+ */
+ default Configuration toConfiguration(){
+ return new Configuration(){
+ @Override
+ public Optional<String> get(String key) {
+ return PropertyProvider.this.get(key);
+ }
+ @Override
+ public boolean containsKey(String key) {
+ return PropertyProvider.this.containsKey(key);
+ }
+ @Override
+ public Map<String, String> toMap() {
+ return PropertyProvider.this.toMap();
+ }
+ @Override
+ public MetaInfo getMetaInfo() {
+ return PropertyProvider.this.getMetaInfo();
+ }
+ };
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/api/src/main/java/org/apache/tamaya/PropertyProviders.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/PropertyProviders.java b/api/src/main/java/org/apache/tamaya/PropertyProviders.java
new file mode 100644
index 0000000..5a9aebd
--- /dev/null
+++ b/api/src/main/java/org/apache/tamaya/PropertyProviders.java
@@ -0,0 +1,535 @@
+/*
+ * 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.Bootstrap;
+import org.apache.tamaya.spi.PropertyProvidersSingletonSpi;
+
+import java.net.URI;
+import java.util.*;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.logging.Logger;
+
+/**
+ * Accessor factory for several standard {@link PropertyProvider} instances, usable for creating {@code Configuration}
+ * parts.
+ */
+public final class PropertyProviders {
+
+ private static final PropertyProvidersSingletonSpi spi = loadPropertyProvidersSingletonSpi();
+
+ private static final Logger LOG = Logger.getLogger(PropertyProviders.class.getName());
+
+ /**
+ * Private singleton constructor.
+ */
+ private PropertyProviders() {
+ }
+
+ /**
+ * Method to initially load the singleton SPI fromMap the {@link org.apache.tamaya.spi.Bootstrap} mechanism.
+ * The instance loaded will be used until the VM is shutdown. In case use cases require more flexibility
+ * it should be transparently implemented in the SPI implementation. This singleton will simply delegate calls
+ * and not cache any responses.
+ *
+ * @return the SPI, never null.
+ */
+ private static PropertyProvidersSingletonSpi loadPropertyProvidersSingletonSpi() {
+ return Bootstrap.getService(PropertyProvidersSingletonSpi.class);
+ }
+
+ /**
+ * Creates a new {@link }PropertyMap} using the given command line arguments
+ *
+ * @param args the command line arguments, not null.
+ * @return a new {@link }PropertyMap} instance with the given arguments contained as properties.
+ */
+ public static PropertyProvider fromArgs(String... args) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .fromArgs(null, args);
+ }
+
+ /**
+ * Creates a new {@link }PropertyMap} using the given command line arguments
+ *
+ * @param metaInfo the meta information to be provided additionally.
+ * @param args the command line arguments, not null.
+ * @return a new {@link }PropertyMap} instance with the given arguments contained as properties.
+ */
+ public static PropertyProvider fromArgs(MetaInfo metaInfo, String... args) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .fromArgs(metaInfo, args);
+ }
+
+ /**
+ * Creates a new read-only {@link PropertyProvider} by reading the according path resources. The effective resources read
+ * hereby are determined by the {@code PathResolverService} configured into the {@code Bootstrap} SPI.
+ * Properties read fromMap resources evaluated on
+ * paths with lower order are overriding any duplicate values fromMap previous paths hereby.
+ *
+ * @param aggregationPolicy the aggregationPolicy to be used, not null.
+ * @param paths the paths to be resolved by the {@code PathResolverService} , not null.
+ * @return a new {@link }PropertyMap} instance with the given paths contained as properties.
+ */
+ public static PropertyProvider fromPaths(AggregationPolicy aggregationPolicy, String... paths) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .fromPaths(aggregationPolicy, null, Arrays.asList(paths));
+ }
+
+ /**
+ * Creates a new read-only {@link PropertyProvider} by reading the according path resources. The effective resources read
+ * hereby are determined by the {@code PathResolverService} configured into the {@code Bootstrap} SPI.
+ * Properties read fromMap resources evaluated on
+ * paths with lower order are overriding any duplicate values fromMap previous paths hereby.
+ *
+ * @param paths the paths to be resolved by the {@code PathResolverService} , not null.
+ * @return a new {@link }PropertyMap} instance with the given paths contained as properties.
+ * @throws ConfigException if duplicate entries are encountered (AggregationPolicy.EXCEPTION).
+ */
+ public static PropertyProvider fromPaths(String... paths) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .fromPaths(AggregationPolicy.EXCEPTION, null, Arrays.asList(paths));
+ }
+
+
+ /**
+ * Creates a new read-only {@link PropertyProvider} by reading the according path resources. The effective resources read
+ * hereby are determined by the {@code PathResolverService} configured into the {@code Bootstrap} SPI.
+ * Properties read fromMap resources evaluated on
+ * paths with lower order are overriding any duplicate values fromMap previous paths hereby.
+ *
+ * @param paths the paths to be resolved by the {@code PathResolverService} , not null.
+ * @return a new {@link }PropertyMap} instance with the given paths contained as properties.
+ * @throws ConfigException if duplicate entries are encountered (AggregationPolicy.EXCEPTION).
+ */
+ public static PropertyProvider fromPaths(List<String> paths) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .fromPaths(AggregationPolicy.EXCEPTION, null, paths);
+ }
+
+ /**
+ * Creates a new read-only {@link PropertyProvider} by reading the according path resources. The effective resources read
+ * hereby are determined by the {@code PathResolverService} configured into the {@code Bootstrap} SPI.
+ * Properties read fromMap resources evaluated on
+ * paths with lower order are overriding any duplicate values fromMap previous paths hereby.
+ *
+ * @param aggregationPolicy the aggregationPolicy to be used, not null.
+ * @param paths the paths to be resolved by the {@code PathResolverService} , not null.
+ * @return a new {@link }PropertyMap} instance with the given paths contained as properties.
+ */
+ public static PropertyProvider fromPaths(AggregationPolicy aggregationPolicy, List<String> paths) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .fromPaths(aggregationPolicy, null, paths);
+ }
+
+ /**
+ * Creates a new read-only {@link PropertyProvider} by reading the according path resources. The effective resources read
+ * hereby are determined by the {@code PathResolverService} configured into the {@code Bootstrap} SPI.
+ * Properties read fromMap resources evaluated on
+ * paths with lower order are overriding any duplicate values fromMap previous paths hereby.
+ *
+ * @param metaInfo the meat information to be provided additionally.
+ * @param paths the paths to be resolved by the {@code PathResolverService} , not null.
+ * @return a new {@link }PropertyMap} instance with the given paths contained as properties.
+ * @throws ConfigException if duplicate entries are encountered (AggregationPolicy.EXCEPTION).
+ */
+ public static PropertyProvider fromPaths(MetaInfo metaInfo, List<String> paths) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .fromPaths(AggregationPolicy.EXCEPTION, metaInfo, paths);
+ }
+
+ /**
+ * Creates a new read-only {@link PropertyProvider} by reading the according path resources. The effective resources read
+ * hereby are determined by the {@code PathResolverService} configured into the {@code Bootstrap} SPI.
+ * Properties read fromMap resources evaluated on
+ * paths with lower order are overriding any duplicate values fromMap previous paths hereby.
+ * @param aggregationPolicy the aggregationPolicy to be used, not null.
+ * @param metaInfo the meat information to be provided additionally.
+ * @param paths the paths to be resolved by the {@code PathResolverService} , not null.
+ * @return a new {@link }PropertyMap} instance with the given paths contained as properties.
+ * @throws ConfigException if duplicate entries are encountered (AggregationPolicy.EXCEPTION).
+ */
+ public static PropertyProvider fromPaths(AggregationPolicy aggregationPolicy, MetaInfo metaInfo, List<String> paths) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .fromPaths(aggregationPolicy, metaInfo, paths);
+ }
+
+ /**
+ * Creates a new read-only {@link PropertyProvider} based on the resources defined by the given paths. The effective resources
+ * read hereby are determined by the {@code PathResolverService} configured into the {@code Bootstrap} SPI.
+ *
+ * @param uris the uris to be read, not null.
+ * @return a new {@link }PropertyMap} instance based on the given paths/resources found.
+ * @throws ConfigException if duplicate entries are encountered (AggregationPolicy.EXCEPTION).
+ */
+ public static PropertyProvider fromUris(URI... uris) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .fromUris(AggregationPolicy.EXCEPTION, null, Arrays.asList(uris));
+ }
+
+ /**
+ * Creates a new read-only {@link PropertyProvider} based on the resources defined by the given paths. The effective resources
+ * read hereby are determined by the {@code PathResolverService} configured into the {@code Bootstrap} SPI.
+ * @param aggregationPolicy the aggregationPolicy to be used, not null.
+ * @param uris the uris to be read, not null.
+ * @return a new {@link }PropertyMap} instance based on the given paths/resources found.
+ * @throws ConfigException if duplicate entries are encountered (AggregationPolicy.EXCEPTION).
+ */
+ public static PropertyProvider fromUris(AggregationPolicy aggregationPolicy, URI... uris) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .fromUris(aggregationPolicy, null, Arrays.asList(uris));
+ }
+
+ /**
+ * Creates a new read-only {@link PropertyProvider} based on the resources defined by the given paths. The effective resources
+ * read hereby are determined by the {@code PathResolverService} configured into the {@code Bootstrap} SPI.
+ *
+ * @param uris the uris to be read, not null.
+ * @return a new {@link }PropertyMap} instance based on the given paths/resources found.
+ * @throws ConfigException if duplicate entries are encountered (AggregationPolicy.EXCEPTION).
+ */
+ public static PropertyProvider fromUris(List<URI> uris) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .fromUris(AggregationPolicy.EXCEPTION, null, uris);
+ }
+
+ /**
+ * Creates a new read-only {@link PropertyProvider} based on the resources defined by the given paths. The effective resources
+ * read hereby are determined by the {@code PathResolverService} configured into the {@code Bootstrap} SPI.
+ * @param aggregationPolicy the aggregationPolicy to be used, not null.
+ * @param uris the uris to be read, not null.
+ * @return a new {@link }PropertyMap} instance based on the given paths/resources found.
+ * @throws ConfigException if duplicate entries are encountered (AggregationPolicy.EXCEPTION).
+ */
+ public static PropertyProvider fromUris(AggregationPolicy aggregationPolicy, List<URI> uris) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .fromUris(aggregationPolicy, null, uris);
+ }
+
+ /**
+ * Creates a new read-only {@link PropertyProvider} based on the resources defined by the given paths. The effective resources
+ * read hereby are determined by the {@code PathResolverService} configured into the {@code Bootstrap} SPI.
+ *
+ * @param metaInfo the meat information to be provided additionally.
+ * @param uris the uris to be read, not null.
+ * @return a new {@link }PropertyMap} instance based on the given paths/resources found.
+ * @throws ConfigException if duplicate entries are encountered (AggregationPolicy.EXCEPTION).
+ */
+ public static PropertyProvider fromUris(MetaInfo metaInfo, URI... uris) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .fromUris(AggregationPolicy.EXCEPTION, metaInfo, Arrays.asList(uris));
+ }
+
+ /**
+ * Creates a new read-only {@link PropertyProvider} based on the resources defined by the given paths. The effective resources
+ * read hereby are determined by the {@code PathResolverService} configured into the {@code Bootstrap} SPI.
+ * @param aggregationPolicy the aggregationPolicy to be used, not null.
+ * @param metaInfo the meat information to be provided additionally.
+ * @param uris the uris to be read, not null.
+ * @return a new {@link }PropertyMap} instance based on the given paths/resources found.
+ */
+ public static PropertyProvider fromUris(AggregationPolicy aggregationPolicy, MetaInfo metaInfo, URI... uris) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .fromUris(aggregationPolicy, metaInfo, Arrays.asList(uris));
+ }
+
+ /**
+ * Creates a new read-only {@link PropertyProvider} based on the resources defined by the given paths. The effective resources
+ * read hereby are determined by the {@code PathResolverService} configured into the {@code Bootstrap} SPI.
+ *
+ * @param metaInfo the meat information to be provided additionally.
+ * @param uris the uris to be read, not null.
+ * @return a new {@link }PropertyMap} instance based on the given paths/resources found.
+ * @throws ConfigException if duplicate entries are encountered (AggregationPolicy.EXCEPTION).
+ */
+ public static PropertyProvider fromUris(MetaInfo metaInfo, List<URI> uris) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .fromUris(AggregationPolicy.EXCEPTION, metaInfo, uris);
+ }
+
+ /**
+ * Creates a new read-only {@link PropertyProvider} based on the resources defined by the given paths. The effective resources
+ * read hereby are determined by the {@code PathResolverService} configured into the {@code Bootstrap} SPI.
+ * @param aggregationPolicy the aggregationPolicy to be used, not null.
+ * @param metaInfo the meat information to be provided additionally.
+ * @param uris the uris to be read, not null.
+ * @return a new {@link }PropertyMap} instance based on the given paths/resources found.
+ */
+ public static PropertyProvider fromUris(AggregationPolicy aggregationPolicy, MetaInfo metaInfo, List<URI> uris) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .fromUris(aggregationPolicy, metaInfo, uris);
+ }
+
+ /**
+ * Creates a new read-only {@link org.apache.tamaya.PropertyProvider} by using the given Map.
+ *
+ * @param map the properties to be included, not null.
+ * @return a new {@link }PropertyMap} instance with the given properties fromMap the Map instance passed.
+ */
+ public static PropertyProvider fromMap(Map<String, String> map) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .fromMap(null, map);
+ }
+
+
+ /**
+ * Creates a new read-only {@link org.apache.tamaya.PropertyProvider} by using the given Map.
+ *
+ * @param metaInfo the meat information to be provided additionally.
+ * @param map the properties to be included, not null.
+ * @return a new {@link }PropertyMap} instance with the given properties fromMap the Map instance passed.
+ */
+ public static PropertyProvider fromMap(MetaInfo metaInfo, Map<String, String> map) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .fromMap(metaInfo, map);
+ }
+
+ /**
+ * Get an empty and immutable PropertyProvider instance.
+ *
+ * @return an empty and immutable PropertyProvider instance, never null.
+ */
+ public static PropertyProvider empty() {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .empty(null);
+ }
+
+ /**
+ * Get an empty and immutable PropertyProvider instance.
+ *
+ * @return an empty and mutable PropertyProvider instance, never null.
+ */
+ public static PropertyProvider emptyMutable() {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .emptyMutable(null);
+ }
+
+ /**
+ * Get an empty and immutable PropertyProvider instance. The meta-information contains the given String
+ * under the key 'info'.
+ *
+ * @return an empty and immutable PropertyProvider instance, never null, with the given Strings as info meta-data..
+ */
+ public static PropertyProvider empty(MetaInfo metaInfo) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .empty(metaInfo);
+ }
+
+ /**
+ * Get an empty and mutable PropertyProvider instance. The meta-information contains the given String
+ * under the key 'info'.
+ *
+ * @return an empty and immutable PropertyProvider instance, never null, with the given Strings as info meta-data..
+ */
+ public static PropertyProvider emptyMutable(MetaInfo metaInfo) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .emptyMutable(metaInfo);
+ }
+
+ /**
+ * Returns a read-only {@link org.apache.tamaya.PropertyProvider} reflecting the current runtime environment properties.
+ *
+ * @return a new read-only {@link org.apache.tamaya.PropertyProvider} instance based on the current runtime environment properties.
+ */
+ public static PropertyProvider fromEnvironmentProperties() {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .fromEnvironmentProperties();
+ }
+
+ /**
+ * Creates a new read-only {@link org.apache.tamaya.PropertyProvider} reflecting the current system properties.
+ *
+ * @return a new read-only {@link org.apache.tamaya.PropertyProvider} instance based on the current system properties.
+ */
+ public static PropertyProvider fromSystemProperties() {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .fromSystemProperties();
+ }
+
+ /**
+ * Converts a given {@link PropertyProvider} instance into a serializable and immutable form,
+ * so it can be sent over a network connection.
+ *
+ * @param provider the PropertyProvider to be freezed.
+ * @return the serializable instance.
+ */
+ public static PropertyProvider freezed(PropertyProvider provider) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .freezed(null, provider);
+ }
+
+ /**
+ * Creates a new {@link org.apache.tamaya.PropertyProvider} containing all property maps given, hereby later maps in the array override
+ * properties fromMap previous instances.
+ *
+ * @param propertyMaps the maps to be included, not null.
+ * @return the union instance containing all given maps.
+ */
+ public static PropertyProvider aggregate(PropertyProvider... propertyMaps) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .aggregate(AggregationPolicy.OVERRIDE, null, Arrays.asList(propertyMaps));
+ }
+
+ /**
+ * Creates a new {@link org.apache.tamaya.PropertyProvider} containing all property maps given, hereby later maps in the array override
+ * properties fromMap previous instances.
+ *
+ * @param providers the maps to be included, not null.
+ * @return the union instance containing all given maps.
+ */
+ public static PropertyProvider aggregate(List<PropertyProvider> providers) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .aggregate(AggregationPolicy.OVERRIDE, null, providers);
+ }
+
+ /**
+ * Creates a new {@link org.apache.tamaya.PropertyProvider} containing all property maps given, hereby using the given AggregationPolicy.
+ *
+ * @param aggregationPolicy the AggregationPolicy to be used, not null.
+ * @param propertyMaps the maps to be included, not null.
+ * @return the aggregated instance containing all given maps.
+ */
+ public static PropertyProvider aggregate(AggregationPolicy aggregationPolicy, PropertyProvider... propertyMaps) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .aggregate(aggregationPolicy, null, Arrays.asList(propertyMaps));
+ }
+
+ /**
+ * Creates a new {@link org.apache.tamaya.PropertyProvider} containing all property maps given, hereby using the given AggregationPolicy.
+ *
+ * @param aggregationPolicy the AggregationPolicy to be used, not null.
+ * @param providers the providers to be included, not null.
+ * @return the aggregated instance containing all given maps.
+ */
+ public static PropertyProvider aggregate(AggregationPolicy aggregationPolicy, List<PropertyProvider> providers) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .aggregate(aggregationPolicy, null, providers);
+ }
+
+ /**
+ * Creates a new {@link org.apache.tamaya.PropertyProvider} that is mutable by adding a map based instance that overrides
+ * values fromMap the original map.
+ *
+ * @param provider the provider to be made mutable, not null.
+ * @return the mutable instance.
+ */
+ public static PropertyProvider mutable(PropertyProvider provider) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .mutable(null, provider);
+ }
+
+ /**
+ * Creates a new {@link org.apache.tamaya.PropertyProvider} containing only properties that are shared by all given maps,
+ * hereby later maps in the array override properties fromMap previous instances.
+ * @param aggregationPolicy the aggregationPolicy to be used, not null.
+ * @param providers the maps to be included, not null.
+ * @return the intersecting instance containing all given maps.
+ */
+ public static PropertyProvider intersected(AggregationPolicy aggregationPolicy, PropertyProvider... providers) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .intersected(aggregationPolicy, Arrays.asList(providers));
+ }
+
+ /**
+ * Creates a new {@link org.apache.tamaya.PropertyProvider} containing only properties that are shared by all given maps,
+ * hereby later maps in the array override properties fromMap previous instances.
+ *
+ * @param providers the maps to be included, not null.
+ * @return the intersecting instance containing all given maps.
+ * @throws ConfigException if duplicate entries are encountered (AggregationPolicy.EXCEPTION).
+ */
+ public static PropertyProvider intersected(PropertyProvider... providers) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .intersected(AggregationPolicy.OVERRIDE, Arrays.asList(providers));
+ }
+
+ /**
+ * Creates a new {@link org.apache.tamaya.PropertyProvider} containing only properties fromMap the target instance, that are not contained
+ * in one of the other maps passed.
+ *
+ * @param target the base map, not null.
+ * @param providers the maps to be subtracted, not null.
+ * @return the intersecting instance containing all given maps.
+ */
+ public static PropertyProvider subtracted(PropertyProvider target, PropertyProvider... providers) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .subtracted(target, Arrays.asList(providers));
+ }
+
+
+ /**
+ * Creates a filtered {@link org.apache.tamaya.PropertyProvider} (a view) of a given base {@link }PropertyMap}. The filter hereby is
+ * applied dynamically on access, so also runtime changes of the base map are reflected appropriately.
+ *
+ * @param provider the base map instance, not null.
+ * @param filter the filtger to be applied, not null.
+ * @return the new filtering instance.
+ */
+ public static PropertyProvider filtered(Predicate<String> filter, PropertyProvider provider) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .filtered(filter, provider);
+ }
+
+ /**
+ * Creates a new contextual {@link org.apache.tamaya.PropertyProvider}. Contextual maps delegate to different instances of PropertyMap depending
+ * on the keys returned fromMap the isolationP
+ *
+ * @param mapSupplier the supplier creating new provider instances
+ * @param isolationKeySupplier the supplier providing contextual keys based on the current environment.
+ */
+ public static PropertyProvider contextual(Supplier<PropertyProvider> mapSupplier,
+ Supplier<String> isolationKeySupplier) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .contextual(mapSupplier, isolationKeySupplier);
+ }
+
+
+ /**
+ * Creates a filtered {@link org.apache.tamaya.PropertyProvider} (a view) of a given base {@link }PropertyMap}. The filter hereby is
+ * applied dynamically on access, so also runtime changes of the base map are reflected appropriately.
+ *
+ * @param mainMap the main map instance, not null.
+ * @param parentMap the delegated parent map instance, not null.
+ * @return the new delegating instance.
+ */
+ public static PropertyProvider delegating(PropertyProvider mainMap, Map<String, String> parentMap) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .delegating(mainMap, parentMap);
+ }
+
+ /**
+ * Creates a {@link PropertyProvider} where all keys of a current map,
+ * existing in another map are replaced
+ * with the ones fromMap the other {@link PropertyProvider}. The filter hereby is
+ * applied dynamically on access, so also runtime changes of the base map are reflected appropriately.
+ * Keys not existing in the {@code mainMap}, but present in {@code replacementMao} will be hidden.
+ *
+ * @param mainMap the main map instance, which keys, present in {@code replacementMap} will be replaced
+ * with the ones
+ * in {@code replacementMap}, not null.
+ * @param replacementMap the map instance, that will replace all corresponding entries in {@code mainMap}, not null.
+ * @return the new delegating instance.
+ */
+ public static PropertyProvider replacing(PropertyProvider mainMap, Map<String, String> replacementMap) {
+ return Optional.of(spi).orElseThrow(() -> new IllegalStateException("No PropertyProvidersSingletonSpi available."))
+ .replacing(mainMap, replacementMap);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/api/src/main/java/org/apache/tamaya/Stages.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/Stages.java b/api/src/main/java/org/apache/tamaya/Stages.java
index 5a3559a..7cbf2cd 100644
--- a/api/src/main/java/org/apache/tamaya/Stages.java
+++ b/api/src/main/java/org/apache/tamaya/Stages.java
@@ -79,7 +79,7 @@ final class Stages {
/**
- * Method that loads the singleton backing bean from the {@link org.apache.tamaya.spi.Bootstrap} component.
+ * Method that loads the singleton backing bean fromMap the {@link org.apache.tamaya.spi.Bootstrap} component.
* @return the PropertyAdaptersSingletonSpi, never null.
*/
private static StagesSingletonSpi loadStagesSingletonSpi(){
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/api/src/main/java/org/apache/tamaya/annot/ConfiguredProperty.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/annot/ConfiguredProperty.java b/api/src/main/java/org/apache/tamaya/annot/ConfiguredProperty.java
index 6b4320f..de76cbe 100644
--- a/api/src/main/java/org/apache/tamaya/annot/ConfiguredProperty.java
+++ b/api/src/main/java/org/apache/tamaya/annot/ConfiguredProperty.java
@@ -60,7 +60,7 @@ import java.lang.annotation.*;
* Within this example we evaluate multiple possible keys. Evaluation is aborted if a key could be successfully
* resolved. Hereby the ordering of the annotations define the ordering of resolution, so in the example above
* resolution equals to {@code "aValue", "a.b.value", "a.b.deprecated.value"}. If no value could be read
- * from the configuration, it uses the value from the {@code DefaultValue} annotation. Interesting here
+ * fromMap the configuration, it uses the value fromMap the {@code DefaultValue} annotation. Interesting here
* is that this value is not static, it is evaluated by calling
* {@link org.apache.tamaya.Configuration#evaluateValue(org.apache.tamaya.Configuration, String)}.
*/
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/api/src/main/java/org/apache/tamaya/spi/ConfigurationManagerSingletonSpi.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/spi/ConfigurationManagerSingletonSpi.java b/api/src/main/java/org/apache/tamaya/spi/ConfigurationManagerSingletonSpi.java
index 8e777ef..9be6ef1 100644
--- a/api/src/main/java/org/apache/tamaya/spi/ConfigurationManagerSingletonSpi.java
+++ b/api/src/main/java/org/apache/tamaya/spi/ConfigurationManagerSingletonSpi.java
@@ -21,6 +21,9 @@ package org.apache.tamaya.spi;
import org.apache.tamaya.Configuration;
import java.beans.PropertyChangeListener;
+import java.util.*;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
/**
* Manager for {@link org.apache.tamaya.Configuration} instances. Implementations must register an instance
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/api/src/main/java/org/apache/tamaya/spi/PropertyProvidersSingletonSpi.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/tamaya/spi/PropertyProvidersSingletonSpi.java b/api/src/main/java/org/apache/tamaya/spi/PropertyProvidersSingletonSpi.java
new file mode 100644
index 0000000..80de67b
--- /dev/null
+++ b/api/src/main/java/org/apache/tamaya/spi/PropertyProvidersSingletonSpi.java
@@ -0,0 +1,198 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tamaya.spi;
+
+import org.apache.tamaya.AggregationPolicy;
+import org.apache.tamaya.MetaInfo;
+import org.apache.tamaya.PropertyProvider;
+
+import java.net.URI;
+import java.util.*;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+
+/**
+ * Singleton backing bean for providing the functionality for {@link org.apache.tamaya.PropertyProviders}.
+ */
+public interface PropertyProvidersSingletonSpi {
+
+ /**
+ * Creates a new read-only {@link org.apache.tamaya.PropertyProvider} by using the given Map.
+ *
+ * @param metaInfo the meat information to be provided additionally.
+ * @param map the properties to be included, not null.
+ * @return a new {@link }PropertyMap} instance with the given properties fromMap the Map instance passed.
+ */
+ PropertyProvider fromMap(MetaInfo metaInfo, Map<String, String> map);
+
+ /**
+ * Creates a new {@link }PropertyMap} using the given command line arguments
+ *
+ * @param metaInfo the meta information to be provided additionally.
+ * @param args the command line arguments, not null.
+ * @return a new {@link }PropertyMap} instance with the given arguments contained as properties.
+ */
+ PropertyProvider fromArgs(MetaInfo metaInfo, String... args);
+
+ /**
+ * Creates a new read-only {@link PropertyProvider} by reading the according path resources. The effective resources read
+ * hereby are determined by the {@code PathResolverService} configured into the {@code Bootstrap} SPI.
+ * Properties read fromMap resources evaluated on
+ * paths with lower order are overriding any duplicate values fromMap previous paths hereby.
+ *
+ * @param metaInfo the meat information to be provided additionally.
+ * @param paths the paths to be resolved by the {@code PathResolverService} , not null.
+ * @param aggregationPolicy the {@link org.apache.tamaya.AggregationPolicy} to be used to resolve conflicts.
+ * @return a new {@link }PropertyMap} instance with the given paths contained as properties.
+ */
+ PropertyProvider fromPaths(AggregationPolicy aggregationPolicy, MetaInfo metaInfo, List<String> paths);
+
+
+ /**
+ * Creates a new read-only {@link PropertyProvider} based on the resources defined by the given paths. The effective resources
+ * read hereby are determined by the {@code PathResolverService} configured into the {@code Bootstrap} SPI.
+ *
+ * @param metaInfo the meat information to be provided additionally.
+ * @param uris the uris to be read, not null.
+ * @return a new {@link }PropertyMap} instance based on the given paths/resources found.
+ */
+ PropertyProvider fromUris(AggregationPolicy aggregationPolicy, MetaInfo metaInfo, List<URI> uris);
+
+ /**
+ * Get an empty and immutable PropertyProvider instance. The meta-information contains the given String
+ * under the key 'info'.
+ *
+ * @return an empty and immutable PropertyProvider instance, never null, with the given Strings as info meta-data..
+ */
+ PropertyProvider empty(MetaInfo metaInfo);
+
+ /**
+ * Get an empty and mutable PropertyProvider instance. The meta-information contains the given String
+ * under the key 'info'.
+ *
+ * @return an empty and immutable PropertyProvider instance, never null, with the given Strings as info meta-data..
+ */
+ PropertyProvider emptyMutable(MetaInfo metaInfo);
+
+ /**
+ * Returns a read-only {@link org.apache.tamaya.PropertyProvider} reflecting the current runtime environment properties.
+ *
+ * @return a new read-only {@link org.apache.tamaya.PropertyProvider} instance based on the current runtime environment properties.
+ */
+ PropertyProvider fromEnvironmentProperties();
+
+ /**
+ * Creates a new read-only {@link org.apache.tamaya.PropertyProvider} reflecting the current system properties.
+ *
+ * @return a new read-only {@link org.apache.tamaya.PropertyProvider} instance based on the current system properties.
+ */
+ PropertyProvider fromSystemProperties();
+
+ /**
+ * Converts a given {@link PropertyProvider} instance into a serializable and immutable form,
+ * so it can be sent over a network connection.
+ *
+ * @param provider the PropertyProvider to be freezed.
+ * @return the serializable instance.
+ */
+ PropertyProvider freezed(MetaInfo metaInfo, PropertyProvider provider);
+
+ /**
+ * Creates a new {@link org.apache.tamaya.PropertyProvider} containing all property maps given, hereby using the given AggregationPolicy.
+ *
+ * @param aggregationPolicy the AggregationPolicy to be used, not null.
+ * @param propertyMaps the maps to be included, not null.
+ * @return the aggregated instance containing all given maps.
+ */
+ PropertyProvider aggregate(AggregationPolicy aggregationPolicy, MetaInfo metaInfo, List<PropertyProvider> propertyMaps);
+
+ /**
+ * Creates a new {@link org.apache.tamaya.PropertyProvider} that is mutable by adding a map based instance that overrides
+ * values fromMap the original map.
+ * @param provider the provider to be made mutable, not null.
+ * @return the mutable instance.
+ */
+ PropertyProvider mutable(MetaInfo metaInfo, PropertyProvider provider);
+
+ /**
+ * Creates a new {@link org.apache.tamaya.PropertyProvider} containing only properties that are shared by all given maps,
+ * hereby later maps in the array override properties fromMap previous instances.
+ *
+ * @param propertyMaps the maps to be included, not null.
+ * @return the intersecting instance containing all given maps.
+ */
+ PropertyProvider intersected(AggregationPolicy policy, List<PropertyProvider> propertyMaps);
+
+ /**
+ * Creates a new {@link org.apache.tamaya.PropertyProvider} containing only properties fromMap the target instance, that are not contained
+ * in one of the other maps passed.
+ *
+ * @param target the base map, not null.
+ * @param subtrahendSets the maps to be subtracted, not null.
+ * @return the intersecting instance containing all given maps.
+ */
+ PropertyProvider subtracted(PropertyProvider target, List<PropertyProvider> subtrahendSets);
+
+
+ /**
+ * Creates a filtered {@link org.apache.tamaya.PropertyProvider} (a view) of a given base {@link }PropertyMap}. The filter hereby is
+ * applied dynamically on access, so also runtime changes of the base map are reflected appropriately.
+ *
+ * @param propertyMap the base map instance, not null.
+ * @param filter the filtger to be applied, not null.
+ * @return the new filtering instance.
+ */
+ PropertyProvider filtered(Predicate<String> filter, PropertyProvider propertyMap);
+
+ /**
+ * Creates a new contextual {@link org.apache.tamaya.PropertyProvider}. Contextual maps delegate to different instances of PropertyMap depending
+ * on the keys returned fromMap the isolationP
+ *
+ * @param mapSupplier the supplier creating new provider instances
+ * @param isolationKeySupplier the supplier providing contextual keys based on the current environment.
+ */
+ PropertyProvider contextual(Supplier<PropertyProvider> mapSupplier,
+ Supplier<String> isolationKeySupplier);
+
+
+ /**
+ * Creates a filtered {@link org.apache.tamaya.PropertyProvider} (a view) of a given base {@link }PropertyMap}. The filter hereby is
+ * applied dynamically on access, so also runtime changes of the base map are reflected appropriately.
+ *
+ * @param mainMap the main map instance, not null.
+ * @param parentMap the delegated parent map instance, not null.
+ * @return the new delegating instance.
+ */
+ PropertyProvider delegating(PropertyProvider mainMap, Map<String, String> parentMap);
+
+ /**
+ * Creates a {@link PropertyProvider} where all keys of a current map,
+ * existing in another map are replaced
+ * with the ones fromMap the other {@link PropertyProvider}. The filter hereby is
+ * applied dynamically on access, so also runtime changes of the base map are reflected appropriately.
+ * Keys not existing in the {@code mainMap}, but present in {@code replacementMao} will be hidden.
+ *
+ * @param mainMap the main map instance, which keys, present in {@code replacementMap} will be replaced
+ * with the ones
+ * in {@code replacementMap}, not null.
+ * @param replacementMap the map instance, that will replace all corresponding entries in {@code mainMap}, not null.
+ * @return the new delegating instance.
+ */
+ PropertyProvider replacing(PropertyProvider mainMap, Map<String, String> replacementMap);
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/config/ConfigurationBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/config/ConfigurationBuilder.java b/core/src/main/java/org/apache/tamaya/core/config/ConfigurationBuilder.java
index 3275907..7dc56c1 100644
--- a/core/src/main/java/org/apache/tamaya/core/config/ConfigurationBuilder.java
+++ b/core/src/main/java/org/apache/tamaya/core/config/ConfigurationBuilder.java
@@ -18,14 +18,9 @@
*/
package org.apache.tamaya.core.config;
-import org.apache.tamaya.core.properties.AggregationPolicy;
-import org.apache.tamaya.core.properties.PropertyProviders;
+import org.apache.tamaya.*;
import org.apache.tamaya.core.spi.ResourceLoader;
-import org.apache.tamaya.Configuration;
-import org.apache.tamaya.MetaInfo;
-import org.apache.tamaya.MetaInfoBuilder;
-import org.apache.tamaya.PropertyProvider;
import org.apache.tamaya.spi.Bootstrap;
import java.net.URI;
import java.util.*;
@@ -118,13 +113,13 @@ public final class ConfigurationBuilder{
}
PropertyProvider prov = PropertyProviders.fromUris(metaInfo, sourcesToRead.collect(Collectors.toList()));
if(!this.data.isEmpty()){
- prov = PropertyProviders.union(AggregationPolicy.OVERRIDE, prov, PropertyProviders.from(this.data));
+ prov = PropertyProviders.aggregate(AggregationPolicy.OVERRIDE, prov, PropertyProviders.fromMap(this.data));
}
for(ConfigMapAddition addition : addedMaps){
PropertyProvider[] newMaps = new PropertyProvider[addition.configMaps.length + 1];
newMaps[0] = prov;
System.arraycopy(addition.configMaps, 0, newMaps, 1, addition.configMaps.length);
- prov = PropertyProviders.union(addition.policy, newMaps);
+ prov = PropertyProviders.aggregate(addition.policy, newMaps);
}
final PropertyProvider finalProvider = prov;
return new MapConfiguration(metaInfo, () -> finalProvider.toMap());
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/config/ConfigurationFormats.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/config/ConfigurationFormats.java b/core/src/main/java/org/apache/tamaya/core/config/ConfigurationFormats.java
index ebce952..740eacb 100644
--- a/core/src/main/java/org/apache/tamaya/core/config/ConfigurationFormats.java
+++ b/core/src/main/java/org/apache/tamaya/core/config/ConfigurationFormats.java
@@ -40,7 +40,7 @@ public final class ConfigurationFormats{
private static final ConfigurationFormatsSingletonSpi spi = loadSpi();
/**
- * Method to load the spi from the Bootstrap component.
+ * Method to load the spi fromMap the Bootstrap component.
*
* @return an instance of ConfigurationFormatsSingletonSpi, never null.
*/
@@ -96,10 +96,10 @@ public final class ConfigurationFormats{
}
/**
- * Get an instance for reading configuration from a {@code .properties} file,
+ * Get an instance for reading configuration fromMap a {@code .properties} file,
* as defined by {@link java.util.Properties#load(java.io.InputStream)}.
*
- * @return a format instance for reading configuration from a {@code .properties} file, never null.
+ * @return a format instance for reading configuration fromMap a {@code .properties} file, never null.
*/
public static ConfigurationFormat getPropertiesFormat(){
return Optional.ofNullable(spi)
@@ -108,10 +108,10 @@ public final class ConfigurationFormats{
}
/**
- * Get an instance for reading configuration from a {@code .xml} properties file,
+ * Get an instance for reading configuration fromMap a {@code .xml} properties file,
* as defined by {@link java.util.Properties#loadFromXML(java.io.InputStream)}.
*
- * @return a format instance for reading configuration from a {@code .xml} properties file, never null.
+ * @return a format instance for reading configuration fromMap a {@code .xml} properties file, never null.
*/
public static ConfigurationFormat getXmlPropertiesFormat(){
return Optional.ofNullable(spi)
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/config/EnvPropertiesConfigProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/config/EnvPropertiesConfigProvider.java b/core/src/main/java/org/apache/tamaya/core/config/EnvPropertiesConfigProvider.java
index 62fb8dd..7fe9c09 100644
--- a/core/src/main/java/org/apache/tamaya/core/config/EnvPropertiesConfigProvider.java
+++ b/core/src/main/java/org/apache/tamaya/core/config/EnvPropertiesConfigProvider.java
@@ -18,8 +18,8 @@
*/
package org.apache.tamaya.core.config;
-import org.apache.tamaya.core.properties.AggregationPolicy;
-import org.apache.tamaya.core.properties.PropertyProviders;
+import org.apache.tamaya.AggregationPolicy;
+import org.apache.tamaya.PropertyProviders;
import org.apache.tamaya.core.spi.ConfigurationProviderSpi;
import org.apache.tamaya.Configuration;
@@ -35,7 +35,8 @@ public class EnvPropertiesConfigProvider implements ConfigurationProviderSpi{
private Configuration envConfig;
public EnvPropertiesConfigProvider(){
- envConfig = ConfigurationBuilder.of("environment.properties").addConfigMaps(AggregationPolicy.OVERRIDE, PropertyProviders.fromEnvironmentProperties()).build();
+ envConfig = ConfigurationBuilder.of("environment.properties").addConfigMaps(AggregationPolicy.OVERRIDE,
+ PropertyProviders.fromEnvironmentProperties()).build();
}
@Override
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/config/FreezedConfiguration.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/config/FreezedConfiguration.java b/core/src/main/java/org/apache/tamaya/core/config/FreezedConfiguration.java
index 134bdc5..4e35c4d 100644
--- a/core/src/main/java/org/apache/tamaya/core/config/FreezedConfiguration.java
+++ b/core/src/main/java/org/apache/tamaya/core/config/FreezedConfiguration.java
@@ -18,12 +18,7 @@
*/
package org.apache.tamaya.core.config;
-import org.apache.tamaya.ConfigChangeSetBuilder;
-import org.apache.tamaya.PropertyProvider;
-
-import org.apache.tamaya.Configuration;
-import org.apache.tamaya.MetaInfoBuilder;
-import org.apache.tamaya.core.properties.PropertyProviders;
+import org.apache.tamaya.*;
import java.io.Serializable;
import java.time.Instant;
@@ -31,7 +26,8 @@ import java.util.Map;
import java.util.Objects;
/**
- * Created by Anatole on 29.03.14.
+ * Configuration implementation that stores all current values of a given (possibly dynamic, contextual and non remote
+ * capable instance) and is fully serializable.
*/
final class FreezedConfiguration extends AbstractConfiguration implements Serializable{
private static final long serialVersionUID = -6373137316556444171L;
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/config/SystemPropertiesConfigProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/config/SystemPropertiesConfigProvider.java b/core/src/main/java/org/apache/tamaya/core/config/SystemPropertiesConfigProvider.java
index 1356fb1..f19d874 100644
--- a/core/src/main/java/org/apache/tamaya/core/config/SystemPropertiesConfigProvider.java
+++ b/core/src/main/java/org/apache/tamaya/core/config/SystemPropertiesConfigProvider.java
@@ -18,8 +18,8 @@
*/
package org.apache.tamaya.core.config;
-import org.apache.tamaya.core.properties.AggregationPolicy;
-import org.apache.tamaya.core.properties.PropertyProviders;
+import org.apache.tamaya.AggregationPolicy;
+import org.apache.tamaya.PropertyProviders;
import org.apache.tamaya.core.spi.ConfigurationProviderSpi;
import org.apache.tamaya.Configuration;
@@ -35,7 +35,8 @@ public class SystemPropertiesConfigProvider implements ConfigurationProviderSpi{
private Configuration systemConfig;
public SystemPropertiesConfigProvider(){
- systemConfig = ConfigurationBuilder.of("system.properties").addConfigMaps(AggregationPolicy.OVERRIDE, PropertyProviders.fromSystemProperties()).build();
+ systemConfig = ConfigurationBuilder.of("system.properties").addConfigMaps(AggregationPolicy.OVERRIDE,
+ PropertyProviders.fromSystemProperties()).build();
}
@Override
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/AggregatedPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/AggregatedPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/internal/AggregatedPropertyProvider.java
new file mode 100644
index 0000000..fdcd073
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/AggregatedPropertyProvider.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tamaya.core.internal;
+
+import org.apache.tamaya.*;
+import org.apache.tamaya.core.properties.AbstractPropertyProvider;
+
+import java.util.*;
+
+/**
+ * Implementation for a {@link org.apache.tamaya.PropertyProvider} that is an aggregate of
+ * multiple child instances. Controlled by an {@link org.apache.tamaya.AggregationPolicy} the
+ * following aggregations are supported:
+ * <ul>
+ * <li><b>IGNORE: </b>Ignore all overrides.</li>
+ * <li><b>: </b></li>
+ * <li><b>: </b></li>
+ * <li><b>: </b></li>
+ * </ul>
+ */
+class AggregatedPropertyProvider extends AbstractPropertyProvider {
+
+ private static final long serialVersionUID = -1419376385695224799L;
+ private AggregationPolicy policy = AggregationPolicy.IGNORE;
+ private List<PropertyProvider> units = new ArrayList<PropertyProvider>();
+ private PropertyProvider mutableProvider;
+
+ /**
+ * Creates a new instance.
+ * @param mutableProvider the provider instance that would be used for delegating
+ * change requests.
+ * @param policy
+ * The aggregation policy to be used.
+ * @param propertyMaps
+ * The property sets to be included.
+ */
+ public AggregatedPropertyProvider(MetaInfo metaInfo, PropertyProvider mutableProvider, AggregationPolicy policy, List<PropertyProvider> propertyMaps) {
+ super(MetaInfoBuilder.of(metaInfo).setType("aggregated").build());
+ Objects.requireNonNull(policy);
+ this.policy = policy;
+ units.addAll(propertyMaps);
+ this.mutableProvider = mutableProvider;
+ }
+
+ /**
+ * Get the {@link AggregationPolicy} for this instance.
+ *
+ * @return the {@link AggregationPolicy}, never {@code null}.
+ */
+ public AggregationPolicy getAggregationPolicy() {
+ return policy;
+ }
+
+ /**
+ * Return the names of the {@link org.apache.tamaya.PropertyProvider} instances to be
+ * aggregated in this instance, in the order of precedence (the first are
+ * the weakest).
+ *
+ * @return the ordered list of aggregated scope identifiers, never
+ * {@code null}.
+ */
+ public List<PropertyProvider> getConfigurationUnits() {
+ return Collections.unmodifiableList(units);
+ }
+
+ /**
+ * Apply a config change to this item. Hereby the change must be related to the same instance.
+ * @param change the config change
+ * @throws org.apache.tamaya.ConfigException if an unrelated change was passed.
+ * @throws UnsupportedOperationException when the configuration is not writable.
+ */
+ @Override
+ public void apply(ConfigChangeSet change){
+ if(mutableProvider!=null)
+ mutableProvider.apply(change);
+ else
+ super.apply(change);
+ }
+
+ @Override
+ public Map<String,String> toMap() {
+ Map<String, String> value = new HashMap<>();
+ for (PropertyProvider unit : units) {
+ for (Map.Entry<String, String> entry : unit.toMap()
+ .entrySet()) {
+ switch (policy) {
+ case IGNORE:
+ if (!value.containsKey(entry.getKey())) {
+ value.put(entry.getKey(), entry.getValue());
+ }
+ break;
+ case EXCEPTION:
+ if (value.containsKey(entry.getKey())) {
+ throw new IllegalStateException("Duplicate key: "
+ + entry.getKey()
+ + " in " + this);
+ }
+ else {
+ value.put(entry.getKey(), entry.getValue());
+ }
+ break;
+ case OVERRIDE:
+ value.put(entry.getKey(), entry.getValue());
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return value;
+ }
+
+ @Override
+ public ConfigChangeSet load() {
+ for (PropertyProvider unit : units) {
+ unit.load();
+ }
+ return super.load();
+ }
+
+}
[7/8] incubator-tamaya git commit: TAMAYA-14: added resource support.
TAMAYA-15: Moved PropertyProviders to API,
added SPI. TAMAYA-8: Added/improved Javadoc.
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/ContextualPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/ContextualPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/internal/ContextualPropertyProvider.java
new file mode 100644
index 0000000..9bea099
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/ContextualPropertyProvider.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tamaya.core.internal;
+
+import org.apache.tamaya.*;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Supplier;
+
+/**
+ * Created by Anatole on 12.04.2014.
+ */
+class ContextualPropertyProvider implements PropertyProvider{
+
+ private volatile Map<String,PropertyProvider> cachedMaps = new ConcurrentHashMap<>();
+
+ private Supplier<PropertyProvider> mapSupplier;
+ private Supplier<String> isolationKeySupplier;
+ private MetaInfo metaInfo;
+
+ /**
+ * Creates a new contextual PropertyMap. Contextual maps delegate to different instances of PropertyMap depending
+ * on the keys returned fromMap the isolationP
+ *
+ * @param mapSupplier
+ * @param isolationKeySupplier
+ */
+ public ContextualPropertyProvider(Supplier<PropertyProvider> mapSupplier, Supplier<String> isolationKeySupplier){
+ this.metaInfo = MetaInfoBuilder.of().setType("contextual").set("mapSupplier", mapSupplier.toString())
+ .set("isolationKeySupplier", isolationKeySupplier.toString()).build();
+ Objects.requireNonNull(mapSupplier);
+ Objects.requireNonNull(isolationKeySupplier);
+ this.mapSupplier = mapSupplier;
+ this.isolationKeySupplier = isolationKeySupplier;
+ }
+
+ /**
+ * This method provides the contextual Map for the current environment. Hereby, ba default, for each different
+ * key returned by the #isolationKeySupplier a separate PropertyMap instance is acquired fromMap the #mapSupplier.
+ * If the map supplier returns an instance it is cached in the local #cachedMaps.
+ *
+ * @return the current contextual PropertyMap.
+ */
+ protected PropertyProvider getContextualMap(){
+ String environmentKey = this.isolationKeySupplier.get();
+ if(environmentKey == null){
+ return PropertyProviders.empty();
+ }
+ PropertyProvider map = this.cachedMaps.get(environmentKey);
+ if(map == null){
+ synchronized(cachedMaps){
+ map = this.cachedMaps.get(environmentKey);
+ if(map == null){
+ map = this.mapSupplier.get();
+ if(map == null){
+ return PropertyProviders
+ .empty(MetaInfoBuilder.of().setInfo(
+ "No map provided by supplier " + mapSupplier + " for key " + environmentKey)
+ .build());
+ }
+ this.cachedMaps.put(environmentKey, map);
+ }
+ }
+ }
+ return map;
+ }
+
+ @Override
+ public ConfigChangeSet load(){
+ return getContextualMap().load();
+ }
+
+ @Override
+ public boolean containsKey(String key){
+ return getContextualMap().containsKey(key);
+ }
+
+ @Override
+ public Map<String,String> toMap(){
+ return getContextualMap().toMap();
+ }
+
+ @Override
+ public MetaInfo getMetaInfo(){
+ return this.metaInfo;
+ }
+
+ @Override
+ public Optional<String> get(String key){
+ return getContextualMap().get(key);
+ }
+
+ @Override
+ public Set<String> keySet(){
+ return getContextualMap().keySet();
+ }
+
+ /**
+ * Apply a config change to this item. Hereby the change must be related to the same instance.
+ * @param change the config change
+ * @throws org.apache.tamaya.ConfigException if an unrelated change was passed.
+ * @throws UnsupportedOperationException when the configuration is not writable.
+ */
+ @Override
+ public void apply(ConfigChangeSet change){
+ getContextualMap().apply(change);
+ }
+
+ /**
+ * Access a cached PropertyMap.
+ *
+ * @param key the target environment key as returned by the environment key supplier, not null.
+ * @return the corresponding PropertyMap, or null.
+ */
+ public PropertyProvider getCachedMap(String key){
+ return this.cachedMaps.get(key);
+ }
+
+ /**
+ * Access the set of currently loaded/cached maps.
+ *
+ * @return the set of cached map keys, never null.
+ */
+ public Set<String> getCachedMapKeys(){
+ return this.cachedMaps.keySet();
+ }
+
+ /**
+ * Access the supplier for environment key, determining map isolation.
+ *
+ * @return the environment key supplier instance, not null.
+ */
+ public Supplier<String> getIsolationKeySupplier(){
+ return this.isolationKeySupplier;
+ }
+
+ /**
+ * Access the supplier for new PropertyMap instances.
+ *
+ * @return the PropertyMap supplier instance, not null.
+ */
+ public Supplier<PropertyProvider> getMapSupplier(){
+ return this.mapSupplier;
+ }
+
+ @Override
+ public String toString(){
+ return "ContextualMap{" +
+ "cachedMaps(key)=" + cachedMaps.keySet() +
+ ", mapSupplier=" + mapSupplier +
+ ", isolationKeySupplier=" + isolationKeySupplier +
+ '}';
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/DefaultConfigProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/DefaultConfigProvider.java b/core/src/main/java/org/apache/tamaya/core/internal/DefaultConfigProvider.java
index 3519c3e..a3b1d47 100644
--- a/core/src/main/java/org/apache/tamaya/core/internal/DefaultConfigProvider.java
+++ b/core/src/main/java/org/apache/tamaya/core/internal/DefaultConfigProvider.java
@@ -18,9 +18,9 @@
*/
package org.apache.tamaya.core.internal;
+import org.apache.tamaya.PropertyProviders;
import org.apache.tamaya.core.config.ConfigurationBuilder;
-import org.apache.tamaya.core.properties.AggregationPolicy;
-import org.apache.tamaya.core.properties.PropertyProviders;
+import org.apache.tamaya.AggregationPolicy;
import org.apache.tamaya.core.spi.ConfigurationProviderSpi;
import org.apache.tamaya.Configuration;
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/DefaultPropertyProvidersSingletonSpi.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/DefaultPropertyProvidersSingletonSpi.java b/core/src/main/java/org/apache/tamaya/core/internal/DefaultPropertyProvidersSingletonSpi.java
new file mode 100644
index 0000000..0bb7e07
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/DefaultPropertyProvidersSingletonSpi.java
@@ -0,0 +1,286 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tamaya.core.internal;
+
+import org.apache.tamaya.AggregationPolicy;
+import org.apache.tamaya.MetaInfo;
+import org.apache.tamaya.MetaInfoBuilder;
+import org.apache.tamaya.PropertyProvider;
+import org.apache.tamaya.spi.PropertyProvidersSingletonSpi;
+
+import java.net.URI;
+import java.time.Instant;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.logging.Logger;
+
+/**
+ * Default implementation of the singleton backing bean for the {@link org.apache.tamaya.PropertyProviders}.
+ */
+public class DefaultPropertyProvidersSingletonSpi implements PropertyProvidersSingletonSpi {
+
+ private final PropertyProvider EMPTY_PROPERTYPROVIDER = fromMap(MetaInfo.of("<empty>"), Collections.emptyMap());
+ private static final PropertyProvider ENV_PROPERTYPROVIDER = new EnvironmentPropertyProvider();
+
+ private static final Logger LOG = Logger.getLogger(DefaultPropertyProvidersSingletonSpi.class.getName());
+
+ @Override
+ public PropertyProvider fromArgs(MetaInfo metaInfo, String... args) {
+ Objects.requireNonNull(metaInfo);
+ // TODO read the CLI with some better library, e.g. move parsing service to ext. service SPI
+ Map<String, String> properties = new HashMap<>();
+ for (int base = 0; base < args.length; base++) {
+ if (args[base].startsWith("--")) {
+ String argKey = args[base].substring(2);
+ String value = "true"; // flag only
+ if (base != args.length - 1) {
+ if (args[base + 1].startsWith("-")) {
+ base++;
+ int eqIndex = argKey.indexOf('=');
+ if (eqIndex > 0) {
+ value = argKey.substring(eqIndex + 1);
+ argKey = argKey.substring(0, eqIndex);
+ }
+ } else {
+ value = args[base + 1];
+ base += 2;
+ }
+ }
+ properties.put(argKey, value);
+ } else if (args[base].startsWith("-")) {
+ String argKey = args[base].substring(1);
+ String value = "true"; // flag only
+ if (base != args.length - 1) {
+ if (args[base + 1].startsWith("-")) {
+ base++;
+ int eqIndex = argKey.indexOf('=');
+ if (eqIndex > 0) {
+ value = argKey.substring(eqIndex + 1);
+ argKey = argKey.substring(0, eqIndex);
+ }
+ } else {
+ value = args[base + 1];
+ base += 2;
+ }
+ }
+ properties.put(argKey, value);
+ }
+ }
+ return fromMap(metaInfo, properties);
+ }
+
+ @Override
+ public PropertyProvider fromPaths(AggregationPolicy aggregationPolicy, MetaInfo metaInfo, List<String> paths) {
+ if(metaInfo == null){
+ metaInfo = MetaInfoBuilder.of().setInfo("From Paths").set("paths", paths.toString()).build();
+ }
+ return new PathBasedPropertyProvider(metaInfo, paths, aggregationPolicy);
+ }
+
+ @Override
+ public PropertyProvider fromUris(AggregationPolicy aggregationPolicy, MetaInfo metaInfo, List<URI> uris) {
+ if(metaInfo == null){
+ metaInfo = MetaInfoBuilder.of().setInfo("From URIs").set("uris", uris.toString()).build();
+ }
+ return new URIBasedPropertyProvider(metaInfo, uris, aggregationPolicy);
+ }
+
+ @Override
+ public PropertyProvider fromMap(MetaInfo metaInfo, Map<String, String> map) {
+ if(metaInfo == null){
+ metaInfo = MetaInfoBuilder.of().setInfo("From Map").set("map", map.toString()).build();
+ }
+ return new MapBasedPropertyProvider(metaInfo, map);
+ }
+
+ @Override
+ public PropertyProvider empty(MetaInfo metaInfo) {
+ if(metaInfo==null) {
+ return EMPTY_PROPERTYPROVIDER;
+ }
+ return fromMap(metaInfo, Collections.emptyMap());
+ }
+
+ @Override
+ public PropertyProvider emptyMutable(MetaInfo metaInfo) {
+ return fromMap(metaInfo, new ConcurrentHashMap<>());
+ }
+
+ /**
+ * Returns a read-only {@link PropertyProvider} reflecting the current runtime environment properties.
+ *
+ * @return a new read-only {@link PropertyProvider} instance based on the current runtime environment properties.
+ */
+ @Override
+ public PropertyProvider fromEnvironmentProperties() {
+ return ENV_PROPERTYPROVIDER;
+ }
+
+ /**
+ * Creates a new read-only {@link PropertyProvider} reflecting the current system properties.
+ *
+ * @return a new read-only {@link PropertyProvider} instance based on the current system properties.
+ */
+ @Override
+ public PropertyProvider fromSystemProperties() {
+ return new SystemPropertiesPropertyProvider();
+ }
+
+ @Override
+ public PropertyProvider freezed(MetaInfo metaInfo, PropertyProvider provider) {
+ if(metaInfo==null){
+ metaInfo = MetaInfoBuilder.of().setType("freezed")
+ .set("provider", provider.toString())
+ .set("freezedAt", Date.from(Instant.now()).toString())
+ .build();
+ }
+ else{
+ metaInfo = MetaInfoBuilder.of(metaInfo).setType("freezed")
+ .set("freezedAt", Date.from(Instant.now()).toString())
+ .set("provider", provider.toString())
+ .build();
+ }
+ return FreezedPropertyProvider.of(metaInfo, provider);
+ }
+
+ /**
+ * Creates a new {@link PropertyProvider} containing all property maps given, hereby using the given AggregationPolicy.
+ *
+ * @param policy the AggregationPolicy to be used, not null.
+ * @param providers the maps to be included, not null.
+ * @return the aggregated instance containing all given maps.
+ */
+ @Override
+ public PropertyProvider aggregate(AggregationPolicy policy, MetaInfo metaInfo, List<PropertyProvider> providers) {
+ if(metaInfo==null){
+ metaInfo = MetaInfoBuilder.of().setInfo("Aggregated")
+ .set("AggregationPolicy", policy.toString())
+ .set("providers", providers.toString())
+ .build();
+ }
+ return new AggregatedPropertyProvider(metaInfo, null, policy, providers);
+ }
+
+ /**
+ * Creates a new {@link PropertyProvider} that is mutable by adding a map based instance that overrides
+ * values fromMap the original map.
+ * @param provider the provider to be made mutable, not null.
+ * @return the mutable instance.
+ */
+ @Override
+ public PropertyProvider mutable(MetaInfo metaInfo, PropertyProvider provider) {
+ if(metaInfo==null){
+ metaInfo = MetaInfoBuilder.of(provider.getMetaInfo())
+ .set("mutableSince", Date.from(Instant.now()).toString())
+ .build();
+ }
+ PropertyProvider mutableProvider = emptyMutable(metaInfo);
+ List<PropertyProvider> providers = new ArrayList<>(2);
+ providers.add(provider);
+ providers.add(mutableProvider);
+ return new AggregatedPropertyProvider(metaInfo, mutableProvider, AggregationPolicy.OVERRIDE, providers);
+ }
+
+ /**
+ * Creates a new {@link PropertyProvider} containing only properties that are shared by all given maps,
+ * hereby later maps in the array override properties fromMap previous instances.
+ * @param aggregationPolicy the policy to resolve aggregation conflicts.
+ * @param providers the maps to be included, not null.
+ * @return the intersecting instance containing all given maps.
+ */
+ @Override
+ public PropertyProvider intersected(AggregationPolicy aggregationPolicy, List<PropertyProvider> providers) {
+ return new IntersectingPropertyProvider(aggregationPolicy, providers);
+ }
+
+ /**
+ * Creates a new {@link PropertyProvider} containing only properties fromMap the target instance, that are not contained
+ * in one of the other maps passed.
+ *
+ * @param target the base map, not null.
+ * @param subtrahendSets the maps to be subtracted, not null.
+ * @return the intersecting instance containing all given maps.
+ */
+ @Override
+ public PropertyProvider subtracted(PropertyProvider target, List<PropertyProvider> subtrahendSets) {
+ return new SubtractingPropertyProvider(target, subtrahendSets);
+ }
+
+
+ /**
+ * Creates a filtered {@link PropertyProvider} (a view) of a given base {@link }PropertyMap}. The filter hereby is
+ * applied dynamically on access, so also runtime changes of the base map are reflected appropriately.
+ *
+ * @param propertyMap the base map instance, not null.
+ * @param filter the filtger to be applied, not null.
+ * @return the new filtering instance.
+ */
+ @Override
+ public PropertyProvider filtered(Predicate<String> filter, PropertyProvider propertyMap) {
+ return new FilteredPropertyProvider(propertyMap, filter);
+ }
+
+ /**
+ * Creates a new contextual {@link PropertyProvider}. Contextual maps delegate to different instances of PropertyMap depending
+ * on the keys returned fromMap the isolationP
+ *
+ * @param mapSupplier the supplier creating new provider instances
+ * @param isolationKeySupplier the supplier providing contextual keys based on the current environment.
+ */
+ @Override
+ public PropertyProvider contextual(Supplier<PropertyProvider> mapSupplier,
+ Supplier<String> isolationKeySupplier) {
+ return new ContextualPropertyProvider(mapSupplier, isolationKeySupplier);
+ }
+
+
+ /**
+ * Creates a filtered {@link PropertyProvider} (a view) of a given base {@link }PropertyMap}. The filter hereby is
+ * applied dynamically on access, so also runtime changes of the base map are reflected appropriately.
+ *
+ * @param mainMap the main map instance, not null.
+ * @param parentMap the delegated parent map instance, not null.
+ * @return the new delegating instance.
+ */
+ @Override
+ public PropertyProvider delegating(PropertyProvider mainMap, Map<String, String> parentMap) {
+ return new DelegatingPropertyProvider(mainMap, parentMap);
+ }
+
+ /**
+ * Creates a {@link org.apache.tamaya.PropertyProvider} where all keys of a current map,
+ * existing in another map are replaced
+ * with the ones fromMap the other {@link org.apache.tamaya.PropertyProvider}. The filter hereby is
+ * applied dynamically on access, so also runtime changes of the base map are reflected appropriately.
+ * Keys not existing in the {@code mainMap}, but present in {@code replacementMao} will be hidden.
+ *
+ * @param mainMap the main map instance, which keys, present in {@code replacementMap} will be replaced
+ * with the ones
+ * in {@code replacementMap}, not null.
+ * @param replacementMap the map instance, that will replace all corresponding entries in {@code mainMap}, not null.
+ * @return the new delegating instance.
+ */
+ @Override
+ public PropertyProvider replacing(PropertyProvider mainMap, Map<String, String> replacementMap) {
+ return new ReplacingPropertyProvider(mainMap, replacementMap);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/DelegatingPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/DelegatingPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/internal/DelegatingPropertyProvider.java
new file mode 100644
index 0000000..0057bf2
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/DelegatingPropertyProvider.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tamaya.core.internal;
+
+import org.apache.tamaya.ConfigChangeSet;
+import org.apache.tamaya.MetaInfo;
+import org.apache.tamaya.MetaInfoBuilder;
+import org.apache.tamaya.PropertyProvider;
+
+import java.util.*;
+
+/**
+ * Implementation for a {@link org.apache.tamaya.PropertyProvider} that is an aggregate of
+ * multiple child instances. Controlled by an {@link org.apache.tamaya.AggregationPolicy} the
+ * following aggregations are supported:
+ * <ul>
+ * <li><b>IGNORE: </b>Ignore all overrides.</li>
+ * <li><b>: </b></li>
+ * <li><b>: </b></li>
+ * <li><b>: </b></li>
+ * </ul>
+ */
+class DelegatingPropertyProvider implements PropertyProvider{
+
+ private static final long serialVersionUID = -1419376385695224799L;
+ private PropertyProvider mainMap;
+ private Map<String,String> parentMap;
+ private MetaInfo metaInfo;
+
+ /**
+ * Creates a mew instance, with aggregation polilcy
+ * {@code AggregationPolicy.OVERRIDE}.
+ *
+ * @param mainMap The main ConfigMap.
+ * @param parentMap The delegated parent ConfigMap.
+ */
+ public DelegatingPropertyProvider(PropertyProvider mainMap, Map<String,String> parentMap){
+ this.metaInfo =
+ MetaInfoBuilder.of().setType("delegate").set("provider", mainMap.toString()).set("delegate", parentMap.toString())
+ .build();
+ Objects.requireNonNull(parentMap);
+ this.parentMap = parentMap;
+ Objects.requireNonNull(parentMap);
+ this.parentMap = parentMap;
+ }
+
+ @Override
+ public ConfigChangeSet load(){
+ return mainMap.load();
+ }
+
+ @Override
+ public boolean containsKey(String key){
+ return keySet().contains(key);
+ }
+
+ @Override
+ public Map<String,String> toMap(){
+ return null;
+ }
+
+ @Override
+ public MetaInfo getMetaInfo(){
+ return this.metaInfo;
+ }
+
+ @Override
+ public Optional<String> get(String key){
+ Optional<String> val = mainMap.get(key);
+ if(!val.isPresent()){
+ return Optional.ofNullable(parentMap.get(key));
+ }
+ return val;
+ }
+
+ @Override
+ public Set<String> keySet(){
+ Set<String> keys = new HashSet<>(mainMap.keySet());
+ keys.addAll(parentMap.keySet());
+ return keys;
+ }
+
+ @Override
+ public String toString(){
+ return super.toString() + "(mainMap=" + mainMap + ", delegate=" + parentMap + ")";
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/EnvironmentPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/EnvironmentPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/internal/EnvironmentPropertyProvider.java
new file mode 100644
index 0000000..2a4064b
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/EnvironmentPropertyProvider.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tamaya.core.internal;
+
+import org.apache.tamaya.MetaInfoBuilder;
+import org.apache.tamaya.core.properties.AbstractPropertyProvider;
+
+import java.util.Map;
+
+class EnvironmentPropertyProvider extends AbstractPropertyProvider {
+
+ private static final long serialVersionUID = 4753258482658331010L;
+
+ public Map<String,String> toMap(){
+ return System.getenv();
+ }
+
+ public EnvironmentPropertyProvider(){
+ super(MetaInfoBuilder.of().setType("env-properties").build());
+ }
+
+ @Override
+ public String toString(){
+ return "EnvironmentPropertyMap{" +
+ "props=" + super.toString() +
+ '}';
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/FilteredPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/FilteredPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/internal/FilteredPropertyProvider.java
new file mode 100644
index 0000000..cd83c1d
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/FilteredPropertyProvider.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tamaya.core.internal;
+
+import org.apache.tamaya.ConfigChangeSet;
+import org.apache.tamaya.MetaInfoBuilder;
+import org.apache.tamaya.PropertyProvider;
+import org.apache.tamaya.core.properties.AbstractPropertyProvider;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Predicate;
+
+class FilteredPropertyProvider extends AbstractPropertyProvider {
+
+ private static final long serialVersionUID = 4301042530074932562L;
+ private PropertyProvider unit;
+ private Predicate<String> filter;
+
+ public FilteredPropertyProvider(PropertyProvider configuration, Predicate<String> filter){
+ super(MetaInfoBuilder.of(configuration.getMetaInfo()).setType("filtered").set("filter", filter.toString()).build());
+ Objects.requireNonNull(configuration);
+ this.unit = configuration;
+ this.filter = filter;
+ }
+
+ @Override
+ public Map<String,String> toMap(){
+ final Map<String,String> result = new HashMap<>();
+ this.unit.toMap().entrySet().forEach(e -> {
+ if(filter.test(e.getKey())){
+ result.put(e.getKey(), e.getValue());
+ }
+ });
+ return result;
+ }
+
+ @Override
+ public ConfigChangeSet load(){
+ unit.load();
+ return super.load();
+ }
+
+ /**
+ * Apply a config change to this item. Hereby the change must be related to the same instance.
+ * @param change the config change
+ * @throws org.apache.tamaya.ConfigException if an unrelated change was passed.
+ * @throws UnsupportedOperationException when the configuration is not writable.
+ */
+ @Override
+ public void apply(ConfigChangeSet change){
+ this.unit.apply(change);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/FreezedPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/FreezedPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/internal/FreezedPropertyProvider.java
new file mode 100644
index 0000000..fb43385
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/FreezedPropertyProvider.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tamaya.core.internal;
+
+import org.apache.tamaya.ConfigChangeSet;
+import org.apache.tamaya.MetaInfo;
+import org.apache.tamaya.MetaInfoBuilder;
+import org.apache.tamaya.PropertyProvider;
+
+import java.io.Serializable;
+import java.time.Instant;
+import java.util.*;
+
+/**
+ * This class models a freezed instance of an {@link org.apache.tamaya.PropertyProvider}.
+ * Created by Anatole on 28.03.14.
+ */
+final class FreezedPropertyProvider implements PropertyProvider, Serializable{
+
+ private static final long serialVersionUID = 3365413090311267088L;
+ private Map<String,Map<String,String>> fieldMMetaInfo = new HashMap<>();
+ private MetaInfo metaInfo;
+ private Map<String,String> properties = new HashMap<>();
+
+ private FreezedPropertyProvider(MetaInfo metaInfo, PropertyProvider propertyMap) {
+ Map<String, String> map = propertyMap.toMap();
+ this.properties.putAll(map);
+ this.properties = Collections.unmodifiableMap(this.properties);
+ if (metaInfo == null) {
+ this.metaInfo =
+ MetaInfoBuilder.of(propertyMap.getMetaInfo()).set("freezedAt", Instant.now().toString()).build();
+ } else {
+ this.metaInfo = metaInfo;
+ }
+ }
+
+ public static PropertyProvider of(MetaInfo metaInfo, PropertyProvider propertyProvider){
+ if(propertyProvider instanceof FreezedPropertyProvider){
+ return propertyProvider;
+ }
+ return new FreezedPropertyProvider(metaInfo, propertyProvider);
+ }
+
+ @Override
+ public ConfigChangeSet load(){
+ return ConfigChangeSet.emptyChangeSet(this);
+ }
+
+ public int size(){
+ return properties.size();
+ }
+
+ public boolean isEmpty(){
+ return properties.isEmpty();
+ }
+
+ @Override
+ public boolean containsKey(String key){
+ return properties.containsKey(key);
+ }
+
+ @Override
+ public Map<String,String> toMap(){
+ return Collections.unmodifiableMap(this.properties);
+ }
+
+ @Override
+ public MetaInfo getMetaInfo(){
+ return this.metaInfo;
+ }
+
+ @Override
+ public Optional<String> get(String key){
+ return Optional.ofNullable(properties.get(key));
+ }
+
+ @Override
+ public Set<String> keySet(){
+ return properties.keySet();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/IntersectingPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/IntersectingPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/internal/IntersectingPropertyProvider.java
new file mode 100644
index 0000000..2e82d3c
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/IntersectingPropertyProvider.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tamaya.core.internal;
+
+import org.apache.tamaya.*;
+import org.apache.tamaya.core.properties.AbstractPropertyProvider;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * Provider implementation that combines multiple other providers by intersecting
+ * the key/values common to all providers, conflicting keys are resolved using an
+ * {@link org.apache.tamaya.AggregationPolicy}.
+ */
+class IntersectingPropertyProvider extends AbstractPropertyProvider {
+
+ private List<PropertyProvider> providers;
+ private PropertyProvider aggregatedDelegate;
+
+ public IntersectingPropertyProvider(AggregationPolicy policy, List<PropertyProvider> providers) {
+ super(MetaInfoBuilder.of().setType("intersection").build());
+ this.providers = new ArrayList<>(providers);
+ aggregatedDelegate = PropertyProviders.aggregate(policy, this.providers);
+ }
+
+ public IntersectingPropertyProvider(MetaInfo metaInfo, AggregationPolicy policy, PropertyProvider... providers) {
+ super(metaInfo);
+ this.providers = Arrays.asList(Objects.requireNonNull(providers));
+ aggregatedDelegate = PropertyProviders.aggregate(policy, providers);
+ }
+
+
+ @Override
+ public Optional<String> get(String key) {
+ if (containsKey(key))
+ return aggregatedDelegate.get(key);
+ return Optional.empty();
+ }
+
+ private boolean filter(Map.Entry<String, String> entry) {
+ return containsKey(entry.getKey());
+ }
+
+ @Override
+ public boolean containsKey(String key) {
+ for (PropertyProvider prov : this.providers) {
+ if (!prov.containsKey(key)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public Map<String, String> toMap() {
+ return aggregatedDelegate.toMap().entrySet().stream().filter(en -> containsKey(en.getKey())).collect(
+ Collectors.toConcurrentMap(en -> en.getKey(), en -> en.getValue()));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/MapBasedPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/MapBasedPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/internal/MapBasedPropertyProvider.java
new file mode 100644
index 0000000..d782353
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/MapBasedPropertyProvider.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tamaya.core.internal;
+
+import org.apache.tamaya.ConfigChangeSet;
+import org.apache.tamaya.MetaInfo;
+import org.apache.tamaya.core.properties.AbstractPropertyProvider;
+
+import java.beans.PropertyChangeEvent;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Logger;
+
+/**
+ * Models a {@link org.apache.tamaya.PropertyProvider} that can be build using a builder pattern.
+ */
+class MapBasedPropertyProvider extends AbstractPropertyProvider {
+
+ private static final long serialVersionUID = 7601389831472839249L;
+
+ private static final Logger LOG = Logger.getLogger(MapBasedPropertyProvider.class.getName());
+ /**
+ * The unit's entries.
+ */
+ private Map<String,String> entries = new ConcurrentHashMap<>();
+
+ /**
+ * Constructor used by {@link org.apache.tamaya.core.properties.MapBasedPropertyProviderBuilder}, or subclasses.
+ *
+ * @param entries the config entries, not null.
+ */
+ MapBasedPropertyProvider(MetaInfo metaInfo, Map<String,String> entries){
+ super(metaInfo);
+ Objects.requireNonNull(entries, "entries required.");
+ this.entries.putAll(entries);
+ }
+
+
+ /**
+ * Constructor used by {@link org.apache.tamaya.core.properties.MapBasedPropertyProviderBuilder}, or subclasses.
+ *
+ * @param entries the entries
+ * @param sources the sources
+ * @param errors the errors
+ */
+ MapBasedPropertyProvider(MetaInfo metaInfo, Map<String,String> entries, Set<String> sources,
+ Collection<Throwable> errors){
+ super(metaInfo);
+ Objects.requireNonNull(entries, "entries required.");
+ this.entries.putAll(entries);
+ addSources(sources);
+ }
+
+ MapBasedPropertyProvider(MetaInfo metaInfo, Set<String> sources){
+ super(metaInfo);
+ addSources(sources);
+ }
+
+ @Override
+ public Map<String, String> toMap() {
+ return new HashMap<>(this.entries);
+ }
+
+ @Override
+ public ConfigChangeSet load(){
+ // Can not reload...
+ return ConfigChangeSet.emptyChangeSet(this);
+ }
+
+ /**
+ * Apply a config change to this item. Hereby the change must be related to the same instance.
+ * @param change the config change
+ * @throws org.apache.tamaya.ConfigException if an unrelated change was passed.
+ * @throws UnsupportedOperationException when the configuration is not writable.
+ */
+ @Override
+ public void apply(ConfigChangeSet change){
+ change.getEvents().forEach(this::applyChange);
+ }
+
+ private void applyChange(PropertyChangeEvent propertyChangeEvent) {
+ LOG.finest(() -> "Applying change to map provider: " + propertyChangeEvent);
+ if(propertyChangeEvent.getNewValue()==null){
+ this.entries.remove(propertyChangeEvent.getPropertyName());
+ }
+ else{
+ this.entries.put(propertyChangeEvent.getPropertyName(), propertyChangeEvent.getNewValue().toString());
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/MetaConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/MetaConfig.java b/core/src/main/java/org/apache/tamaya/core/internal/MetaConfig.java
index d02e7ec..1c8b07d 100644
--- a/core/src/main/java/org/apache/tamaya/core/internal/MetaConfig.java
+++ b/core/src/main/java/org/apache/tamaya/core/internal/MetaConfig.java
@@ -33,7 +33,7 @@ import java.util.logging.Logger;
/**
* Singleton to read the configuration for the configuration system
- * from {@code META-INF/config.properties}.
+ * fromMap {@code META-INF/config.properties}.
* Created by Anatole on 17.10.2014.
*/
public final class MetaConfig {
@@ -54,7 +54,7 @@ public final class MetaConfig {
properties.putAll(read);
}
catch(Exception e){
- LOG.log(Level.SEVERE, e, () -> "Error reading meta configuration from " + uri);
+ LOG.log(Level.SEVERE, e, () -> "Error reading meta configuration fromMap " + uri);
}
}
}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/PathBasedPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/PathBasedPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/internal/PathBasedPropertyProvider.java
new file mode 100644
index 0000000..8ec3874
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/PathBasedPropertyProvider.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tamaya.core.internal;
+
+import org.apache.tamaya.*;
+import org.apache.tamaya.core.config.ConfigurationFormats;
+import org.apache.tamaya.core.properties.AbstractPropertyProvider;
+import org.apache.tamaya.spi.Bootstrap;
+import org.apache.tamaya.core.spi.ConfigurationFormat;
+import org.apache.tamaya.core.spi.ResourceLoader;
+
+import java.net.URI;
+import java.util.*;
+
+/**
+ * Created by Anatole on 16.10.2014.
+ */
+final class PathBasedPropertyProvider extends AbstractPropertyProvider {
+
+ private List<String> paths = new ArrayList<>();
+ private Map<String, String> properties = new HashMap<>();
+ private AggregationPolicy aggregationPolicy;
+
+ public PathBasedPropertyProvider(MetaInfo metaInfo, Collection<String> paths, AggregationPolicy aggregationPolicy) {
+ super(metaInfo);
+ this.paths.addAll(Objects.requireNonNull(paths));
+ this.aggregationPolicy = Objects.requireNonNull(aggregationPolicy);
+ init();
+ }
+
+ @Override
+ public Map<String, String> toMap() {
+ return this.properties;
+ }
+
+ private void init() {
+ List<String> sources = new ArrayList<>();
+ List<String> effectivePaths = new ArrayList<>();
+ paths.forEach((path) -> {
+ effectivePaths.add(path);
+ for (URI uri : Bootstrap.getService(ResourceLoader.class).getResources(path)) {
+ ConfigurationFormat format = ConfigurationFormats.getFormat(uri);
+ if (format != null) {
+ try {
+ Map<String, String> read = format.readConfiguration(uri);
+ sources.add(uri.toString());
+ read.forEach((k, v) -> {
+ switch (aggregationPolicy) {
+ case OVERRIDE:
+ properties.put(k, v);
+ break;
+ case IGNORE:
+ properties.putIfAbsent(k, v);
+ break;
+ case EXCEPTION:
+ default:
+ String prev = properties.putIfAbsent(k, v);
+ if (prev != null && !prev.equals(v)) {
+ throw new ConfigException("Conflicting value encountered in " + uri
+ + ": key=" + k + ", value=" + v + ", existing=" + prev);
+ }
+ }
+ });
+ }
+ catch(ConfigException e){
+ throw e;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ });
+ metaInfo = MetaInfoBuilder.of(getMetaInfo())
+ .setSourceExpressions(new String[effectivePaths.size()])
+ .set("sources", sources.toString()).build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/ReplacingPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/ReplacingPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/internal/ReplacingPropertyProvider.java
new file mode 100644
index 0000000..877f544
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/ReplacingPropertyProvider.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tamaya.core.internal;
+
+import org.apache.tamaya.ConfigChangeSet;
+import org.apache.tamaya.MetaInfo;
+import org.apache.tamaya.MetaInfoBuilder;
+import org.apache.tamaya.PropertyProvider;
+
+import java.util.*;
+
+/**
+ * Implementation for a {@link org.apache.tamaya.PropertyProvider} that is an aggregate of
+ * multiple child instances, where all existing key/values in a replacementMap will
+ * replace values in a main map, if present there.
+ */
+class ReplacingPropertyProvider implements PropertyProvider{
+
+ private static final long serialVersionUID = -1419376385695224799L;
+ private PropertyProvider mainMap;
+ private Map<String,String> replacingMap;
+ private MetaInfo metaInfo;
+
+ /**
+ * Creates a mew instance, with aggregation polilcy
+ * {@code AggregationPolicy.OVERRIDE}.
+ *
+ * @param mainMap The main ConfigMap.
+ * @param replacingMap The replacing ConfigMap.
+ */
+ public ReplacingPropertyProvider(PropertyProvider mainMap, Map<String,String> replacingMap){
+ Objects.requireNonNull(replacingMap);
+ this.replacingMap = replacingMap;
+ Objects.requireNonNull(mainMap);
+ this.mainMap = mainMap;
+ this.metaInfo = MetaInfoBuilder.of().setType("replacing").set("mainProvider", mainMap.toString())
+ .set("replacing", replacingMap.toString()).build();
+ }
+
+ @Override
+ public ConfigChangeSet load(){
+ return mainMap.load();
+ }
+
+ @Override
+ public boolean containsKey(String key){
+ return mainMap.containsKey(key);
+ }
+
+ @Override
+ public Map<String,String> toMap(){
+ Map<String,String> result = new HashMap<>(replacingMap);
+ for(Map.Entry<String,String> en : mainMap.toMap().entrySet()){
+ if(!replacingMap.containsKey(en.getKey())){
+ result.put(en.getKey(), en.getValue());
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public MetaInfo getMetaInfo(){
+ return this.metaInfo;
+ }
+
+ @Override
+ public Optional<String> get(String key){
+ String val = replacingMap.get(key);
+ if(val == null){
+ return mainMap.get(key);
+ }
+ return Optional.ofNullable(val);
+ }
+
+ @Override
+ public Set<String> keySet(){
+ return mainMap.keySet();
+ }
+
+ /**
+ * Apply a config change to this item. Hereby the change must be related to the same instance.
+ * @param change the config change
+ * @throws org.apache.tamaya.ConfigException if an unrelated change was passed.
+ * @throws UnsupportedOperationException when the configuration is not writable.
+ */
+ @Override
+ public void apply(ConfigChangeSet change){
+ this.mainMap.apply(change);
+ }
+
+ @Override
+ public String toString(){
+ return super.toString() + "(mainMap=" + mainMap + ", replacingMap=" + replacingMap + ")";
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/SubtractingPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/SubtractingPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/internal/SubtractingPropertyProvider.java
new file mode 100644
index 0000000..9a76cdc
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/SubtractingPropertyProvider.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tamaya.core.internal;
+
+import org.apache.tamaya.ConfigChangeSet;
+import org.apache.tamaya.MetaInfoBuilder;
+import org.apache.tamaya.PropertyProvider;
+import org.apache.tamaya.core.properties.AbstractPropertyProvider;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+class SubtractingPropertyProvider extends AbstractPropertyProvider {
+
+ private static final long serialVersionUID = 4301042530074932562L;
+ private PropertyProvider unit;
+ private List<PropertyProvider> subtrahends;
+
+ public SubtractingPropertyProvider(PropertyProvider configuration, List<PropertyProvider> subtrahends){
+ super(MetaInfoBuilder.of(configuration.getMetaInfo()).setType("sutracted").build());
+ Objects.requireNonNull(configuration);
+ this.unit = configuration;
+ this.subtrahends = new ArrayList<>(subtrahends);
+ }
+
+ private boolean filter(Map.Entry<String,String> entry){
+ for(PropertyProvider prov: subtrahends){
+ if(prov.containsKey(entry.getKey())){
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public Map<String,String> toMap(){
+ return this.unit.toMap().entrySet().stream().filter(this::filter).collect(Collectors.toMap(
+ (en) -> en.getKey(),
+ (en) -> en.getValue()
+ ));
+ }
+
+ @Override
+ public ConfigChangeSet load(){
+ unit.load();
+ return super.load();
+ }
+
+ /**
+ * Apply a config change to this item. Hereby the change must be related to the same instance.
+ * @param change the config change
+ * @throws org.apache.tamaya.ConfigException if an unrelated change was passed.
+ * @throws UnsupportedOperationException when the configuration is not writable.
+ */
+ @Override
+ public void apply(ConfigChangeSet change){
+ this.unit.apply(change);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/SystemPropertiesPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/SystemPropertiesPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/internal/SystemPropertiesPropertyProvider.java
new file mode 100644
index 0000000..3f3760a
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/SystemPropertiesPropertyProvider.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tamaya.core.internal;
+
+import org.apache.tamaya.MetaInfoBuilder;
+import org.apache.tamaya.core.env.ConfiguredSystemProperties;
+import org.apache.tamaya.core.properties.AbstractPropertyProvider;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+class SystemPropertiesPropertyProvider extends AbstractPropertyProvider {
+
+
+ private static final long serialVersionUID = -5935940312707001199L;
+
+ public SystemPropertiesPropertyProvider(){
+ super(MetaInfoBuilder.of().setType("sys-properties").build());
+ }
+
+ @Override
+ public Map<String,String> toMap(){
+ Properties sysProps = System.getProperties();
+ if(sysProps instanceof ConfiguredSystemProperties){
+ sysProps = ((ConfiguredSystemProperties)sysProps).getInitialProperties();
+ }
+ Map<String,String> props = new HashMap<>();
+ for (Map.Entry<Object,Object> en : sysProps.entrySet()) {
+ props.put(en.getKey().toString(), en.getValue().toString());
+ }
+ return Collections.unmodifiableMap(props);
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/UriBasedPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/UriBasedPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/internal/UriBasedPropertyProvider.java
new file mode 100644
index 0000000..0604e0b
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/UriBasedPropertyProvider.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tamaya.core.internal;
+
+import org.apache.tamaya.AggregationPolicy;
+import org.apache.tamaya.ConfigException;
+import org.apache.tamaya.MetaInfo;
+import org.apache.tamaya.MetaInfoBuilder;
+import org.apache.tamaya.core.config.ConfigurationFormats;
+import org.apache.tamaya.core.properties.AbstractPropertyProvider;
+import org.apache.tamaya.spi.Bootstrap;
+import org.apache.tamaya.core.spi.ConfigurationFormat;
+
+import java.net.URI;
+import java.util.*;
+
+/**
+ * Created by Anatole on 16.10.2014.
+ */
+final class URIBasedPropertyProvider extends AbstractPropertyProvider {
+
+ private List<URI> uris = new ArrayList<>();
+ private Map<String,String> properties = new HashMap<>();
+ private AggregationPolicy aggregationPolicy;
+
+ public URIBasedPropertyProvider(MetaInfo metaInfo, List<URI> uris, AggregationPolicy aggregationPolicy) {
+ super(metaInfo);
+ this.uris.addAll(Objects.requireNonNull(uris));
+ this.aggregationPolicy = Objects.requireNonNull(aggregationPolicy);
+ init();
+ }
+
+ private void init(){
+ List<String> sources = new ArrayList<>();
+ for(URI uri : uris){
+ ConfigurationFormat format = ConfigurationFormats.getFormat(uri);
+ if(format != null){
+ try{
+ Map<String, String> read = format.readConfiguration(uri);
+ sources.add(uri.toString());
+ read.forEach((k, v) -> {
+ switch (aggregationPolicy) {
+ case OVERRIDE:
+ properties.put(k, v);
+ break;
+ case IGNORE:
+ properties.putIfAbsent(k, v);
+ break;
+ case EXCEPTION:
+ default:
+ String prev = properties.putIfAbsent(k, v);
+ if (prev != null) {
+ throw new ConfigException("Duplicate value encountered in " + uri
+ + ": key=" + k + ", value=" + v + ", existing=" + prev);
+ }
+ }
+ });
+ }
+ catch(ConfigException e){
+ throw e;
+ }
+ catch(Exception e){
+ e.printStackTrace();
+ }
+ }
+ }
+ MetaInfoBuilder metaInfoBuilder = MetaInfoBuilder.of(getMetaInfo());
+ metaInfo = metaInfoBuilder
+ .setSources(sources.toString()).build();
+ }
+
+ @Override
+ public Map<String, String> toMap() {
+ return properties;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/env/ClassLoaderDependentApplicationEnvironmentProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/env/ClassLoaderDependentApplicationEnvironmentProvider.java b/core/src/main/java/org/apache/tamaya/core/internal/env/ClassLoaderDependentApplicationEnvironmentProvider.java
index 478ff8c..14ea12f 100644
--- a/core/src/main/java/org/apache/tamaya/core/internal/env/ClassLoaderDependentApplicationEnvironmentProvider.java
+++ b/core/src/main/java/org/apache/tamaya/core/internal/env/ClassLoaderDependentApplicationEnvironmentProvider.java
@@ -86,7 +86,7 @@ public class ClassLoaderDependentApplicationEnvironmentProvider implements Envir
data.putAll(read);
}
catch(Exception e){
- LOG.log(Level.SEVERE, e, () -> "Error reading application environment data from " + uri);
+ LOG.log(Level.SEVERE, e, () -> "Error reading application environment data fromMap " + uri);
}
}
String applicationId = data.getOrDefault(WARID_PROP, cl.toString());
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/env/ClassLoaderDependentEarEnvironmentProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/env/ClassLoaderDependentEarEnvironmentProvider.java b/core/src/main/java/org/apache/tamaya/core/internal/env/ClassLoaderDependentEarEnvironmentProvider.java
index f407b93..7251896 100644
--- a/core/src/main/java/org/apache/tamaya/core/internal/env/ClassLoaderDependentEarEnvironmentProvider.java
+++ b/core/src/main/java/org/apache/tamaya/core/internal/env/ClassLoaderDependentEarEnvironmentProvider.java
@@ -89,7 +89,7 @@ public class ClassLoaderDependentEarEnvironmentProvider implements EnvironmentPr
data.putAll(read);
}
catch(Exception e){
- LOG.log(Level.SEVERE, e, () -> "Error reading ear environment data from " + uri);
+ LOG.log(Level.SEVERE, e, () -> "Error reading ear environment data fromMap " + uri);
}
}
String earId = data.getOrDefault(EARID_PROP, cl.toString());
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/env/SystemClassLoaderEnvironmentProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/env/SystemClassLoaderEnvironmentProvider.java b/core/src/main/java/org/apache/tamaya/core/internal/env/SystemClassLoaderEnvironmentProvider.java
index 575dc3d..9c6ecbe 100644
--- a/core/src/main/java/org/apache/tamaya/core/internal/env/SystemClassLoaderEnvironmentProvider.java
+++ b/core/src/main/java/org/apache/tamaya/core/internal/env/SystemClassLoaderEnvironmentProvider.java
@@ -34,7 +34,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;
/**
- * System environment provider (loaded only once using the system class loader) that loads additional environment properties from the classpath evaluating
+ * System environment provider (loaded only once using the system class loader) that loads additional environment properties fromMap the classpath evaluating
* {@code META-INF/env/system.properties, META-INF/env/system.xml and META-INF/env/system.ini}.
*/
public class SystemClassLoaderEnvironmentProvider implements EnvironmentProvider {
@@ -69,7 +69,7 @@ public class SystemClassLoaderEnvironmentProvider implements EnvironmentProvider
builder.setAll(data);
}
catch(Exception e){
- LOG.log(Level.SEVERE, e, () -> "Error readong environment data from " + uri);
+ LOG.log(Level.SEVERE, e, () -> "Error readong environment data fromMap " + uri);
}
}
builder.setParent(parentEnvironment);
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/format/IniFormat.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/format/IniFormat.java b/core/src/main/java/org/apache/tamaya/core/internal/format/IniFormat.java
index 1b0bada..cb76d95 100644
--- a/core/src/main/java/org/apache/tamaya/core/internal/format/IniFormat.java
+++ b/core/src/main/java/org/apache/tamaya/core/internal/format/IniFormat.java
@@ -60,6 +60,7 @@ public class IniFormat implements ConfigurationFormat{
lineNum++;
line = line.trim();
if(line.isEmpty()){
+ line = reader.readLine();
continue;
}
if(line.startsWith("[")){
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredField.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredField.java b/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredField.java
index 63f712e..fe79615 100644
--- a/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredField.java
+++ b/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredField.java
@@ -56,7 +56,7 @@ public class ConfiguredField {
}
/**
- * Evaluate the initial value from the configuration and apply it to the field.
+ * Evaluate the initial value fromMap the configuration and apply it to the field.
*
* @param target the target instance.
* @throws ConfigException if evaluation or conversion failed.
@@ -185,7 +185,7 @@ public class ConfiguredField {
}
/**
- * This method checks if the given (qualified) configuration key is referenced from this field.
+ * This method checks if the given (qualified) configuration key is referenced fromMap this field.
* This is useful to determine, if a key changed in a configuration should trigger any change events
* on the related instances.
*
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredInstancesManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredInstancesManager.java b/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredInstancesManager.java
index c037c41..00f0958 100644
--- a/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredInstancesManager.java
+++ b/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredInstancesManager.java
@@ -40,7 +40,7 @@ public final class ConfiguredInstancesManager implements PropertyChangeListener{
private final Object LOCK = new Object();
private ConfiguredInstancesManager(){
-// Configuration.addGlobalPropertyChangeListener(this);
+// Configuration.addConfigChangeListener(this);
}
public static <T> void register(ConfiguredType configuredType, Object instance) {
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredMethod.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredMethod.java b/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredMethod.java
index 6e39a25..970a748 100644
--- a/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredMethod.java
+++ b/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredMethod.java
@@ -123,7 +123,7 @@ public class ConfiguredMethod {
}
/**
- * This method checks if the given (qualified) configuration key is referenced from this field.
+ * This method checks if the given (qualified) configuration key is referenced fromMap this field.
* This is useful to determine, if a key changed in a configuration should trigger any change events
* on the related instances.
*
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/logging/Slf4jLogger.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/logging/Slf4jLogger.java b/core/src/main/java/org/apache/tamaya/core/internal/logging/Slf4jLogger.java
index 0bdf367..d7f359a 100644
--- a/core/src/main/java/org/apache/tamaya/core/internal/logging/Slf4jLogger.java
+++ b/core/src/main/java/org/apache/tamaya/core/internal/logging/Slf4jLogger.java
@@ -69,7 +69,7 @@ public class Slf4jLogger extends AbstractDelegatingLogger {
@Override
public Level getLevel() {
final Level level;
- // Verify from the wider (trace) to the narrower (error)
+ // Verify fromMap the wider (trace) to the narrower (error)
if (logger.isTraceEnabled()) {
level = Level.FINEST;
} else if (logger.isDebugEnabled()) {
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/resources/AntPathClasspathResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/resources/AntPathClasspathResolver.java b/core/src/main/java/org/apache/tamaya/core/internal/resources/AntPathClasspathResolver.java
index a7f09ef..9d4e96b 100644
--- a/core/src/main/java/org/apache/tamaya/core/internal/resources/AntPathClasspathResolver.java
+++ b/core/src/main/java/org/apache/tamaya/core/internal/resources/AntPathClasspathResolver.java
@@ -18,10 +18,15 @@
*/
package org.apache.tamaya.core.internal.resources;
+import org.apache.tamaya.core.internal.resources.io.PathMatchingResourcePatternResolver;
+import org.apache.tamaya.core.internal.resources.io.Resource;
+
+import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
public class AntPathClasspathResolver implements PathResolver{
@@ -33,22 +38,38 @@ public class AntPathClasspathResolver implements PathResolver{
@Override
public Collection<URI> resolve(ClassLoader classLoader, Stream<String> expressions){
+ PathMatchingResourcePatternResolver resolver = PathMatchingResourcePatternResolver.of(classLoader);
List<URI> result = new ArrayList<>();
expressions.forEach((expression) -> {
- if(expression.startsWith("classpath:")){
- String exp = expression.substring("classpath:".length());
- URL url = classLoader.getResource(exp);
- if(url != null){
- try{
- result.add(url.toURI());
- }
- catch(URISyntaxException e){
+ try {
+ Resource[] resources = resolver.getResources(expression);
+ for (Resource res : resources) {
+ try {
+ result.add(res.getURI());
+ } catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
+ catch(IOException e){
+ // TODO log
+ }
});
+// if(expression.startsWith("classpath:")){
+// String exp = expression.substring("classpath:".length());
+// URL url = classLoader.getResource(exp);
+// if(url != null){
+// try{
+// result.add(url.toURI());
+// }
+// catch(URISyntaxException e){
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+// }
+// }
+// });
return result;
}
}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/resources/AntPathClasspathsResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/resources/AntPathClasspathsResolver.java b/core/src/main/java/org/apache/tamaya/core/internal/resources/AntPathClasspathsResolver.java
index 4f3a898..fe4ca28 100644
--- a/core/src/main/java/org/apache/tamaya/core/internal/resources/AntPathClasspathsResolver.java
+++ b/core/src/main/java/org/apache/tamaya/core/internal/resources/AntPathClasspathsResolver.java
@@ -18,6 +18,9 @@
*/
package org.apache.tamaya.core.internal.resources;
+import org.apache.tamaya.core.internal.resources.io.PathMatchingResourcePatternResolver;
+import org.apache.tamaya.core.internal.resources.io.Resource;
+
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
@@ -34,31 +37,49 @@ public class AntPathClasspathsResolver implements PathResolver{
@Override
public Collection<URI> resolve(ClassLoader classLoader, Stream<String> expressions){
+ PathMatchingResourcePatternResolver resolver = PathMatchingResourcePatternResolver.of(classLoader);
List<URI> result = new ArrayList<>();
- Objects.requireNonNull(classLoader);
expressions.forEach((expression) -> {
- if(expression.startsWith("classpath*:")){
- String exp = expression.substring("classpath*:".length());
- Enumeration<URL> urls;
- try{
- urls = classLoader.getResources(exp);
- while(urls.hasMoreElements()){
- URL url = (URL) urls.nextElement();
- try{
- result.add(url.toURI());
- }
- catch(URISyntaxException e){
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
+ try {
+ Resource[] resources = resolver.getResources(expression);
+ for (Resource res : resources) {
+ try {
+ result.add(res.getURI());
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
}
}
- catch(IOException e1){
- // TODO Auto-generated catch block
- e1.printStackTrace();
- }
+ }
+ catch(IOException e){
+ // TODO log
}
});
+// List<URI> result = new ArrayList<>();
+// Objects.requireNonNull(classLoader);
+// expressions.forEach((expression) -> {
+// if(expression.startsWith("classpath*:")){
+// String exp = expression.substring("classpath*:".length());
+// Enumeration<URL> urls;
+// try{
+// urls = classLoader.getResources(exp);
+// while(urls.hasMoreElements()){
+// URL url = (URL) urls.nextElement();
+// try{
+// result.add(url.toURI());
+// }
+// catch(URISyntaxException e){
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+// }
+// }
+// catch(IOException e1){
+// // TODO Auto-generated catch block
+// e1.printStackTrace();
+// }
+// }
+// });
return result;
}
}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/resources/AntPathFileResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/resources/AntPathFileResolver.java b/core/src/main/java/org/apache/tamaya/core/internal/resources/AntPathFileResolver.java
index 7b0a2fe..09758aa 100644
--- a/core/src/main/java/org/apache/tamaya/core/internal/resources/AntPathFileResolver.java
+++ b/core/src/main/java/org/apache/tamaya/core/internal/resources/AntPathFileResolver.java
@@ -18,7 +18,11 @@
*/
package org.apache.tamaya.core.internal.resources;
+import org.apache.tamaya.core.internal.resources.io.PathMatchingResourcePatternResolver;
+import org.apache.tamaya.core.internal.resources.io.Resource;
+
import java.io.File;
+import java.io.IOException;
import java.net.URI;
import java.util.*;
import java.util.stream.Stream;
@@ -33,15 +37,23 @@ public class AntPathFileResolver implements PathResolver{
@Override
public Collection<URI> resolve(ClassLoader classLoader, Stream<String> expressions){
+ PathMatchingResourcePatternResolver resolver = PathMatchingResourcePatternResolver.of(classLoader);
List<URI> result = new ArrayList<>();
expressions.forEach((expression) -> {
- if(expression.startsWith("file:")){
- String exp = expression.substring("file:".length());
- File f = new File(exp);
- if(f.exists() && f.isFile()){
- result.add(f.toURI());
+ try {
+ Resource[] resources = resolver.getResources(expression);
+ for (Resource res : resources) {
+ try {
+ result.add(res.getURI());
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
}
}
+ catch(IOException e){
+ // TODO log
+ }
});
return result;
}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/resources/AntPathFilesResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/resources/AntPathFilesResolver.java b/core/src/main/java/org/apache/tamaya/core/internal/resources/AntPathFilesResolver.java
index 852bdfc..7290f45 100644
--- a/core/src/main/java/org/apache/tamaya/core/internal/resources/AntPathFilesResolver.java
+++ b/core/src/main/java/org/apache/tamaya/core/internal/resources/AntPathFilesResolver.java
@@ -18,7 +18,11 @@
*/
package org.apache.tamaya.core.internal.resources;
+import org.apache.tamaya.core.internal.resources.io.PathMatchingResourcePatternResolver;
+import org.apache.tamaya.core.internal.resources.io.Resource;
+
import java.io.File;
+import java.io.IOException;
import java.net.URI;
import java.util.*;
import java.util.stream.Stream;
@@ -32,15 +36,23 @@ public class AntPathFilesResolver implements PathResolver{
@Override
public Collection<URI> resolve(ClassLoader classLoader, Stream<String> expressions){
+ PathMatchingResourcePatternResolver resolver = PathMatchingResourcePatternResolver.of(classLoader);
List<URI> result = new ArrayList<>();
expressions.forEach((expression) -> {
- if(expression.startsWith("file*:")){
- String exp = expression.substring("file:".length());
- File f = new File(exp);
- if(f.exists() && f.isFile()){
- result.add(f.toURI());
+ try {
+ Resource[] resources = resolver.getResources(expression);
+ for (Resource res : resources) {
+ try {
+ result.add(res.getURI());
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
}
}
+ catch(IOException e){
+ // TODO log
+ }
});
return result;
}
[5/8] incubator-tamaya git commit: TAMAYA-14: added resource support.
TAMAYA-15: Moved PropertyProviders to API,
added SPI. TAMAYA-8: Added/improved Javadoc.
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/resources/io/ClassUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/resources/io/ClassUtils.java b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/ClassUtils.java
new file mode 100644
index 0000000..6041cae
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/ClassUtils.java
@@ -0,0 +1,1232 @@
+/*
+* Copyright 2002-2014 the original author or authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package org.apache.tamaya.core.internal.resources.io;
+
+import java.beans.Introspector;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Proxy;
+import java.util.*;
+
+/**
+* Miscellaneous class utility methods.
+* Mainly for internal use within the framework.
+*
+* @author Juergen Hoeller
+* @author Keith Donald
+* @author Rob Harrop
+* @author Sam Brannen
+* @since 1.1
+*/
+public class ClassUtils {
+
+ /** Suffix for array class names: "[]" */
+ public static final String ARRAY_SUFFIX = "[]";
+
+ /** Prefix for internal array class names: "[" */
+ private static final String INTERNAL_ARRAY_PREFIX = "[";
+
+ /** Prefix for internal non-primitive array class names: "[L" */
+ private static final String NON_PRIMITIVE_ARRAY_PREFIX = "[L";
+
+ /** The package separator character '.' */
+ private static final char PACKAGE_SEPARATOR = '.';
+
+ /** The path separator character '/' */
+ private static final char PATH_SEPARATOR = '/';
+
+ /** The inner class separator character '$' */
+ private static final char INNER_CLASS_SEPARATOR = '$';
+//
+// /** The CGLIB class separator character "$$" */
+// public static final String CGLIB_CLASS_SEPARATOR = "$$";
+//
+// /** The ".class" file suffix */
+// public static final String CLASS_FILE_SUFFIX = ".class";
+//
+
+ /**
+ * Map with primitive wrapper type as key and corresponding primitive
+ * type as value, for example: Integer.class -> int.class.
+ */
+ private static final Map<Class<?>, Class<?>> primitiveWrapperTypeMap = new HashMap<Class<?>, Class<?>>(8);
+
+ /**
+ * Map with primitive type as key and corresponding wrapper
+ * type as value, for example: int.class -> Integer.class.
+ */
+ private static final Map<Class<?>, Class<?>> primitiveTypeToWrapperMap = new HashMap<Class<?>, Class<?>>(8);
+
+ /**
+ * Map with primitive type name as key and corresponding primitive
+ * type as value, for example: "int" -> "int.class".
+ */
+ private static final Map<String, Class<?>> primitiveTypeNameMap = new HashMap<String, Class<?>>(32);
+//
+// /**
+// * Map with common "java.lang" class name as key and corresponding Class as value.
+// * Primarily for efficient deserialization of remote invocations.
+// */
+// private static final Map<String, Class<?>> commonClassCache = new HashMap<String, Class<?>>(32);
+//
+//
+ static {
+ primitiveWrapperTypeMap.put(Boolean.class, boolean.class);
+ primitiveWrapperTypeMap.put(Byte.class, byte.class);
+ primitiveWrapperTypeMap.put(Character.class, char.class);
+ primitiveWrapperTypeMap.put(Double.class, double.class);
+ primitiveWrapperTypeMap.put(Float.class, float.class);
+ primitiveWrapperTypeMap.put(Integer.class, int.class);
+ primitiveWrapperTypeMap.put(Long.class, long.class);
+ primitiveWrapperTypeMap.put(Short.class, short.class);
+
+ for (Map.Entry<Class<?>, Class<?>> entry : primitiveWrapperTypeMap.entrySet()) {
+ primitiveTypeToWrapperMap.put(entry.getValue(), entry.getKey());
+// registerCommonClasses(entry.getKey());
+ }
+
+ Set<Class<?>> primitiveTypes = new HashSet<Class<?>>(32);
+ primitiveTypes.addAll(primitiveWrapperTypeMap.values());
+ primitiveTypes.addAll(Arrays.asList(new Class<?>[] {
+ boolean[].class, byte[].class, char[].class, double[].class,
+ float[].class, int[].class, long[].class, short[].class}));
+ primitiveTypes.add(void.class);
+ for (Class<?> primitiveType : primitiveTypes) {
+ primitiveTypeNameMap.put(primitiveType.getName(), primitiveType);
+ }
+
+// registerCommonClasses(Boolean[].class, Byte[].class, Character[].class, Double[].class,
+// Float[].class, Integer[].class, Long[].class, Short[].class);
+// registerCommonClasses(Number.class, Number[].class, String.class, String[].class,
+// Object.class, Object[].class, Class.class, Class[].class);
+// registerCommonClasses(Throwable.class, Exception.class, RuntimeException.class,
+// Error.class, StackTraceElement.class, StackTraceElement[].class);
+ }
+//
+// private ClassUtils(){}
+//
+// /**
+// * Register the given common classes with the ClassUtils cache.
+// */
+// private static void registerCommonClasses(Class<?>... commonClasses) {
+// for (Class<?> clazz : commonClasses) {
+// commonClassCache.put(clazz.getName(), clazz);
+// }
+// }
+//
+ /**
+ * Return the default ClassLoader to use: typically the thread context
+ * ClassLoader, if available; the ClassLoader that loaded the ClassUtils
+ * class will be used as fallback.
+ * <p>Call this method if you intend to use the thread context ClassLoader
+ * in a scenario where you clearly prefer a non-null ClassLoader reference:
+ * for example, for class path resource loading (but not necessarily for
+ * {@code Class.forName}, which accepts a {@code null} ClassLoader
+ * reference as well).
+ * @return the default ClassLoader (only {@code null} if even the system
+ * ClassLoader isn't accessible)
+ * @see Thread#getContextClassLoader()
+ * @see ClassLoader#getSystemClassLoader()
+ */
+ public static ClassLoader getDefaultClassLoader() {
+ ClassLoader cl = null;
+ try {
+ cl = Thread.currentThread().getContextClassLoader();
+ }
+ catch (Throwable ex) {
+ // Cannot access thread context ClassLoader - falling back...
+ }
+ if (cl == null) {
+ // No thread context class loader -> use class loader of this class.
+ cl = ClassUtils.class.getClassLoader();
+ if (cl == null) {
+ // getClassLoader() returning null indicates the bootstrap ClassLoader
+ try {
+ cl = ClassLoader.getSystemClassLoader();
+ }
+ catch (Throwable ex) {
+ // Cannot access system ClassLoader - oh well, maybe the caller can live with null...
+ }
+ }
+ }
+ return cl;
+ }
+
+// /**
+// * Override the thread context ClassLoader with the environment's bean ClassLoader
+// * if necessary, i.e. if the bean ClassLoader is not equivalent to the thread
+// * context ClassLoader already.
+// * @param classLoaderToUse the actual ClassLoader to use for the thread context
+// * @return the original thread context ClassLoader, or {@code null} if not overridden
+// */
+// public static ClassLoader overrideThreadContextClassLoader(ClassLoader classLoaderToUse) {
+// Thread currentThread = Thread.currentThread();
+// ClassLoader threadContextClassLoader = currentThread.getContextClassLoader();
+// if (classLoaderToUse != null && !classLoaderToUse.equals(threadContextClassLoader)) {
+// currentThread.setContextClassLoader(classLoaderToUse);
+// return threadContextClassLoader;
+// }
+// else {
+// return null;
+// }
+// }
+
+ /**
+ * Replacement for {@code Class.forName()} that also returns Class instances
+ * for primitives (e.g. "int") and array class names (e.g. "String[]").
+ * Furthermore, it is also capable of resolving inner class names in Java source
+ * style (e.g. "java.lang.Thread.State" instead of "java.lang.Thread$State").
+ * @param name the name of the Class
+ * @param classLoader the class loader to use
+ * (may be {@code null}, which indicates the default class loader)
+ * @return Class instance for the supplied name
+ * @throws ClassNotFoundException if the class was not found
+ * @throws LinkageError if the class file could not be loaded
+ * @see Class#forName(String, boolean, ClassLoader)
+ */
+ public static Class<?> forName(String name, ClassLoader classLoader) throws ClassNotFoundException, LinkageError {
+ Objects.requireNonNull(name, "Name must not be null");
+
+ Class<?> clazz = resolvePrimitiveClassName(name);
+// if (clazz == null) {
+// clazz = commonClassCache.get(name);
+// }
+ if (clazz != null) {
+ return clazz;
+ }
+
+ // "java.lang.String[]" style arrays
+ if (name.endsWith(ARRAY_SUFFIX)) {
+ String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length());
+ Class<?> elementClass = forName(elementClassName, classLoader);
+ return Array.newInstance(elementClass, 0).getClass();
+ }
+
+ // "[Ljava.lang.String;" style arrays
+ if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(";")) {
+ String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1);
+ Class<?> elementClass = forName(elementName, classLoader);
+ return Array.newInstance(elementClass, 0).getClass();
+ }
+
+ // "[[I" or "[[Ljava.lang.String;" style arrays
+ if (name.startsWith(INTERNAL_ARRAY_PREFIX)) {
+ String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length());
+ Class<?> elementClass = forName(elementName, classLoader);
+ return Array.newInstance(elementClass, 0).getClass();
+ }
+
+ ClassLoader clToUse = classLoader;
+ if (clToUse == null) {
+ clToUse = getDefaultClassLoader();
+ }
+ try {
+ return (clToUse != null ? clToUse.loadClass(name) : Class.forName(name));
+ }
+ catch (ClassNotFoundException ex) {
+ int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR);
+ if (lastDotIndex != -1) {
+ String innerClassName =
+ name.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR + name.substring(lastDotIndex + 1);
+ try {
+ return (clToUse != null ? clToUse.loadClass(innerClassName) : Class.forName(innerClassName));
+ }
+ catch (ClassNotFoundException ex2) {
+ // Swallow - let original exception get through
+ }
+ }
+ throw ex;
+ }
+ }
+
+// /**
+// * Resolve the given class name into a Class instance. Supports
+// * primitives (like "int") and array class names (like "String[]").
+// * <p>This is effectively equivalent to the {@code forName}
+// * method with the same arguments, with the only difference being
+// * the exceptions thrown in case of class loading failure.
+// * @param className the name of the Class
+// * @param classLoader the class loader to use
+// * (may be {@code null}, which indicates the default class loader)
+// * @return Class instance for the supplied name
+// * @throws IllegalArgumentException if the class name was not resolvable
+// * (that is, the class could not be found or the class file could not be loaded)
+// * @see #forName(String, ClassLoader)
+// */
+// public static Class<?> resolveClassName(String className, ClassLoader classLoader) throws IllegalArgumentException {
+// try {
+// return forName(className, classLoader);
+// }
+// catch (ClassNotFoundException ex) {
+// throw new IllegalArgumentException("Cannot find class [" + className + "]", ex);
+// }
+// catch (LinkageError ex) {
+// throw new IllegalArgumentException(
+// "Error loading class [" + className + "]: problem with class file or dependent class.", ex);
+// }
+// }
+
+ /**
+ * Resolve the given class name as primitive class, if appropriate,
+ * according to the JVM's naming rules for primitive classes.
+ * <p>Also supports the JVM's internal class names for primitive arrays.
+ * Does <i>not</i> support the "[]" suffix notation for primitive arrays;
+ * this is only supported by {@link #forName(String, ClassLoader)}.
+ * @param name the name of the potentially primitive class
+ * @return the primitive class, or {@code null} if the name does not denote
+ * a primitive class or primitive array class
+ */
+ public static Class<?> resolvePrimitiveClassName(String name) {
+ Class<?> result = null;
+ // Most class names will be quite long, considering that they
+ // SHOULD sit in a package, so a length check is worthwhile.
+ if (name != null && name.length() <= 8) {
+ // Could be a primitive - likely.
+ result = primitiveTypeNameMap.get(name);
+ }
+ return result;
+ }
+
+// /**
+// * Determine whether the {@link Class} identified by the supplied name is present
+// * and can be loaded. Will return {@code false} if either the class or
+// * one of its dependencies is not present or cannot be loaded.
+// * @param className the name of the class to check
+// * @param classLoader the class loader to use
+// * (may be {@code null}, which indicates the default class loader)
+// * @return whether the specified class is present
+// */
+// public static boolean isPresent(String className, ClassLoader classLoader) {
+// try {
+// forName(className, classLoader);
+// return true;
+// }
+// catch (Throwable ex) {
+// // Class or one of its dependencies is not present...
+// return false;
+// }
+// }
+//
+// /**
+// * Return the user-defined class for the given instance: usually simply
+// * the class of the given instance, but the original class in case of a
+// * CGLIB-generated subclass.
+// * @param instance the instance to check
+// * @return the user-defined class
+// */
+// public static Class<?> getUserClass(Object instance) {
+// Objects.requireNonNull(instance, "Instance must not be null");
+// return getUserClass(instance.getClass());
+// }
+//
+// /**
+// * Return the user-defined class for the given class: usually simply the given
+// * class, but the original class in case of a CGLIB-generated subclass.
+// * @param clazz the class to check
+// * @return the user-defined class
+// */
+// public static Class<?> getUserClass(Class<?> clazz) {
+// if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
+// Class<?> superClass = clazz.getSuperclass();
+// if (superClass != null && !Object.class.equals(superClass)) {
+// return superClass;
+// }
+// }
+// return clazz;
+// }
+//
+// /**
+// * Check whether the given class is cache-safe in the given context,
+// * i.e. whether it is loaded by the given ClassLoader or a parent of it.
+// * @param clazz the class to analyze
+// * @param classLoader the ClassLoader to potentially cache metadata in
+// */
+// public static boolean isCacheSafe(Class<?> clazz, ClassLoader classLoader) {
+// Objects.requireNonNull(clazz, "Class must not be null");
+// try {
+// ClassLoader target = clazz.getClassLoader();
+// if (target == null) {
+// return true;
+// }
+// ClassLoader cur = classLoader;
+// if (cur == target) {
+// return true;
+// }
+// while (cur != null) {
+// cur = cur.getParent();
+// if (cur == target) {
+// return true;
+// }
+// }
+// return false;
+// }
+// catch (SecurityException ex) {
+// // Probably from the system ClassLoader - let's consider it safe.
+// return true;
+// }
+// }
+//
+//
+// /**
+// * Get the class name without the qualified package name.
+// * @param className the className to get the short name for
+// * @return the class name of the class without the package name
+// * @throws IllegalArgumentException if the className is empty
+// */
+// public static String getShortName(String className) {
+// Objects.requireNonNull(className, "Class name must not be empty");
+// int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR);
+// int nameEndIndex = className.indexOf(CGLIB_CLASS_SEPARATOR);
+// if (nameEndIndex == -1) {
+// nameEndIndex = className.length();
+// }
+// String shortName = className.substring(lastDotIndex + 1, nameEndIndex);
+// shortName = shortName.replace(INNER_CLASS_SEPARATOR, PACKAGE_SEPARATOR);
+// return shortName;
+// }
+//
+// /**
+// * Get the class name without the qualified package name.
+// * @param clazz the class to get the short name for
+// * @return the class name of the class without the package name
+// */
+// public static String getShortName(Class<?> clazz) {
+// return getShortName(getQualifiedName(clazz));
+// }
+//
+// /**
+// * Return the short string name of a Java class in uncapitalized JavaBeans
+// * property format. Strips the outer class name in case of an inner class.
+// * @param clazz the class
+// * @return the short name rendered in a standard JavaBeans property format
+// * @see java.beans.Introspector#decapitalize(String)
+// */
+// public static String getShortNameAsProperty(Class<?> clazz) {
+// String shortName = ClassUtils.getShortName(clazz);
+// int dotIndex = shortName.lastIndexOf(PACKAGE_SEPARATOR);
+// shortName = (dotIndex != -1 ? shortName.substring(dotIndex + 1) : shortName);
+// return Introspector.decapitalize(shortName);
+// }
+//
+// /**
+// * Determine the name of the class file, relative to the containing
+// * package: e.g. "String.class"
+// * @param clazz the class
+// * @return the file name of the ".class" file
+// */
+// public static String getClassFileName(Class<?> clazz) {
+// Objects.requireNonNull(clazz, "Class must not be null");
+// String className = clazz.getName();
+// int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR);
+// return className.substring(lastDotIndex + 1) + CLASS_FILE_SUFFIX;
+// }
+//
+// /**
+// * Determine the name of the package of the given class,
+// * e.g. "java.lang" for the {@code java.lang.String} class.
+// * @param clazz the class
+// * @return the package name, or the empty String if the class
+// * is defined in the default package
+// */
+// public static String getPackageName(Class<?> clazz) {
+// Objects.requireNonNull(clazz, "Class must not be null");
+// return getPackageName(clazz.getName());
+// }
+//
+// /**
+// * Determine the name of the package of the given fully-qualified class name,
+// * e.g. "java.lang" for the {@code java.lang.String} class name.
+// * @param fqClassName the fully-qualified class name
+// * @return the package name, or the empty String if the class
+// * is defined in the dObjects.requireNonNullefault package
+// */
+// public static String getPackageName(String fqClassName) {
+// Objects.requireNonNull(fqClassName, "Class name must not be null");
+// int lastDotIndex = fqClassName.lastIndexOf(PACKAGE_SEPARATOR);
+// return (lastDotIndex != -1 ? fqClassName.substring(0, lastDotIndex) : "");
+// }
+//
+// /**
+// * Return the qualified name of the given class: usually simply
+// * the class name, but component type class name + "[]" for arrays.
+// * @param clazz the class
+// * @return the qualified name of the class
+// */
+// public static String getQualifiedName(Class<?> clazz) {
+// Objects.requireNonNull(clazz, "Class must not be null");
+// if (clazz.isArray()) {
+// return getQualifiedNameForArray(clazz);
+// }
+// else {
+// return clazz.getName();
+// }
+// }
+//
+// /**
+// * Build a nice qualified name for an array:
+// * component type class name + "[]".
+// * @param clazz the array class
+// * @return a qualified name for the array class
+// */
+// private static String getQualifiedNameForArray(Class<?> clazz) {
+// StringBuilder result = new StringBuilder();
+// while (clazz.isArray()) {
+// clazz = clazz.getComponentType();
+// result.append(ClassUtils.ARRAY_SUFFIX);
+// }
+// result.insert(0, clazz.getName());
+// return result.toString();
+// }
+//
+// /**
+// * Return the qualified name of the given method, consisting of
+// * fully qualified interface/class name + "." + method name.
+// * @param method the method
+// * @return the qualified name of the method
+// */
+// public static String getQualifiedMethodName(Method method) {
+// Objects.requireNonNull(method, "Method must not be null");
+// return method.getDeclaringClass().getName() + "." + method.getName();
+// }
+//
+// /**
+// * Return a descriptive name for the given object's type: usually simply
+// * the class name, but component type class name + "[]" for arrays,
+// * and an appended list of implemented interfaces for JDK proxies.
+// * @param value the value to introspect
+// * @return the qualified name of the class
+// */
+// public static String getDescriptiveType(Object value) {
+// if (value == null) {
+// return null;
+// }
+// Class<?> clazz = value.getClass();
+// if (Proxy.isProxyClass(clazz)) {
+// StringBuilder result = new StringBuilder(clazz.getName());
+// result.append(" implementing ");
+// Class<?>[] ifcs = clazz.getInterfaces();
+// for (int i = 0; i < ifcs.length; i++) {
+// result.append(ifcs[i].getName());
+// if (i < ifcs.length - 1) {
+// result.append(',');
+// }
+// }
+// return result.toString();
+// }
+// else if (clazz.isArray()) {
+// return getQualifiedNameForArray(clazz);
+// }
+// else {
+// return clazz.getName();
+// }
+// }
+//
+// /**
+// * Check whether the given class matches the user-specified type name.
+// * @param clazz the class to check
+// * @param typeName the type name to match
+// */
+// public static boolean matchesTypeName(Class<?> clazz, String typeName) {
+// return (typeName != null &&
+// (typeName.equals(clazz.getName()) || typeName.equals(clazz.getSimpleName()) ||
+// (clazz.isArray() && typeName.equals(getQualifiedNameForArray(clazz)))));
+// }
+//
+//
+// /**
+// * Determine whether the given class has a public constructor with the given signature.
+// * <p>Essentially translates {@code NoSuchMethodException} to "false".
+// * @param clazz the clazz to analyze
+// * @param paramTypes the parameter types of the method
+// * @return whether the class has a corresponding constructor
+// * @see Class#getMethod
+// */
+// public static boolean hasConstructor(Class<?> clazz, Class<?>... paramTypes) {
+// return (getConstructorIfAvailable(clazz, paramTypes) != null);
+// }
+//
+// /**
+// * Determine whether the given class has a public constructor with the given signature,
+// * and return it if available (else return {@code null}).
+// * <p>Essentially translates {@code NoSuchMethodException} to {@code null}.
+// * @param clazz the clazz to analyze
+// * @param paramTypes the parameter types of the method
+// * @return the constructor, or {@code null} if not found
+// * @see Class#getConstructor
+// */
+// public static <T> Constructor<T> getConstructorIfAvailable(Class<T> clazz, Class<?>... paramTypes) {
+// Objects.requireNonNull(clazz, "Class must not be null");
+// try {
+// return clazz.getConstructor(paramTypes);
+// }
+// catch (NoSuchMethodException ex) {
+// return null;
+// }
+// }
+//
+// /**
+// * Determine whether the given class has a public method with the given signature.
+// * <p>Essentially translates {@code NoSuchMethodException} to "false".
+// * @param clazz the clazz to analyze
+// * @param methodName the name of the method
+// * @param paramTypes the parameter types of the method
+// * @return whether the class has a corresponding method
+// * @see Class#getMethod
+// */
+// public static boolean hasMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) {
+// return (getMethodIfAvailable(clazz, methodName, paramTypes) != null);
+// }
+//
+// /**
+// * Determine whether the given class has a public method with the given signature,
+// * and return it if available (else throws an {@code IllegalStateException}).
+// * <p>In case of any signature specified, only returns the method if there is a
+// * unique candidate, i.e. a single public method with the specified name.
+// * <p>Essentially translates {@code NoSuchMethodException} to {@code IllegalStateException}.
+// * @param clazz the clazz to analyze
+// * @param methodName the name of the method
+// * @param paramTypes the parameter types of the method
+// * (may be {@code null} to indicate any signature)
+// * @return the method (never {@code null})
+// * @throws IllegalStateException if the method has not been found
+// * @see Class#getMethod
+// */
+// public static Method getMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) {
+// Objects.requireNonNull(clazz, "Class must not be null");
+// Objects.requireNonNull(methodName, "Method name must not be null");
+// if (paramTypes != null) {
+// try {
+// return clazz.getMethod(methodName, paramTypes);
+// }
+// catch (NoSuchMethodException ex) {
+// throw new IllegalStateException("Expected method not found: " + ex);
+// }
+// }
+// else {
+// Set<Method> candidates = new HashSet<Method>(1);
+// Method[] methods = clazz.getMethods();
+// for (Method method : methods) {
+// if (methodName.equals(method.getName())) {
+// candidates.add(method);
+// }
+// }
+// if (candidates.size() == 1) {
+// return candidates.iterator().next();
+// }
+// else if (candidates.isEmpty()) {
+// throw new IllegalStateException("Expected method not found: " + clazz + "." + methodName);
+// }
+// else {
+// throw new IllegalStateException("No unique method found: " + clazz + "." + methodName);
+// }
+// }
+// }
+//
+// /**
+// * Determine whether the given class has a public method with the given signature,
+// * and return it if available (else return {@code null}).
+// * <p>In case of any signature specified, only returns the method if there is a
+// * unique candidate, i.e. a single public method with the specified name.
+// * <p>Essentially translates {@code NoSuchMethodException} to {@code null}.
+// * @param clazz the clazz to analyze
+// * @param methodName the name of the method
+// * @param paramTypes the parameter types of the method
+// * (may be {@code null} to indicate any signature)
+// * @return the method, or {@code null} if not found
+// * @see Class#getMethod
+// */
+// public static Method getMethodIfAvailable(Class<?> clazz, String methodName, Class<?>... paramTypes) {
+// Objects.requireNonNull(clazz, "Class must not be null");
+// Objects.requireNonNull(methodName, "Method name must not be null");
+// if (paramTypes != null) {
+// try {
+// return clazz.getMethod(methodName, paramTypes);
+// }
+// catch (NoSuchMethodException ex) {
+// return null;
+// }
+// }
+// else {
+// Set<Method> candidates = new HashSet<Method>(1);
+// Method[] methods = clazz.getMethods();
+// for (Method method : methods) {
+// if (methodName.equals(method.getName())) {
+// candidates.add(method);
+// }
+// }
+// if (candidates.size() == 1) {
+// return candidates.iterator().next();
+// }
+// return null;
+// }
+// }
+//
+// /**
+// * Return the number of methods with a given name (with any argument types),
+// * for the given class and/or its superclasses. Includes non-public methods.
+// * @param clazz the clazz to check
+// * @param methodName the name of the method
+// * @return the number of methods with the given name
+// */
+// public static int getMethodCountForName(Class<?> clazz, String methodName) {
+// Objects.requireNonNull(clazz, "Class must not be null");
+// Objects.requireNonNull(methodName, "Method name must not be null");
+// int count = 0;
+// Method[] declaredMethods = clazz.getDeclaredMethods();
+// for (Method method : declaredMethods) {
+// if (methodName.equals(method.getName())) {
+// count++;
+// }
+// }
+// Class<?>[] ifcs = clazz.getInterfaces();
+// for (Class<?> ifc : ifcs) {
+// count += getMethodCountForName(ifc, methodName);
+// }
+// if (clazz.getSuperclass() != null) {
+// count += getMethodCountForName(clazz.getSuperclass(), methodName);
+// }
+// return count;
+// }
+//
+// /**
+// * Does the given class or one of its superclasses at least have one or more
+// * methods with the supplied name (with any argument types)?
+// * Includes non-public methods.
+// * @param clazz the clazz to check
+// * @param methodName the name of the method
+// * @return whether there is at least one method with the given name
+// */
+// public static boolean hasAtLeastOneMethodWithName(Class<?> clazz, String methodName) {
+// Objects.requireNonNull(clazz, "Class must not be null");
+// Objects.requireNonNull(methodName, "Method name must not be null");
+// Method[] declaredMethods = clazz.getDeclaredMethods();
+// for (Method method : declaredMethods) {
+// if (method.getName().equals(methodName)) {
+// return true;
+// }
+// }
+// Class<?>[] ifcs = clazz.getInterfaces();
+// for (Class<?> ifc : ifcs) {
+// if (hasAtLeastOneMethodWithName(ifc, methodName)) {
+// return true;
+// }
+// }
+// return (clazz.getSuperclass() != null && hasAtLeastOneMethodWithName(clazz.getSuperclass(), methodName));
+// }
+//
+// /**
+// * Given a method, which may come from an interface, and a target class used
+// * in the current reflective invocation, find the corresponding target method
+// * if there is one. E.g. the method may be {@code IFoo.bar()} and the
+// * target class may be {@code DefaultFoo}. In this case, the method may be
+// * {@code DefaultFoo.bar()}. This enables attributes on that method to be found.
+// * <p><b>NOTE:</b> In contrast to {@code org.springframework.aop.support.AopUtils#getMostSpecificMethod},
+// * this method does <i>not</i> resolve Java 5 bridge methods automatically.
+// * Call {@code org.springframework.core.BridgeMethodResolver#findBridgedMethod}
+// * if bridge method resolution is desirable (e.g. for obtaining metadata from
+// * the original method definition).
+// * <p><b>NOTE:</b> Since Spring 3.1.1, if Java security settings disallow reflective
+// * access (e.g. calls to {@code Class#getDeclaredMethods} etc, this implementation
+// * will fall back to returning the originally provided method.
+// * @param method the method to be invoked, which may come from an interface
+// * @param targetClass the target class for the current invocation.
+// * May be {@code null} or may not even implement the method.
+// * @return the specific target method, or the original method if the
+// * {@code targetClass} doesn't implement it or is {@code null}
+// */
+// public static Method getMostSpecificMethod(Method method, Class<?> targetClass) {
+// if (method != null && isOverridable(method, targetClass) &&
+// targetClass != null && !targetClass.equals(method.getDeclaringClass())) {
+// try {
+// if (Modifier.isPublic(method.getModifiers())) {
+// try {
+// return targetClass.getMethod(method.getName(), method.getParameterTypes());
+// }
+// catch (NoSuchMethodException ex) {
+// return method;
+// }
+// }
+// else {
+// Method specificMethod =
+// ReflectionUtils.findMethod(targetClass, method.getName(), method.getParameterTypes());
+// return (specificMethod != null ? specificMethod : method);
+// }
+// }
+// catch (SecurityException ex) {
+// // Security settings are disallowing reflective access; fall back to 'method' below.
+// }
+// }
+// return method;
+// }
+//
+// /**
+// * Determine whether the given method is declared by the user or at least pointing to
+// * a user-declared method.
+// * <p>Checks {@link Method#isSynthetic()} (for implementation methods) as well as the
+// * {@code GroovyObject} interface (for interface methods; on an implementation class,
+// * implementations of the {@code GroovyObject} methods will be marked as synthetic anyway).
+// * Note that, despite being synthetic, bridge methods ({@link Method#isBridge()}) are considered
+// * as user-level methods since they are eventually pointing to a user-declared generic method.
+// * @param method the method to check
+// * @return {@code true} if the method can be considered as user-declared; [@code false} otherwise
+// */
+// public static boolean isUserLevelMethod(Method method) {
+// Objects.requireNonNull(method, "Method must not be null");
+// return (method.isBridge() || (!method.isSynthetic() && !isGroovyObjectMethod(method)));
+// }
+//
+// private static boolean isGroovyObjectMethod(Method method) {
+// return method.getDeclaringClass().getName().equals("groovy.lang.GroovyObject");
+// }
+//
+// /**
+// * Determine whether the given method is overridable in the given target class.
+// * @param method the method to check
+// * @param targetClass the target class to check against
+// */
+// private static boolean isOverridable(Method method, Class<?> targetClass) {
+// if (Modifier.isPrivate(method.getModifiers())) {
+// return false;
+// }
+// if (Modifier.isPublic(method.getModifiers()) || Modifier.isProtected(method.getModifiers())) {
+// return true;
+// }
+// return getPackageName(method.getDeclaringClass()).equals(getPackageName(targetClass));
+// }
+//
+// /**
+// * Return a public static method of a class.
+// * @param methodName the static method name
+// * @param clazz the class which defines the method
+// * @param args the parameter types to the method
+// * @return the static method, or {@code null} if no static method was found
+// * @throws IllegalArgumentException if the method name is blank or the clazz is null
+// */
+// public static Method getStaticMethod(Class<?> clazz, String methodName, Class<?>... args) {
+// Objects.requireNonNull(clazz, "Class must not be null");
+// Objects.requireNonNull(methodName, "Method name must not be null");
+// try {
+// Method method = clazz.getMethod(methodName, args);
+// return Modifier.isStatic(method.getModifiers()) ? method : null;
+// }
+// catch (NoSuchMethodException ex) {
+// return null;
+// }
+// }
+//
+//
+// /**
+// * Check if the given class represents a primitive wrapper,
+// * i.e. Boolean, Byte, Character, Short, Integer, Long, Float, or Double.
+// * @param clazz the class to check
+// * @return whether the given class is a primitive wrapper class
+// */
+// public static boolean isPrimitiveWrapper(Class<?> clazz) {
+// Objects.requireNonNull(clazz, "Class must not be null");
+// return primitiveWrapperTypeMap.containsKey(clazz);
+// }
+//
+// /**
+// * Check if the given class represents a primitive (i.e. boolean, byte,
+// * char, short, int, long, float, or double) or a primitive wrapper
+// * (i.e. Boolean, Byte, Character, Short, Integer, Long, Float, or Double).
+// * @param clazz the class to check
+// * @return whether the given class is a primitive or primitive wrapper class
+// */
+// public static boolean isPrimitiveOrWrapper(Class<?> clazz) {
+// Objects.requireNonNull(clazz, "Class must not be null");
+// return (clazz.isPrimitive() || isPrimitiveWrapper(clazz));
+// }
+//
+// /**
+// * Check if the given class represents an array of primitives,
+// * i.e. boolean, byte, char, short, int, long, float, or double.
+// * @param clazz the class to check
+// * @return whether the given class is a primitive array class
+// */
+// public static boolean isPrimitiveArray(Class<?> clazz) {
+// Objects.requireNonNull(clazz, "Class must not be null");
+// return (clazz.isArray() && clazz.getComponentType().isPrimitive());
+// }
+//
+// /**
+// * Check if the given class represents an array of primitive wrappers,
+// * i.e. Boolean, Byte, Character, Short, Integer, Long, Float, or Double.
+// * @param clazz the class to check
+// * @return whether the given class is a primitive wrapper array class
+// */
+// public static boolean isPrimitiveWrapperArray(Class<?> clazz) {
+// Objects.requireNonNull(clazz, "Class must not be null");
+// return (clazz.isArray() && isPrimitiveWrapper(clazz.getComponentType()));
+// }
+//
+// /**
+// * Resolve the given class if it is a primitive class,
+// * returning the corresponding primitive wrapper type instead.
+// * @param clazz the class to check
+// * @return the original class, or a primitive wrapper for the original primitive type
+// */
+// public static Class<?> resolvePrimitiveIfNecessary(Class<?> clazz) {
+// Objects.requireNonNull(clazz, "Class must not be null");
+// return (clazz.isPrimitive() && clazz != void.class? primitiveTypeToWrapperMap.get(clazz) : clazz);
+// }
+//
+// /**
+// * Check if the right-hand side type may be assigned to the left-hand side
+// * type, assuming setting by reflection. Considers primitive wrapper
+// * classes as assignable to the corresponding primitive types.
+// * @param lhsType the target type
+// * @param rhsType the value type that should be assigned to the target type
+// * @return if the target type is assignable from the value type
+// */
+// public static boolean isAssignable(Class<?> lhsType, Class<?> rhsType) {
+// Objects.requireNonNull(lhsType, "Left-hand side type must not be null");
+// Objects.requireNonNull(rhsType, "Right-hand side type must not be null");
+// if (lhsType.isAssignableFrom(rhsType)) {
+// return true;
+// }
+// if (lhsType.isPrimitive()) {
+// Class<?> resolvedPrimitive = primitiveWrapperTypeMap.get(rhsType);
+// if (resolvedPrimitive != null && lhsType.equals(resolvedPrimitive)) {
+// return true;
+// }
+// }
+// else {
+// Class<?> resolvedWrapper = primitiveTypeToWrapperMap.get(rhsType);
+// if (resolvedWrapper != null && lhsType.isAssignableFrom(resolvedWrapper)) {
+// return true;
+// }
+// }
+// return false;
+// }
+//
+// /**
+// * Determine if the given type is assignable from the given value,
+// * assuming setting by reflection. Considers primitive wrapper classes
+// * as assignable to the corresponding primitive types.
+// * @param type the target type
+// * @param value the value that should be assigned to the type
+// * @return if the type is assignable from the value
+// */
+// public static boolean isAssignableValue(Class<?> type, Object value) {
+// Objects.requireNonNull(type, "Type must not be null");
+// return (value != null ? isAssignable(type, value.getClass()) : !type.isPrimitive());
+// }
+//
+//
+// /**
+// * Convert a "/"-based resource path to a "."-based fully qualified class name.
+// * @param resourcePath the resource path pointing to a class
+// * @return the corresponding fully qualified class name
+// */
+// public static String convertResourcePathToClassName(String resourcePath) {
+// Objects.requireNonNull(resourcePath, "Resource path must not be null");
+// return resourcePath.replace(PATH_SEPARATOR, PACKAGE_SEPARATOR);
+// }
+//
+// /**
+// * Convert a "."-based fully qualified class name to a "/"-based resource path.
+// * @param className the fully qualified class name
+// * @return the corresponding resource path, pointing to the class
+// */
+// public static String convertClassNameToResourcePath(String className) {
+// Objects.requireNonNull(className, "Class name must not be null");
+// return className.replace(PACKAGE_SEPARATOR, PATH_SEPARATOR);
+// }
+//
+// /**
+// * Return a path suitable for use with {@code ClassLoader.getResource}
+// * (also suitable for use with {@code Class.getResource} by prepending a
+// * slash ('/') to the return value). Built by taking the package of the specified
+// * class file, converting all dots ('.') to slashes ('/'), adding a trailing slash
+// * if necessary, and concatenating the specified resource name to this.
+// * <br/>As such, this function may be used to build a path suitable for
+// * loading a resource file that is in the same package as a class file,
+// * although {@code org.springframework.core.io.ClassPathResource} is usually
+// * even more convenient.
+// * @param clazz the Class whose package will be used as the base
+// * @param resourceName the resource name to append. A leading slash is optional.
+// * @return the built-up resource path
+// * @see ClassLoader#getResource
+// * @see Class#getResource
+// */
+// public static String addResourcePathToPackagePath(Class<?> clazz, String resourceName) {
+// Objects.requireNonNull(resourceName, "Resource name must not be null");
+// if (!resourceName.startsWith("/")) {
+// return classPackageAsResourcePath(clazz) + "/" + resourceName;
+// }
+// return classPackageAsResourcePath(clazz) + resourceName;
+// }
+
+ /**
+ * Given an input class object, return a string which consists of the
+ * class's package name as a pathname, i.e., all dots ('.') are replaced by
+ * slashes ('/'). Neither a leading nor trailing slash is added. The result
+ * could be concatenated with a slash and the name of a resource and fed
+ * directly to {@code ClassLoader.getResource()}. For it to be fed to
+ * {@code Class.getResource} instead, a leading slash would also have
+ * to be prepended to the returned value.
+ * @param clazz the input class. A {@code null} value or the default
+ * (empty) package will result in an empty string ("") being returned.
+ * @return a path which represents the package name
+ * @see ClassLoader#getResource
+ * @see Class#getResource
+ */
+ public static String classPackageAsResourcePath(Class<?> clazz) {
+ if (clazz == null) {
+ return "";
+ }
+ String className = clazz.getName();
+ int packageEndIndex = className.lastIndexOf(PACKAGE_SEPARATOR);
+ if (packageEndIndex == -1) {
+ return "";
+ }
+ String packageName = className.substring(0, packageEndIndex);
+ return packageName.replace(PACKAGE_SEPARATOR, PATH_SEPARATOR);
+ }
+
+// /**
+// * Build a String that consists of the names of the classes/interfaces
+// * in the given array.
+// * <p>Basically like {@code AbstractCollection.toString()}, but stripping
+// * the "class "/"interface " prefix before every class name.
+// * @param classes a Collection of Class objects (may be {@code null})
+// * @return a String of form "[com.foo.Bar, com.foo.Baz]"
+// * @see java.util.AbstractCollection#toString()
+// */
+// public static String classNamesToString(Class<?>... classes) {
+// return classNamesToString(Arrays.asList(classes));
+// }
+//
+// /**
+// * Build a String that consists of the names of the classes/interfaces
+// * in the given collection.
+// * <p>Basically like {@code AbstractCollection.toString()}, but stripping
+// * the "class "/"interface " prefix before every class name.
+// * @param classes a Collection of Class objects (may be {@code null})
+// * @return a String of form "[com.foo.Bar, com.foo.Baz]"
+// * @see java.util.AbstractCollection#toString()
+// */
+// public static String classNamesToString(Collection<Class<?>> classes) {
+// if (classes.isEmpty()) {
+// return "[]";
+// }
+// StringBuilder sb = new StringBuilder("[");
+// for (Iterator<Class<?>> it = classes.iterator(); it.hasNext(); ) {
+// Class<?> clazz = it.next();
+// sb.append(clazz.getName());
+// if (it.hasNext()) {
+// sb.append(", ");
+// }
+// }
+// sb.append("]");
+// return sb.toString();
+// }
+//
+// /**
+// * Copy the given Collection into a Class array.
+// * The Collection must contain Class elements only.
+// * @param collection the Collection to copy
+// * @return the Class array ({@code null} if the passed-in
+// * Collection was {@code null})
+// */
+// public static Class<?>[] toClassArray(Collection<Class<?>> collection) {
+// if (collection == null) {
+// return null;
+// }
+// return collection.toArray(new Class<?>[collection.size()]);
+// }
+//
+// /**
+// * Return all interfaces that the given instance implements as array,
+// * including ones implemented by superclasses.
+// * @param instance the instance to analyze for interfaces
+// * @return all interfaces that the given instance implements as array
+// */
+// public static Class<?>[] getAllInterfaces(Object instance) {
+// Objects.requireNonNull(instance, "Instance must not be null");
+// return getAllInterfacesForClass(instance.getClass());
+// }
+//
+// /**
+// * Return all interfaces that the given class implements as array,
+// * including ones implemented by superclasses.
+// * <p>If the class itself is an interface, it gets returned as sole interface.
+// * @param clazz the class to analyze for interfaces
+// * @return all interfaces that the given object implements as array
+// */
+// public static Class<?>[] getAllInterfacesForClass(Class<?> clazz) {
+// return getAllInterfacesForClass(clazz, null);
+// }
+//
+// /**
+// * Return all interfaces that the given class implements as array,
+// * including ones implemented by superclasses.
+// * <p>If the class itself is an interface, it gets returned as sole interface.
+// * @param clazz the class to analyze for interfaces
+// * @param classLoader the ClassLoader that the interfaces need to be visible in
+// * (may be {@code null} when accepting all declared interfaces)
+// * @return all interfaces that the given object implements as array
+// */
+// public static Class<?>[] getAllInterfacesForClass(Class<?> clazz, ClassLoader classLoader) {
+// Set<Class<?>> ifcs = getAllInterfacesForClassAsSet(clazz, classLoader);
+// return ifcs.toArray(new Class<?>[ifcs.size()]);
+// }
+//
+// /**
+// * Return all interfaces that the given instance implements as Set,
+// * including ones implemented by superclasses.
+// * @param instance the instance to analyze for interfaces
+// * @return all interfaces that the given instance implements as Set
+// */
+// public static Set<Class<?>> getAllInterfacesAsSet(Object instance) {
+// Objects.requireNonNull(instance, "Instance must not be null");
+// return getAllInterfacesForClassAsSet(instance.getClass());
+// }
+//
+// /**
+// * Return all interfaces that the given class implements as Set,
+// * including ones implemented by superclasses.
+// * <p>If the class itself is an interface, it gets returned as sole interface.
+// * @param clazz the class to analyze for interfaces
+// * @return all interfaces that the given object implements as Set
+// */
+// public static Set<Class<?>> getAllInterfacesForClassAsSet(Class<?> clazz) {
+// return getAllInterfacesForClassAsSet(clazz, null);
+// }
+//
+// /**
+// * Return all interfaces that the given class implements as Set,
+// * including ones implemented by superclasses.
+// * <p>If the class itself is an interface, it gets returned as sole interface.
+// * @param clazz the class to analyze for interfaces
+// * @param classLoader the ClassLoader that the interfaces need to be visible in
+// * (may be {@code null} when accepting all declared interfaces)
+// * @return all interfaces that the given object implements as Set
+// */
+// public static Set<Class<?>> getAllInterfacesForClassAsSet(Class<?> clazz, ClassLoader classLoader) {
+// Objects.requireNonNull(clazz, "Class must not be null");
+// if (clazz.isInterface() && isVisible(clazz, classLoader)) {
+// return Collections.<Class<?>>singleton(clazz);
+// }
+// Set<Class<?>> interfaces = new LinkedHashSet<Class<?>>();
+// while (clazz != null) {
+// Class<?>[] ifcs = clazz.getInterfaces();
+// for (Class<?> ifc : ifcs) {
+// interfaces.addAll(getAllInterfacesForClassAsSet(ifc, classLoader));
+// }
+// clazz = clazz.getSuperclass();
+// }
+// return interfaces;
+// }
+//
+// /**
+// * Create a composite interface Class for the given interfaces,
+// * implementing the given interfaces in one single Class.
+// * <p>This implementation builds a JDK proxy class for the given interfaces.
+// * @param interfaces the interfaces to merge
+// * @param classLoader the ClassLoader to create the composite Class in
+// * @return the merged interface as Class
+// * @see java.lang.reflect.Proxy#getProxyClass
+// */
+// public static Class<?> createCompositeInterface(Class<?>[] interfaces, ClassLoader classLoader) {
+// if(interfaces.length==0) throw new IllegalArgumentException("Interfaces must not be empty");
+// Objects.requireNonNull(classLoader, "ClassLoader must not be null");
+// return Proxy.getProxyClass(classLoader, interfaces);
+// }
+//
+// /**
+// * Determine the common ancestor of the given classes, if any.
+// * @param clazz1 the class to introspect
+// * @param clazz2 the other class to introspect
+// * @return the common ancestor (i.e. common superclass, one interface
+// * extending the other), or {@code null} if none found. If any of the
+// * given classes is {@code null}, the other class will be returned.
+// * @since 3.2.6
+// */
+// public static Class<?> determineCommonAncestor(Class<?> clazz1, Class<?> clazz2) {
+// if (clazz1 == null) {
+// return clazz2;
+// }
+// if (clazz2 == null) {
+// return clazz1;
+// }
+// if (clazz1.isAssignableFrom(clazz2)) {
+// return clazz1;
+// }
+// if (clazz2.isAssignableFrom(clazz1)) {
+// return clazz2;
+// }
+// Class<?> ancestor = clazz1;
+// do {
+// ancestor = ancestor.getSuperclass();
+// if (ancestor == null || Object.class.equals(ancestor)) {
+// return null;
+// }
+// }
+// while (!ancestor.isAssignableFrom(clazz2));
+// return ancestor;
+// }
+//
+// /**
+// * Check whether the given class is visible in the given ClassLoader.
+// * @param clazz the class to check (typically an interface)
+// * @param classLoader the ClassLoader to check against (may be {@code null},
+// * in which case this method will always return {@code true})
+// */
+// public static boolean isVisible(Class<?> clazz, ClassLoader classLoader) {
+// if (classLoader == null) {
+// return true;
+// }
+// try {
+// Class<?> actualClass = classLoader.loadClass(clazz.getName());
+// return (clazz == actualClass);
+// // Else: different interface class found...
+// }
+// catch (ClassNotFoundException ex) {
+// // No interface class found...
+// return false;
+// }
+// }
+//
+// /**
+// * Check whether the given object is a CGLIB proxy.
+// * @param object the object to check
+// */
+// public static boolean isCglibProxy(Object object) {
+// return ClassUtils.isCglibProxyClass(object.getClass());
+// }
+//
+// /**
+// * Check whether the specified class is a CGLIB-generated class.
+// * @param clazz the class to check
+// */
+// public static boolean isCglibProxyClass(Class<?> clazz) {
+// return (clazz != null && isCglibProxyClassName(clazz.getName()));
+// }
+//
+// /**
+// * Check whether the specified class name is a CGLIB-generated class.
+// * @param className the class name to check
+// */
+// public static boolean isCglibProxyClassName(String className) {
+// return (className != null && className.contains(CGLIB_CLASS_SEPARATOR));
+// }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/resources/io/DefaultResourceLoader.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/resources/io/DefaultResourceLoader.java b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/DefaultResourceLoader.java
new file mode 100644
index 0000000..442a12d
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/DefaultResourceLoader.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tamaya.core.internal.resources.io;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Objects;
+
+/**
+ * <p>Will return a {@code UrlResource} if the location value is a URL,
+ * and a {@code ClassPathResource} if it is a non-URL path or a
+ * "classpath:" pseudo-URL.
+ *
+ * @author Juergen Hoeller
+ * @since 10.03.2004
+ */
+class DefaultResourceLoader {
+
+ /** Pseudo URL prefix for loading from the class path: "classpath:" */
+ public static final String CLASSPATH_URL_PREFIX = "classpath:";
+
+ private ClassLoader classLoader;
+
+
+ /**
+ * Create a new DefaultResourceLoader.
+ * <p>ClassLoader access will happen using the thread context class loader
+ * at the time of this ResourceLoader's initialization.
+ * @see java.lang.Thread#getContextClassLoader()
+ */
+ public DefaultResourceLoader() {
+ this.classLoader = ClassUtils.getDefaultClassLoader();
+ }
+
+ /**
+ * Create a new DefaultResourceLoader.
+ * @param classLoader the ClassLoader to load class path resources with, or {@code null}
+ * for using the thread context class loader at the time of actual resource access
+ */
+ public DefaultResourceLoader(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+
+ /**
+ * Specify the ClassLoader to load class path resources with, or {@code null}
+ * for using the thread context class loader at the time of actual resource access.
+ * <p>The default is that ClassLoader access will happen using the thread context
+ * class loader at the time of this ResourceLoader's initialization.
+ */
+ void setClassLoader(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+ /**
+ * Return the ClassLoader to load class path resources with.
+ * <p>Will get passed to ClassPathResource's constructor for all
+ * ClassPathResource objects created by this resource loader.
+ * @see ClassPathResource
+ */
+ public ClassLoader getClassLoader() {
+ return (this.classLoader != null ? this.classLoader : ClassUtils.getDefaultClassLoader());
+ }
+
+
+ public Resource getResource(String location) {
+ Objects.requireNonNull(location, "Location must not be null");
+ if (location.startsWith("/")) {
+ return getResourceByPath(location);
+ }
+ else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
+ return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
+ }
+ else {
+ try {
+ // Try to parse the location as a URL...
+ URL url = new URL(location);
+ return new UrlResource(url);
+ }
+ catch (MalformedURLException ex) {
+ // No URL -> resolve as resource path.
+ return getResourceByPath(location);
+ }
+ }
+ }
+
+ /**
+ * Return a Resource handle for the resource at the given path.
+ * <p>The default implementation supports class path locations. This should
+ * be appropriate for standalone implementations but can be overridden,
+ * e.g. for implementations targeted at a Servlet container.
+ * @param path the path to the resource
+ * @return the corresponding Resource handle
+ * @see ClassPathResource
+ */
+ protected Resource getResourceByPath(String path) {
+ return new ClassPathContextResource(path, getClassLoader());
+ }
+
+
+ /**
+ * ClassPathResource that explicitly expresses a context-relative path
+ * through implementing the ContextResource interface.
+ */
+ protected static class ClassPathContextResource extends ClassPathResource {
+
+ public ClassPathContextResource(String path, ClassLoader classLoader) {
+ super(path, classLoader);
+ }
+
+ public String getPathWithinContext() {
+ return getPath();
+ }
+
+ @Override
+ public Resource createRelative(String relativePath) {
+ String pathToUse = StringUtils.applyRelativePath(getPath(), relativePath);
+ return new ClassPathContextResource(pathToUse, getClassLoader());
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/resources/io/FileSystemResource.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/resources/io/FileSystemResource.java b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/FileSystemResource.java
new file mode 100644
index 0000000..93d6ec5
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/FileSystemResource.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tamaya.core.internal.resources.io;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URL;
+import java.util.Objects;
+
+/**
+ * {@link Resource} implementation for {@code java.io.File} handles.
+ * Obviously supports resolution as File, and also as URL.
+ *
+ * @author Juergen Hoeller
+ * @since 28.12.2003
+ * @see java.io.File
+ */
+public class FileSystemResource extends AbstractResource {
+
+ private final File file;
+
+ private final String path;
+
+
+ /**
+ * Create a new {@code FileSystemResource} from a {@link File} handle.
+ * <p>Note: When building relative resources via {@link #createRelative},
+ * the relative path will apply <i>at the same directory level</i>:
+ * e.g. new File("C:/dir1"), relative path "dir2" -> "C:/dir2"!
+ * If you prefer to have relative paths built underneath the given root
+ * directory, use the {@link #FileSystemResource(String) constructor with a file path}
+ * to append a trailing slash to the root path: "C:/dir1/", which
+ * indicates this directory as root for all relative paths.
+ * @param file a File handle
+ */
+ public FileSystemResource(File file) {
+ Objects.requireNonNull(file, "File must not be null");
+ this.file = file;
+ this.path = StringUtils.cleanPath(file.getPath());
+ }
+
+ /**
+ * Create a new {@code FileSystemResource} from a file path.
+ * <p>Note: When building relative resources via {@link #createRelative},
+ * it makes a difference whether the specified resource base path here
+ * ends with a slash or not. In the case of "C:/dir1/", relative paths
+ * will be built underneath that root: e.g. relative path "dir2" ->
+ * "C:/dir1/dir2". In the case of "C:/dir1", relative paths will apply
+ * at the same directory level: relative path "dir2" -> "C:/dir2".
+ * @param path a file path
+ */
+ public FileSystemResource(String path) {
+ Objects.requireNonNull(path, "Path must not be null");
+ this.file = new File(path);
+ this.path = StringUtils.cleanPath(path);
+ }
+
+
+ /**
+ * Return the file path for this resource.
+ */
+ public final String getPath() {
+ return this.path;
+ }
+
+
+ /**
+ * This implementation returns whether the underlying file exists.
+ * @see java.io.File#exists()
+ */
+ @Override
+ public boolean exists() {
+ return this.file.exists();
+ }
+
+ /**
+ * This implementation checks whether the underlying file is marked as readable
+ * (and corresponds to an actual file with content, not to a directory).
+ * @see java.io.File#canRead()
+ * @see java.io.File#isDirectory()
+ */
+ @Override
+ public boolean isReadable() {
+ return (this.file.canRead() && !this.file.isDirectory());
+ }
+
+ /**
+ * This implementation opens a FileInputStream for the underlying file.
+ * @see java.io.FileInputStream
+ */
+ @Override
+ public InputStream getInputStream() throws IOException {
+ return new FileInputStream(this.file);
+ }
+
+ /**
+ * This implementation returns a URL for the underlying file.
+ * @see java.io.File#toURI()
+ */
+ @Override
+ public URL getURL() throws IOException {
+ return this.file.toURI().toURL();
+ }
+
+ /**
+ * This implementation returns a URI for the underlying file.
+ * @see java.io.File#toURI()
+ */
+ @Override
+ public URI getURI() throws IOException {
+ return this.file.toURI();
+ }
+
+ /**
+ * This implementation returns the underlying File reference.
+ */
+ @Override
+ public File getFile() {
+ return this.file;
+ }
+
+ /**
+ * This implementation returns the underlying File's length.
+ */
+ @Override
+ public long contentLength() throws IOException {
+ return this.file.length();
+ }
+
+ /**
+ * This implementation creates a FileSystemResource, applying the given path
+ * relative to the path of the underlying file of this resource descriptor.
+ * @see StringUtils#applyRelativePath(String, String)
+ */
+ @Override
+ public Resource createRelative(String relativePath) {
+ String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
+ return new FileSystemResource(pathToUse);
+ }
+
+ /**
+ * This implementation returns the name of the file.
+ * @see java.io.File#getName()
+ */
+ @Override
+ public String getFilename() {
+ return this.file.getName();
+ }
+
+ /**
+ * This implementation returns a description that includes the absolute
+ * path of the file.
+ * @see java.io.File#getAbsolutePath()
+ */
+ @Override
+ public String getDescription() {
+ return "file [" + this.file.getAbsolutePath() + "]";
+ }
+
+
+ // implementation of WritableResource
+
+ /**
+ * This implementation checks whether the underlying file is marked as writable
+ * (and corresponds to an actual file with content, not to a directory).
+ * @see java.io.File#canWrite()
+ * @see java.io.File#isDirectory()
+ */
+ public boolean isWritable() {
+ return (this.file.canWrite() && !this.file.isDirectory());
+ }
+
+ /**
+ * This implementation opens a FileOutputStream for the underlying file.
+ * @see java.io.FileOutputStream
+ */
+ public OutputStream getOutputStream() throws IOException {
+ return new FileOutputStream(this.file);
+ }
+
+
+ /**
+ * This implementation compares the underlying File references.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ return (obj == this ||
+ (obj instanceof FileSystemResource && this.path.equals(((FileSystemResource) obj).path)));
+ }
+
+ /**
+ * This implementation returns the hash code of the underlying File reference.
+ */
+ @Override
+ public int hashCode() {
+ return this.path.hashCode();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/resources/io/InputStreamResource.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/resources/io/InputStreamResource.java b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/InputStreamResource.java
new file mode 100644
index 0000000..3480f7c
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/InputStreamResource.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tamaya.core.internal.resources.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * {@link Resource} implementation for a given InputStream. Should only
+ * be used if no specific Resource implementation is applicable.
+ * In particular, prefer {@code ByteArrayResource} or any of the
+ * file-based Resource implementations where possible.
+ *
+ * <p>In contrast to other Resource implementations, this is a descriptor
+ * for an <i>already opened</i> resource - therefore returning "true" from
+ * {@code isOpen()}. Do not use it if you need to keep the resource
+ * descriptor somewhere, or if you need to read a stream multiple times.
+ *
+ * @author Juergen Hoeller
+ * @since 28.12.2003
+ */
+public class InputStreamResource extends AbstractResource {
+
+ private final InputStream inputStream;
+
+ private final String description;
+
+ private boolean read = false;
+
+
+ /**
+ * Create a new InputStreamResource.
+ * @param inputStream the InputStream to use
+ */
+ public InputStreamResource(InputStream inputStream) {
+ this(inputStream, "resource loaded through InputStream");
+ }
+
+ /**
+ * Create a new InputStreamResource.
+ * @param inputStream the InputStream to use
+ * @param description where the InputStream comes from
+ */
+ public InputStreamResource(InputStream inputStream, String description) {
+ if (inputStream == null) {
+ throw new IllegalArgumentException("InputStream must not be null");
+ }
+ this.inputStream = inputStream;
+ this.description = (description != null ? description : "");
+ }
+
+
+ /**
+ * This implementation always returns {@code true}.
+ */
+ @Override
+ public boolean exists() {
+ return true;
+ }
+
+ /**
+ * This implementation always returns {@code true}.
+ */
+ @Override
+ public boolean isOpen() {
+ return true;
+ }
+
+ /**
+ * This implementation throws IllegalStateException if attempting to
+ * read the underlying stream multiple times.
+ */
+ @Override
+ public InputStream getInputStream() throws IOException {
+ if (this.read) {
+ throw new IllegalStateException("InputStream has already been read - " +
+ "do not use InputStreamResource if a stream needs to be read multiple times");
+ }
+ this.read = true;
+ return this.inputStream;
+ }
+
+ /**
+ * This implementation returns the passed-in description, if any.
+ */
+ @Override
+ public String getDescription() {
+ return this.description;
+ }
+
+
+ /**
+ * This implementation compares the underlying InputStream.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ return (obj == this ||
+ (obj instanceof InputStreamResource && ((InputStreamResource) obj).inputStream.equals(this.inputStream)));
+ }
+
+ /**
+ * This implementation returns the hash code of the underlying InputStream.
+ */
+ @Override
+ public int hashCode() {
+ return this.inputStream.hashCode();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/resources/io/InputStreamSource.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/resources/io/InputStreamSource.java b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/InputStreamSource.java
new file mode 100644
index 0000000..c84199a
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/InputStreamSource.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tamaya.core.internal.resources.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Simple interface for objects that are sources for an {@link InputStream}.
+ *
+ * <p>This is the base interface for Spring's more extensive {@link Resource} interface.
+ *
+ * <p>For single-use streams, {@link InputStreamResource} can be used for any
+ * given {@code InputStream}. Spring's {@code ByteArrayResource} or any
+ * file-based {@code Resource} implementation can be used as a concrete
+ * instance, allowing one to read the underlying content stream multiple times.
+ * This makes this interface useful as an abstract content source for mail
+ * attachments, for example.
+ *
+ * @author Juergen Hoeller
+ * @since 20.01.2004
+ * @see java.io.InputStream
+ * @see Resource
+ * @see InputStreamResource
+ */
+@FunctionalInterface
+public interface InputStreamSource {
+
+ /**
+ * Return an {@link InputStream}.
+ * <p>It is expected that each call creates a <i>fresh</i> stream.
+ * <p>This requirement is particularly important when you consider an API such
+ * as JavaMail, which needs to be able to read the stream multiple times when
+ * creating mail attachments. For such a use case, it is <i>required</i>
+ * that each {@code getInputStream()} call returns a fresh stream.
+ * @return the input stream for the underlying resource (must not be {@code null})
+ * @throws IOException if the stream could not be opened
+ */
+ InputStream getInputStream() throws IOException;
+
+}
\ No newline at end of file
[3/8] incubator-tamaya git commit: TAMAYA-14: added resource support.
TAMAYA-15: Moved PropertyProviders to API,
added SPI. TAMAYA-8: Added/improved Javadoc.
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/resources/io/StringUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/resources/io/StringUtils.java b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/StringUtils.java
new file mode 100644
index 0000000..bfdb5f2
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/StringUtils.java
@@ -0,0 +1,1173 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tamaya.core.internal.resources.io;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Properties;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TimeZone;
+import java.util.TreeSet;
+
+/**
+* Miscellaneous {@link String} utility methods.
+*
+* <p>Mainly for internal use within the framework; consider
+* <a href="http://jakarta.apache.org/commons/lang/">Jakarta's Commons Lang</a>
+* for a more comprehensive suite of String utilities.
+*
+* <p>This class delivers some simple functionality that should really
+* be provided by the core Java {@code String} and {@link StringBuilder}
+* classes, such as the ability to {@code replace} all occurrences of a given
+* substring in a target string. It also provides easy-to-use methods to convert
+* between delimited strings, such as CSV strings, and collections and arrays.
+*
+* @author Rod Johnson
+* @author Juergen Hoeller
+* @author Keith Donald
+* @author Rob Harrop
+* @author Rick Evans
+* @author Arjen Poutsma
+* @since 16 April 2001
+*/
+class StringUtils {
+
+ private static final String FOLDER_SEPARATOR = "/";
+
+ private static final String WINDOWS_FOLDER_SEPARATOR = "\\";
+
+ private static final String TOP_PATH = "..";
+
+ private static final String CURRENT_PATH = ".";
+
+// private static final char EXTENSION_SEPARATOR = '.';
+//
+
+ private StringUtils(){}
+
+// //---------------------------------------------------------------------
+// // General convenience methods for working with Strings
+// //---------------------------------------------------------------------
+//
+// /**
+// * Check whether the given String is empty.
+// * <p>This method accepts any Object as an argument, comparing it to
+// * {@code null} and the empty String. As a consequence, this method
+// * will never return {@code true} for a non-null non-String object.
+// * <p>The Object signature is useful for general attribute handling code
+// * that commonly deals with Strings but generally has to iterate over
+// * Objects since attributes may e.g. be primitive value objects as well.
+// * @param str the candidate String
+// * @since 3.2.1
+// */
+// public static boolean isEmpty(Object str) {
+// return (str == null || "".equals(str));
+// }
+
+ /**
+ * Check that the given CharSequence is neither {@code null} nor of length 0.
+ * Note: Will return {@code true} for a CharSequence that purely consists of whitespace.
+ * <p><pre class="code">
+ * StringUtils.hasLength(null) = false
+ * StringUtils.hasLength("") = false
+ * StringUtils.hasLength(" ") = true
+ * StringUtils.hasLength("Hello") = true
+ * </pre>
+ * @param str the CharSequence to check (may be {@code null})
+ * @return {@code true} if the CharSequence is not null and has length
+ */
+ public static boolean hasLength(CharSequence str) {
+ return (str != null && str.length() > 0);
+ }
+
+// /**
+// * Check that the given String is neither {@code null} nor of length 0.
+// * Note: Will return {@code true} for a String that purely consists of whitespace.
+// * @param str the String to check (may be {@code null})
+// * @return {@code true} if the String is not null and has length
+// * @see #hasLength(CharSequence)
+// */
+// public static boolean hasLength(String str) {
+// return hasLength((CharSequence) str);
+// }
+
+ /**
+ * Check whether the given CharSequence has actual text.
+ * More specifically, returns {@code true} if the string not {@code null},
+ * its length is greater than 0, and it contains at least one non-whitespace character.
+ * <p><pre class="code">
+ * StringUtils.hasText(null) = false
+ * StringUtils.hasText("") = false
+ * StringUtils.hasText(" ") = false
+ * StringUtils.hasText("12345") = true
+ * StringUtils.hasText(" 12345 ") = true
+ * </pre>
+ * @param str the CharSequence to check (may be {@code null})
+ * @return {@code true} if the CharSequence is not {@code null},
+ * its length is greater than 0, and it does not contain whitespace only
+ * @see Character#isWhitespace
+ */
+ public static boolean hasText(CharSequence str) {
+ if (!hasLength(str)) {
+ return false;
+ }
+ int strLen = str.length();
+ for (int i = 0; i < strLen; i++) {
+ if (!Character.isWhitespace(str.charAt(i))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check whether the given String has actual text.
+ * More specifically, returns {@code true} if the string not {@code null},
+ * its length is greater than 0, and it contains at least one non-whitespace character.
+ * @param str the String to check (may be {@code null})
+ * @return {@code true} if the String is not {@code null}, its length is
+ * greater than 0, and it does not contain whitespace only
+ * @see #hasText(CharSequence)
+ */
+ public static boolean hasText(String str) {
+ return hasText((CharSequence) str);
+ }
+
+// /**
+// * Check whether the given CharSequence contains any whitespace characters.
+// * @param str the CharSequence to check (may be {@code null})
+// * @return {@code true} if the CharSequence is not empty and
+// * contains at least 1 whitespace character
+// * @see Character#isWhitespace
+// */
+// public static boolean containsWhitespace(CharSequence str) {
+// if (!hasLength(str)) {
+// return false;
+// }
+// int strLen = str.length();
+// for (int i = 0; i < strLen; i++) {
+// if (Character.isWhitespace(str.charAt(i))) {
+// return true;
+// }
+// }
+// return false;
+// }
+//
+// /**
+// * Check whether the given String contains any whitespace characters.
+// * @param str the String to check (may be {@code null})
+// * @return {@code true} if the String is not empty and
+// * contains at least 1 whitespace character
+// * @see #containsWhitespace(CharSequence)
+// */
+// public static boolean containsWhitespace(String str) {
+// return containsWhitespace((CharSequence) str);
+// }
+//
+// /**
+// * Trim leading and trailing whitespace from the given String.
+// * @param str the String to check
+// * @return the trimmed String
+// * @see java.lang.Character#isWhitespace
+// */
+// public static String trimWhitespace(String str) {
+// if (!hasLength(str)) {
+// return str;
+// }
+// StringBuilder sb = new StringBuilder(str);
+// while (sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) {
+// sb.deleteCharAt(0);
+// }
+// while (sb.length() > 0 && Character.isWhitespace(sb.charAt(sb.length() - 1))) {
+// sb.deleteCharAt(sb.length() - 1);
+// }
+// return sb.toString();
+// }
+//
+// /**
+// * Trim <i>all</i> whitespace from the given String:
+// * leading, trailing, and in between characters.
+// * @param str the String to check
+// * @return the trimmed String
+// * @see java.lang.Character#isWhitespace
+// */
+// public static String trimAllWhitespace(String str) {
+// if (!hasLength(str)) {
+// return str;
+// }
+// int len = str.length();
+// StringBuilder sb = new StringBuilder(str.length());
+// for (int i = 0; i < len; i++) {
+// char c = str.charAt(i);
+// if (!Character.isWhitespace(c)) {
+// sb.append(c);
+// }
+// }
+// return sb.toString();
+// }
+//
+// /**
+// * Trim leading whitespace from the given String.
+// * @param str the String to check
+// * @return the trimmed String
+// * @see java.lang.Character#isWhitespace
+// */
+// public static String trimLeadingWhitespace(String str) {
+// if (!hasLength(str)) {
+// return str;
+// }
+// StringBuilder sb = new StringBuilder(str);
+// while (sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) {
+// sb.deleteCharAt(0);
+// }
+// return sb.toString();
+// }
+//
+// /**
+// * Trim trailing whitespace from the given String.
+// * @param str the String to check
+// * @return the trimmed String
+// * @see java.lang.Character#isWhitespace
+// */
+// public static String trimTrailingWhitespace(String str) {
+// if (!hasLength(str)) {
+// return str;
+// }
+// StringBuilder sb = new StringBuilder(str);
+// while (sb.length() > 0 && Character.isWhitespace(sb.charAt(sb.length() - 1))) {
+// sb.deleteCharAt(sb.length() - 1);
+// }
+// return sb.toString();
+// }
+//
+// /**
+// * Trim all occurrences of the supplied leading character from the given String.
+// * @param str the String to check
+// * @param leadingCharacter the leading character to be trimmed
+// * @return the trimmed String
+// */
+// public static String trimLeadingCharacter(String str, char leadingCharacter) {
+// if (!hasLength(str)) {
+// return str;
+// }
+// StringBuilder sb = new StringBuilder(str);
+// while (sb.length() > 0 && sb.charAt(0) == leadingCharacter) {
+// sb.deleteCharAt(0);
+// }
+// return sb.toString();
+// }
+//
+// /**
+// * Trim all occurrences of the supplied trailing character from the given String.
+// * @param str the String to check
+// * @param trailingCharacter the trailing character to be trimmed
+// * @return the trimmed String
+// */
+// public static String trimTrailingCharacter(String str, char trailingCharacter) {
+// if (!hasLength(str)) {
+// return str;
+// }
+// StringBuilder sb = new StringBuilder(str);
+// while (sb.length() > 0 && sb.charAt(sb.length() - 1) == trailingCharacter) {
+// sb.deleteCharAt(sb.length() - 1);
+// }
+// return sb.toString();
+// }
+//
+//
+// /**
+// * Test if the given String starts with the specified prefix,
+// * ignoring upper/lower case.
+// * @param str the String to check
+// * @param prefix the prefix to look for
+// * @see java.lang.String#startsWith
+// */
+// public static boolean startsWithIgnoreCase(String str, String prefix) {
+// if (str == null || prefix == null) {
+// return false;
+// }
+// if (str.startsWith(prefix)) {
+// return true;
+// }
+// if (str.length() < prefix.length()) {
+// return false;
+// }
+// String lcStr = str.substring(0, prefix.length()).toLowerCase();
+// String lcPrefix = prefix.toLowerCase();
+// return lcStr.equals(lcPrefix);
+// }
+//
+// /**
+// * Test if the given String ends with the specified suffix,
+// * ignoring upper/lower case.
+// * @param str the String to check
+// * @param suffix the suffix to look for
+// * @see java.lang.String#endsWith
+// */
+// public static boolean endsWithIgnoreCase(String str, String suffix) {
+// if (str == null || suffix == null) {
+// return false;
+// }
+// if (str.endsWith(suffix)) {
+// return true;
+// }
+// if (str.length() < suffix.length()) {
+// return false;
+// }
+//
+// String lcStr = str.substring(str.length() - suffix.length()).toLowerCase();
+// String lcSuffix = suffix.toLowerCase();
+// return lcStr.equals(lcSuffix);
+// }
+//
+// /**
+// * Test whether the given string matches the given substring
+// * at the given index.
+// * @param str the original string (or StringBuilder)
+// * @param index the index in the original string to start matching against
+// * @param substring the substring to match at the given index
+// */
+// public static boolean substringMatch(CharSequence str, int index, CharSequence substring) {
+// for (int j = 0; j < substring.length(); j++) {
+// int i = index + j;
+// if (i >= str.length() || str.charAt(i) != substring.charAt(j)) {
+// return false;
+// }
+// }
+// return true;
+// }
+//
+// /**
+// * Count the occurrences of the substring in string s.
+// * @param str string to search in. Return 0 if this is null.
+// * @param sub string to search for. Return 0 if this is null.
+// */
+// public static int countOccurrencesOf(String str, String sub) {
+// if (str == null || sub == null || str.length() == 0 || sub.length() == 0) {
+// return 0;
+// }
+// int count = 0;
+// int pos = 0;
+// int idx;
+// while ((idx = str.indexOf(sub, pos)) != -1) {
+// ++count;
+// pos = idx + sub.length();
+// }
+// return count;
+// }
+//
+ /**
+ * Replace all occurrences of a substring within a string with
+ * another string.
+ * @param inString String to examine
+ * @param oldPattern String to replace
+ * @param newPattern String to insert
+ * @return a String with the replacements
+ */
+ public static String replace(String inString, String oldPattern, String newPattern) {
+ if (!hasLength(inString) || !hasLength(oldPattern) || newPattern == null) {
+ return inString;
+ }
+ StringBuilder sb = new StringBuilder();
+ int pos = 0; // our position in the old string
+ int index = inString.indexOf(oldPattern);
+ // the index of an occurrence we've found, or -1
+ int patLen = oldPattern.length();
+ while (index >= 0) {
+ sb.append(inString.substring(pos, index));
+ sb.append(newPattern);
+ pos = index + patLen;
+ index = inString.indexOf(oldPattern, pos);
+ }
+ sb.append(inString.substring(pos));
+ // remember to append any characters to the right of a match
+ return sb.toString();
+ }
+
+// /**
+// * Delete all occurrences of the given substring.
+// * @param inString the original String
+// * @param pattern the pattern to delete all occurrences of
+// * @return the resulting String
+// */
+// public static String delete(String inString, String pattern) {
+// return replace(inString, pattern, "");
+// }
+//
+ /**
+ * Delete any character in a given String.
+ * @param inString the original String
+ * @param charsToDelete a set of characters to delete.
+ * E.g. "az\n" will delete 'a's, 'z's and new lines.
+ * @return the resulting String
+ */
+ public static String deleteAny(String inString, String charsToDelete) {
+ if (!hasLength(inString) || !hasLength(charsToDelete)) {
+ return inString;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < inString.length(); i++) {
+ char c = inString.charAt(i);
+ if (charsToDelete.indexOf(c) == -1) {
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+
+//
+// //---------------------------------------------------------------------
+// // Convenience methods for working with formatted Strings
+// //---------------------------------------------------------------------
+//
+// /**
+// * Quote the given String with single quotes.
+// * @param str the input String (e.g. "myString")
+// * @return the quoted String (e.g. "'myString'"),
+// * or {@code null} if the input was {@code null}
+// */
+// public static String quote(String str) {
+// return (str != null ? "'" + str + "'" : null);
+// }
+//
+// /**
+// * Turn the given Object into a String with single quotes
+// * if it is a String; keeping the Object as-is else.
+// * @param obj the input Object (e.g. "myString")
+// * @return the quoted String (e.g. "'myString'"),
+// * or the input object as-is if not a String
+// */
+// public static Object quoteIfString(Object obj) {
+// return (obj instanceof String ? quote((String) obj) : obj);
+// }
+//
+// /**
+// * Unqualify a string qualified by a '.' dot character. For example,
+// * "this.name.is.qualified", returns "qualified".
+// * @param qualifiedName the qualified name
+// */
+// public static String unqualify(String qualifiedName) {
+// return unqualify(qualifiedName, '.');
+// }
+//
+// /**
+// * Unqualify a string qualified by a separator character. For example,
+// * "this:name:is:qualified" returns "qualified" if using a ':' separator.
+// * @param qualifiedName the qualified name
+// * @param separator the separator
+// */
+// public static String unqualify(String qualifiedName, char separator) {
+// return qualifiedName.substring(qualifiedName.lastIndexOf(separator) + 1);
+// }
+//
+// /**
+// * Capitalize a {@code String}, changing the first letter to
+// * upper case as per {@link Character#toUpperCase(char)}.
+// * No other letters are changed.
+// * @param str the String to capitalize, may be {@code null}
+// * @return the capitalized String, {@code null} if null
+// */
+// public static String capitalize(String str) {
+// return changeFirstCharacterCase(str, true);
+// }
+//
+// /**
+// * Uncapitalize a {@code String}, changing the first letter to
+// * lower case as per {@link Character#toLowerCase(char)}.
+// * No other letters are changed.
+// * @param str the String to uncapitalize, may be {@code null}
+// * @return the uncapitalized String, {@code null} if null
+// */
+// public static String uncapitalize(String str) {
+// return changeFirstCharacterCase(str, false);
+// }
+//
+// private static String changeFirstCharacterCase(String str, boolean capitalize) {
+// if (str == null || str.length() == 0) {
+// return str;
+// }
+// StringBuilder sb = new StringBuilder(str.length());
+// if (capitalize) {
+// sb.append(Character.toUpperCase(str.charAt(0)));
+// }
+// else {
+// sb.append(Character.toLowerCase(str.charAt(0)));
+// }
+// sb.append(str.substring(1));
+// return sb.toString();
+// }
+
+ /**
+ * Extract the filename from the given path,
+ * e.g. "mypath/myfile.txt" -> "myfile.txt".
+ * @param path the file path (may be {@code null})
+ * @return the extracted filename, or {@code null} if none
+ */
+ public static String getFilename(String path) {
+ if (path == null) {
+ return null;
+ }
+ int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);
+ return (separatorIndex != -1 ? path.substring(separatorIndex + 1) : path);
+ }
+
+// /**
+// * Extract the filename extension from the given path,
+// * e.g. "mypath/myfile.txt" -> "txt".
+// * @param path the file path (may be {@code null})
+// * @return the extracted filename extension, or {@code null} if none
+// */
+// public static String getFilenameExtension(String path) {
+// if (path == null) {
+// return null;
+// }
+// int extIndex = path.lastIndexOf(EXTENSION_SEPARATOR);
+// if (extIndex == -1) {
+// return null;
+// }
+// int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR);
+// if (folderIndex > extIndex) {
+// return null;
+// }
+// return path.substring(extIndex + 1);
+// }
+//
+// /**
+// * Strip the filename extension from the given path,
+// * e.g. "mypath/myfile.txt" -> "mypath/myfile".
+// * @param path the file path (may be {@code null})
+// * @return the path with stripped filename extension,
+// * or {@code null} if none
+// */
+// public static String stripFilenameExtension(String path) {
+// if (path == null) {
+// return null;
+// }
+// int extIndex = path.lastIndexOf(EXTENSION_SEPARATOR);
+// if (extIndex == -1) {
+// return path;
+// }
+// int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR);
+// if (folderIndex > extIndex) {
+// return path;
+// }
+// return path.substring(0, extIndex);
+// }
+
+ /**
+ * Apply the given relative path to the given path,
+ * assuming standard Java folder separation (i.e. "/" separators).
+ * @param path the path to start from (usually a full file path)
+ * @param relativePath the relative path to apply
+ * (relative to the full file path above)
+ * @return the full file path that results from applying the relative path
+ */
+ public static String applyRelativePath(String path, String relativePath) {
+ int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);
+ if (separatorIndex != -1) {
+ String newPath = path.substring(0, separatorIndex);
+ if (!relativePath.startsWith(FOLDER_SEPARATOR)) {
+ newPath += FOLDER_SEPARATOR;
+ }
+ return newPath + relativePath;
+ }
+ else {
+ return relativePath;
+ }
+ }
+
+ /**
+ * Normalize the path by suppressing sequences like "path/.." and
+ * inner simple dots.
+ * <p>The result is convenient for path comparison. For other uses,
+ * notice that Windows separators ("\") are replaced by simple slashes.
+ * @param path the original path
+ * @return the normalized path
+ */
+ public static String cleanPath(String path) {
+ if (path == null) {
+ return null;
+ }
+ String pathToUse = StringUtils.replace(path, WINDOWS_FOLDER_SEPARATOR, FOLDER_SEPARATOR);
+
+ // Strip prefix from path to analyze, to not treat it as part of the
+ // first path element. This is necessary to correctly parse paths like
+ // "file:core/../core/io/Resource.class", where the ".." should just
+ // strip the first "core" directory while keeping the "file:" prefix.
+ int prefixIndex = pathToUse.indexOf(":");
+ String prefix = "";
+ if (prefixIndex != -1) {
+ prefix = pathToUse.substring(0, prefixIndex + 1);
+ if (prefix.contains("/")) {
+ prefix = "";
+ }
+ else {
+ pathToUse = pathToUse.substring(prefixIndex + 1);
+ }
+ }
+ if (pathToUse.startsWith(FOLDER_SEPARATOR)) {
+ prefix = prefix + FOLDER_SEPARATOR;
+ pathToUse = pathToUse.substring(1);
+ }
+
+ String[] pathArray = delimitedListToStringArray(pathToUse, FOLDER_SEPARATOR);
+ List<String> pathElements = new LinkedList<String>();
+ int tops = 0;
+
+ for (int i = pathArray.length - 1; i >= 0; i--) {
+ String element = pathArray[i];
+ if (CURRENT_PATH.equals(element)) {
+ // Points to current directory - drop it.
+ }
+ else if (TOP_PATH.equals(element)) {
+ // Registering top path found.
+ tops++;
+ }
+ else {
+ if (tops > 0) {
+ // Merging path element with element corresponding to top path.
+ tops--;
+ }
+ else {
+ // Normal path element found.
+ pathElements.add(0, element);
+ }
+ }
+ }
+ // Remaining top paths need to be retained.
+ for (int i = 0; i < tops; i++) {
+ pathElements.add(0, TOP_PATH);
+ }
+ return prefix + collectionToDelimitedString(pathElements, FOLDER_SEPARATOR);
+ }
+//
+// /**
+// * Compare two paths after normalization of them.
+// * @param path1 first path for comparison
+// * @param path2 second path for comparison
+// * @return whether the two paths are equivalent after normalization
+// */
+// public static boolean pathEquals(String path1, String path2) {
+// return cleanPath(path1).equals(cleanPath(path2));
+// }
+//
+// /**
+// * Parse the given {@code localeString} value into a {@link Locale}.
+// * <p>This is the inverse operation of {@link Locale#toString Locale's toString}.
+// * @param localeString the locale String, following {@code Locale's}
+// * {@code toString()} format ("en", "en_UK", etc);
+// * also accepts spaces as separators, as an alternative to underscores
+// * @return a corresponding {@code Locale} instance
+// * @throws IllegalArgumentException in case of an invalid locale specification
+// */
+// public static Locale parseLocaleString(String localeString) {
+// String[] parts = tokenizeToStringArray(localeString, "_ ", false, false);
+// String language = (parts.length > 0 ? parts[0] : "");
+// String country = (parts.length > 1 ? parts[1] : "");
+// validateLocalePart(language);
+// validateLocalePart(country);
+// String variant = "";
+// if (parts.length > 2) {
+// // There is definitely a variant, and it is everything after the country
+// // code sans the separator between the country code and the variant.
+// int endIndexOfCountryCode = localeString.indexOf(country, language.length()) + country.length();
+// // Strip off any leading '_' and whitespace, what's left is the variant.
+// variant = trimLeadingWhitespace(localeString.substring(endIndexOfCountryCode));
+// if (variant.startsWith("_")) {
+// variant = trimLeadingCharacter(variant, '_');
+// }
+// }
+// return (language.length() > 0 ? new Locale(language, country, variant) : null);
+// }
+//
+// private static void validateLocalePart(String localePart) {
+// for (int i = 0; i < localePart.length(); i++) {
+// char ch = localePart.charAt(i);
+// if (ch != '_' && ch != ' ' && !Character.isLetterOrDigit(ch)) {
+// throw new IllegalArgumentException(
+// "Locale part \"" + localePart + "\" contains invalid characters");
+// }
+// }
+// }
+//
+// /**
+// * Determine the RFC 3066 compliant language tag,
+// * as used for the HTTP "Accept-Language" header.
+// * @param locale the Locale to transform to a language tag
+// * @return the RFC 3066 compliant language tag as String
+// */
+// public static String toLanguageTag(Locale locale) {
+// return locale.getLanguage() + (hasText(locale.getCountry()) ? "-" + locale.getCountry() : "");
+// }
+//
+// /**
+// * Parse the given {@code timeZoneString} value into a {@link TimeZone}.
+// * @param timeZoneString the time zone String, following {@link TimeZone#getTimeZone(String)}
+// * but throwing {@link IllegalArgumentException} in case of an invalid time zone specification
+// * @return a corresponding {@link TimeZone} instance
+// * @throws IllegalArgumentException in case of an invalid time zone specification
+// */
+// public static TimeZone parseTimeZoneString(String timeZoneString) {
+// TimeZone timeZone = TimeZone.getTimeZone(timeZoneString);
+// if ("GMT".equals(timeZone.getID()) && !timeZoneString.startsWith("GMT")) {
+// // We don't want that GMT fallback...
+// throw new IllegalArgumentException("Invalid time zone specification '" + timeZoneString + "'");
+// }
+// return timeZone;
+// }
+//
+//
+// //---------------------------------------------------------------------
+// // Convenience methods for working with String arrays
+// //---------------------------------------------------------------------
+//
+// /**
+// * Append the given String to the given String array, returning a new array
+// * consisting of the input array contents plus the given String.
+// * @param array the array to append to (can be {@code null})
+// * @param str the String to append
+// * @return the new array (never {@code null})
+// */
+// public static String[] addStringToArray(String[] array, String str) {
+// if (ObjectUtils.isEmpty(array)) {
+// return new String[] {str};
+// }
+// String[] newArr = new String[array.length + 1];
+// System.arraycopy(array, 0, newArr, 0, array.length);
+// newArr[array.length] = str;
+// return newArr;
+// }
+//
+// /**
+// * Concatenate the given String arrays into one,
+// * with overlapping array elements included twice.
+// * <p>The order of elements in the original arrays is preserved.
+// * @param array1 the first array (can be {@code null})
+// * @param array2 the second array (can be {@code null})
+// * @return the new array ({@code null} if both given arrays were {@code null})
+// */
+// public static String[] concatenateStringArrays(String[] array1, String[] array2) {
+// if (ObjectUtils.isEmpty(array1)) {
+// return array2;
+// }
+// if (ObjectUtils.isEmpty(array2)) {
+// return array1;
+// }
+// String[] newArr = new String[array1.length + array2.length];
+// System.arraycopy(array1, 0, newArr, 0, array1.length);
+// System.arraycopy(array2, 0, newArr, array1.length, array2.length);
+// return newArr;
+// }
+//
+// /**
+// * Merge the given String arrays into one, with overlapping
+// * array elements only included once.
+// * <p>The order of elements in the original arrays is preserved
+// * (with the exception of overlapping elements, which are only
+// * included on their first occurrence).
+// * @param array1 the first array (can be {@code null})
+// * @param array2 the second array (can be {@code null})
+// * @return the new array ({@code null} if both given arrays were {@code null})
+// */
+// public static String[] mergeStringArrays(String[] array1, String[] array2) {
+// if (ObjectUtils.isEmpty(array1)) {
+// return array2;
+// }
+// if (ObjectUtils.isEmpty(array2)) {
+// return array1;
+// }
+// List<String> result = new ArrayList<String>();
+// result.addAll(Arrays.asList(array1));
+// for (String str : array2) {
+// if (!result.contains(str)) {
+// result.add(str);
+// }
+// }
+// return toStringArray(result);
+// }
+//
+// /**
+// * Turn given source String array into sorted array.
+// * @param array the source array
+// * @return the sorted array (never {@code null})
+// */
+// public static String[] sortStringArray(String[] array) {
+// if (ObjectUtils.isEmpty(array)) {
+// return new String[0];
+// }
+// Arrays.sort(array);
+// return array;
+// }
+//
+ /**
+ * Copy the given Collection into a String array.
+ * The Collection must contain String elements only.
+ * @param collection the Collection to copy
+ * @return the String array ({@code null} if the passed-in
+ * Collection was {@code null})
+ */
+ public static String[] toStringArray(Collection<String> collection) {
+ if (collection == null) {
+ return null;
+ }
+ return collection.toArray(new String[collection.size()]);
+ }
+//
+// /**
+// * Copy the given Enumeration into a String array.
+// * The Enumeration must contain String elements only.
+// * @param enumeration the Enumeration to copy
+// * @return the String array ({@code null} if the passed-in
+// * Enumeration was {@code null})
+// */
+// public static String[] toStringArray(Enumeration<String> enumeration) {
+// if (enumeration == null) {
+// return null;
+// }
+// List<String> list = Collections.list(enumeration);
+// return list.toArray(new String[list.size()]);
+// }
+//
+// /**
+// * Trim the elements of the given String array,
+// * calling {@code String.trim()} on each of them.
+// * @param array the original String array
+// * @return the resulting array (of the same size) with trimmed elements
+// */
+// public static String[] trimArrayElements(String[] array) {
+// if (ObjectUtils.isEmpty(array)) {
+// return new String[0];
+// }
+// String[] result = new String[array.length];
+// for (int i = 0; i < array.length; i++) {
+// String element = array[i];
+// result[i] = (element != null ? element.trim() : null);
+// }
+// return result;
+// }
+//
+// /**
+// * Remove duplicate Strings from the given array.
+// * Also sorts the array, as it uses a TreeSet.
+// * @param array the String array
+// * @return an array without duplicates, in natural sort order
+// */
+// public static String[] removeDuplicateStrings(String[] array) {
+// if (ObjectUtils.isEmpty(array)) {
+// return array;
+// }
+// Set<String> set = new TreeSet<String>();
+// for (String element : array) {
+// set.add(element);
+// }
+// return toStringArray(set);
+// }
+//
+ /**
+ * Split a String at the first occurrence of the delimiter.
+ * Does not include the delimiter in the result.
+ * @param toSplit the string to split
+ * @param delimiter to split the string up with
+ * @return a two element array with index 0 being before the delimiter, and
+ * index 1 being after the delimiter (neither element includes the delimiter);
+ * or {@code null} if the delimiter wasn't found in the given input String
+ */
+ public static String[] split(String toSplit, String delimiter) {
+ if (!hasLength(toSplit) || !hasLength(delimiter)) {
+ return null;
+ }
+ int offset = toSplit.indexOf(delimiter);
+ if (offset < 0) {
+ return null;
+ }
+ String beforeDelimiter = toSplit.substring(0, offset);
+ String afterDelimiter = toSplit.substring(offset + delimiter.length());
+ return new String[] {beforeDelimiter, afterDelimiter};
+ }
+//
+// /**
+// * Take an array Strings and split each element based on the given delimiter.
+// * A {@code Properties} instance is then generated, with the left of the
+// * delimiter providing the key, and the right of the delimiter providing the value.
+// * <p>Will trim both the key and value before adding them to the
+// * {@code Properties} instance.
+// * @param array the array to process
+// * @param delimiter to split each element using (typically the equals symbol)
+// * @return a {@code Properties} instance representing the array contents,
+// * or {@code null} if the array to process was null or empty
+// */
+// public static Properties splitArrayElementsIntoProperties(String[] array, String delimiter) {
+// return splitArrayElementsIntoProperties(array, delimiter, null);
+// }
+//
+// /**
+// * Take an array Strings and split each element based on the given delimiter.
+// * A {@code Properties} instance is then generated, with the left of the
+// * delimiter providing the key, and the right of the delimiter providing the value.
+// * <p>Will trim both the key and value before adding them to the
+// * {@code Properties} instance.
+// * @param array the array to process
+// * @param delimiter to split each element using (typically the equals symbol)
+// * @param charsToDelete one or more characters to remove from each element
+// * prior to attempting the split operation (typically the quotation mark
+// * symbol), or {@code null} if no removal should occur
+// * @return a {@code Properties} instance representing the array contents,
+// * or {@code null} if the array to process was {@code null} or empty
+// */
+// public static Properties splitArrayElementsIntoProperties(
+// String[] array, String delimiter, String charsToDelete) {
+//
+// if (ObjectUtils.isEmpty(array)) {
+// return null;
+// }
+// Properties result = new Properties();
+// for (String element : array) {
+// if (charsToDelete != null) {
+// element = deleteAny(element, charsToDelete);
+// }
+// String[] splittedElement = split(element, delimiter);
+// if (splittedElement == null) {
+// continue;
+// }
+// result.setProperty(splittedElement[0].trim(), splittedElement[1].trim());
+// }
+// return result;
+// }
+//
+ /**
+ * Tokenize the given String into a String array via a StringTokenizer.
+ * Trims tokens and omits empty tokens.
+ * <p>The given delimiters string is supposed to consist of any number of
+ * delimiter characters. Each of those characters can be used to separate
+ * tokens. A delimiter is always a single character; for multi-character
+ * delimiters, consider using {@code delimitedListToStringArray}
+ * @param str the String to tokenize
+ * @param delimiters the delimiter characters, assembled as String
+ * (each of those characters is individually considered as delimiter).
+ * @return an array of the tokens
+ * @see java.util.StringTokenizer
+ * @see String#trim()
+ */
+ public static String[] tokenizeToStringArray(String str, String delimiters) {
+ return tokenizeToStringArray(str, delimiters, true, true);
+ }
+
+ /**
+ * Tokenize the given String into a String array via a StringTokenizer.
+ * <p>The given delimiters string is supposed to consist of any number of
+ * delimiter characters. Each of those characters can be used to separate
+ * tokens. A delimiter is always a single character; for multi-character
+ * delimiters, consider using {@code delimitedListToStringArray}
+ * @param str the String to tokenize
+ * @param delimiters the delimiter characters, assembled as String
+ * (each of those characters is individually considered as delimiter)
+ * @param trimTokens trim the tokens via String's {@code trim}
+ * @param ignoreEmptyTokens omit empty tokens from the result array
+ * (only applies to tokens that are empty after trimming; StringTokenizer
+ * will not consider subsequent delimiters as token in the first place).
+ * @return an array of the tokens ({@code null} if the input String
+ * was {@code null})
+ * @see java.util.StringTokenizer
+ * @see String#trim()
+ */
+ public static String[] tokenizeToStringArray(
+ String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) {
+
+ if (str == null) {
+ return null;
+ }
+ StringTokenizer st = new StringTokenizer(str, delimiters);
+ List<String> tokens = new ArrayList<String>();
+ while (st.hasMoreTokens()) {
+ String token = st.nextToken();
+ if (trimTokens) {
+ token = token.trim();
+ }
+ if (!ignoreEmptyTokens || token.length() > 0) {
+ tokens.add(token);
+ }
+ }
+ return toStringArray(tokens);
+ }
+
+ /**
+ * Take a String which is a delimited list and convert it to a String array.
+ * <p>A single delimiter can consists of more than one character: It will still
+ * be considered as single delimiter string, rather than as bunch of potential
+ * delimiter characters - in contrast to {@code tokenizeToStringArray}.
+ * @param str the input String
+ * @param delimiter the delimiter between elements (this is a single delimiter,
+ * rather than a bunch individual delimiter characters)
+ * @return an array of the tokens in the list
+ * @see #tokenizeToStringArray
+ */
+ public static String[] delimitedListToStringArray(String str, String delimiter) {
+ return delimitedListToStringArray(str, delimiter, null);
+ }
+
+ /**
+ * Take a String which is a delimited list and convert it to a String array.
+ * <p>A single delimiter can consists of more than one character: It will still
+ * be considered as single delimiter string, rather than as bunch of potential
+ * delimiter characters - in contrast to {@code tokenizeToStringArray}.
+ * @param str the input String
+ * @param delimiter the delimiter between elements (this is a single delimiter,
+ * rather than a bunch individual delimiter characters)
+ * @param charsToDelete a set of characters to delete. Useful for deleting unwanted
+ * line breaks: e.g. "\r\n\f" will delete all new lines and line feeds in a String.
+ * @return an array of the tokens in the list
+ * @see #tokenizeToStringArray
+ */
+ public static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete) {
+ if (str == null) {
+ return new String[0];
+ }
+ if (delimiter == null) {
+ return new String[] {str};
+ }
+ List<String> result = new ArrayList<String>();
+ if ("".equals(delimiter)) {
+ for (int i = 0; i < str.length(); i++) {
+ result.add(deleteAny(str.substring(i, i + 1), charsToDelete));
+ }
+ }
+ else {
+ int pos = 0;
+ int delPos;
+ while ((delPos = str.indexOf(delimiter, pos)) != -1) {
+ result.add(deleteAny(str.substring(pos, delPos), charsToDelete));
+ pos = delPos + delimiter.length();
+ }
+ if (str.length() > 0 && pos <= str.length()) {
+ // Add rest of String, but not in case of empty input.
+ result.add(deleteAny(str.substring(pos), charsToDelete));
+ }
+ }
+ return toStringArray(result);
+ }
+
+// /**
+// * Convert a CSV list into an array of Strings.
+// * @param str the input String
+// * @return an array of Strings, or the empty array in case of empty input
+// */
+// public static String[] commaDelimitedListToStringArray(String str) {
+// return delimitedListToStringArray(str, ",");
+// }
+//
+// /**
+// * Convenience method to convert a CSV string list to a set.
+// * Note that this will suppress duplicates.
+// * @param str the input String
+// * @return a Set of String entries in the list
+// */
+// public static Set<String> commaDelimitedListToSet(String str) {
+// Set<String> set = new TreeSet<String>();
+// String[] tokens = commaDelimitedListToStringArray(str);
+// for (String token : tokens) {
+// set.add(token);
+// }
+// return set;
+// }
+//
+ /**
+ * Convenience method to return a Collection as a delimited (e.g. CSV)
+ * String. E.g. useful for {@code toString()} implementations.
+ * @param coll the Collection to display
+ * @param delim the delimiter to use (probably a ",")
+ * @param prefix the String to start each element with
+ * @param suffix the String to end each element with
+ * @return the delimited String
+ */
+ public static String collectionToDelimitedString(Collection<?> coll, String delim, String prefix, String suffix) {
+ if (coll.isEmpty()) {
+ return "";
+ }
+ StringBuilder sb = new StringBuilder();
+ Iterator<?> it = coll.iterator();
+ while (it.hasNext()) {
+ sb.append(prefix).append(it.next()).append(suffix);
+ if (it.hasNext()) {
+ sb.append(delim);
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Convenience method to return a Collection as a delimited (e.g. CSV)
+ * String. E.g. useful for {@code toString()} implementations.
+ * @param coll the Collection to display
+ * @param delim the delimiter to use (probably a ",")
+ * @return the delimited String
+ */
+ public static String collectionToDelimitedString(Collection<?> coll, String delim) {
+ return collectionToDelimitedString(coll, delim, "", "");
+ }
+
+// /**
+// * Convenience method to return a Collection as a CSV String.
+// * E.g. useful for {@code toString()} implementations.
+// * @param coll the Collection to display
+// * @return the delimited String
+// */
+// public static String collectionToCommaDelimitedString(Collection<?> coll) {
+// return collectionToDelimitedString(coll, ",");
+// }
+
+// /**
+// * Convenience method to return a String array as a delimited (e.g. CSV)
+// * String. E.g. useful for {@code toString()} implementations.
+// * @param arr the array to display
+// * @param delim the delimiter to use (probably a ",")
+// * @return the delimited String
+// */
+// public static String arrayToDelimitedString(Object[] arr, String delim) {
+// if (ObjectUtils.isEmpty(arr)) {
+// return "";
+// }
+// if (arr.length == 1) {
+// return ObjectUtils.nullSafeToString(arr[0]);
+// }
+// StringBuilder sb = new StringBuilder();
+// for (int i = 0; i < arr.length; i++) {
+// if (i > 0) {
+// sb.append(delim);
+// }
+// sb.append(arr[i]);
+// }
+// return sb.toString();
+// }
+//
+// /**
+// * Convenience method to return a String array as a CSV String.
+// * E.g. useful for {@code toString()} implementations.
+// * @param arr the array to display
+// * @return the delimited String
+// */
+// public static String arrayToCommaDelimitedString(Object[] arr) {
+// return arrayToDelimitedString(arr, ",");
+// }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/resources/io/UrlResource.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/resources/io/UrlResource.java b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/UrlResource.java
new file mode 100644
index 0000000..9764820
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/UrlResource.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2002-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tamaya.core.internal.resources.io;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Objects;
+
+/**
+ * {@link org.apache.tamaya.core.internal.resources.io.Resource} implementation for {@code java.net.URL} locators.
+ * Obviously supports resolution as URL, and also as File in case of
+ * the "file:" protocol.
+ *
+ * @author Juergen Hoeller
+ * @since 28.12.2003
+ * @see java.net.URL
+ */
+public class UrlResource extends AbstractFileResolvingResource {
+
+ /**
+ * Original URI, if available; used for URI and File access.
+ */
+ private final URI uri;
+
+ /**
+ * Original URL, used for actual access.
+ */
+ private final URL url;
+
+ /**
+ * Cleaned URL (with normalized path), used for comparisons.
+ */
+ private final URL cleanedUrl;
+
+
+ /**
+ * Create a new UrlResource based on the given URI object.
+ * @param uri a URI
+ * @throws MalformedURLException if the given URL path is not valid
+ */
+ public UrlResource(URI uri) throws MalformedURLException {
+ Objects.requireNonNull(uri, "URI must not be null");
+ this.uri = uri;
+ this.url = uri.toURL();
+ this.cleanedUrl = getCleanedUrl(this.url, uri.toString());
+ }
+
+ /**
+ * Create a new UrlResource based on the given URL object.
+ * @param url a URL
+ */
+ public UrlResource(URL url) {
+ Objects.requireNonNull(url, "URL must not be null");
+ this.url = url;
+ this.cleanedUrl = getCleanedUrl(this.url, url.toString());
+ this.uri = null;
+ }
+
+ /**
+ * Create a new UrlResource based on a URL path.
+ * <p>Note: The given path needs to be pre-encoded if necessary.
+ * @param path a URL path
+ * @throws MalformedURLException if the given URL path is not valid
+ * @see java.net.URL#URL(String)
+ */
+ public UrlResource(String path) throws MalformedURLException {
+ Objects.requireNonNull(path, "Path must not be null");
+ this.uri = null;
+ this.url = new URL(path);
+ this.cleanedUrl = getCleanedUrl(this.url, path);
+ }
+
+ /**
+ * Create a new UrlResource based on a URI specification.
+ * <p>The given parts will automatically get encoded if necessary.
+ * @param protocol the URL protocol to use (e.g. "jar" or "file" - without colon);
+ * also known as "scheme"
+ * @param location the location (e.g. the file path within that protocol);
+ * also known as "scheme-specific part"
+ * @throws MalformedURLException if the given URL specification is not valid
+ * @see java.net.URI#URI(String, String, String)
+ */
+ public UrlResource(String protocol, String location) throws MalformedURLException {
+ this(protocol, location, null);
+ }
+
+ /**
+ * Create a new UrlResource based on a URI specification.
+ * <p>The given parts will automatically get encoded if necessary.
+ * @param protocol the URL protocol to use (e.g. "jar" or "file" - without colon);
+ * also known as "scheme"
+ * @param location the location (e.g. the file path within that protocol);
+ * also known as "scheme-specific part"
+ * @param fragment the fragment within that location (e.g. anchor on an HTML page,
+ * as following after a "#" separator)
+ * @throws MalformedURLException if the given URL specification is not valid
+ * @see java.net.URI#URI(String, String, String)
+ */
+ public UrlResource(String protocol, String location, String fragment) throws MalformedURLException {
+ try {
+ this.uri = new URI(protocol, location, fragment);
+ this.url = this.uri.toURL();
+ this.cleanedUrl = getCleanedUrl(this.url, this.uri.toString());
+ }
+ catch (URISyntaxException ex) {
+ MalformedURLException exToThrow = new MalformedURLException(ex.getMessage());
+ exToThrow.initCause(ex);
+ throw exToThrow;
+ }
+ }
+
+ /**
+ * Determine a cleaned URL for the given original URL.
+ * @param originalUrl the original URL
+ * @param originalPath the original URL path
+ * @return the cleaned URL
+ */
+ private URL getCleanedUrl(URL originalUrl, String originalPath) {
+ try {
+ return new URL(StringUtils.cleanPath(originalPath));
+ }
+ catch (MalformedURLException ex) {
+ // Cleaned URL path cannot be converted to URL
+ // -> take original URL.
+ return originalUrl;
+ }
+ }
+
+
+ /**
+ * This implementation opens an InputStream for the given URL.
+ * It sets the "UseCaches" flag to {@code false},
+ * mainly to avoid jar file locking on Windows.
+ * @see java.net.URL#openConnection()
+ * @see java.net.URLConnection#setUseCaches(boolean)
+ * @see java.net.URLConnection#getInputStream()
+ */
+ @Override
+ public InputStream getInputStream()throws IOException {
+ URLConnection con = null;
+ try {
+ con = this.url.openConnection();
+ ResourceUtils.useCachesIfNecessary(con);
+ return con.getInputStream();
+ }
+ catch (IOException ex) {
+ // Close the HTTP connection (if applicable).
+ if (con instanceof HttpURLConnection) {
+ ((HttpURLConnection) con).disconnect();
+ }
+ throw ex;
+ }
+ }
+
+ /**
+ * This implementation returns the underlying URL reference.
+ */
+ @Override
+ public URL getURL() throws IOException {
+ return this.url;
+ }
+
+ /**
+ * This implementation returns the underlying URI directly,
+ * if possible.
+ */
+ @Override
+ public URI getURI() throws IOException {
+ if (this.uri != null) {
+ return this.uri;
+ }
+ else {
+ return super.getURI();
+ }
+ }
+
+ /**
+ * This implementation returns a File reference for the underlying URL/URI,
+ * provided that it refers to a file in the file system.
+ */
+ @Override
+ public File getFile() throws IOException {
+ if (this.uri != null) {
+ return super.getFile(this.uri);
+ }
+ else {
+ return super.getFile();
+ }
+ }
+
+ /**
+ * This implementation creates a UrlResource, applying the given path
+ * relative to the path of the underlying URL of this resource descriptor.
+ * @see java.net.URL#URL(java.net.URL, String)
+ */
+ @Override
+ public Resource createRelative(String relativePath) throws MalformedURLException {
+ if (relativePath.startsWith("/")) {
+ relativePath = relativePath.substring(1);
+ }
+ return new UrlResource(new URL(this.url, relativePath));
+ }
+
+ /**
+ * This implementation returns the name of the file that this URL refers to.
+ * @see java.net.URL#getFile()
+ * @see java.io.File#getName()
+ */
+ @Override
+ public String getFilename() {
+ return new File(this.url.getFile()).getName();
+ }
+
+ /**
+ * This implementation returns a description that includes the URL.
+ */
+ @Override
+ public String getDescription() {
+ return "URL [" + this.url + "]";
+ }
+
+
+ /**
+ * This implementation compares the underlying URL references.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ return (obj == this ||
+ (obj instanceof UrlResource && this.cleanedUrl.equals(((UrlResource) obj).cleanedUrl)));
+ }
+
+ /**
+ * This implementation returns the hash code of the underlying URL reference.
+ */
+ @Override
+ public int hashCode() {
+ return this.cleanedUrl.hashCode();
+ }
+
+}
+
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/resources/io/VfsResource.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/resources/io/VfsResource.java b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/VfsResource.java
new file mode 100644
index 0000000..06f509d
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/VfsResource.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tamaya.core.internal.resources.io;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+import java.util.Objects;
+
+/**
+ * JBoss VFS based {@link Resource} implementation.
+ *
+ * <p>As of Spring 4.0, this class supports VFS 3.x on JBoss AS 6+ (package
+ * {@code org.jboss.vfs}) and is in particular compatible with JBoss AS 7 and
+ * WildFly 8.
+ *
+ * @author Ales Justin
+ * @author Juergen Hoeller
+ * @author Costin Leau
+ * @since 3.0
+ */
+public class VfsResource extends AbstractResource {
+
+ private final Object resource;
+
+
+ public VfsResource(Object resource) {
+ Objects.requireNonNull(resource, "VirtualFile must not be null");
+ this.resource = resource;
+ }
+
+
+ @Override
+ public InputStream getInputStream()throws IOException {
+ return VfsUtils.getInputStream(this.resource);
+ }
+
+ @Override
+ public boolean exists() {
+ return VfsUtils.exists(this.resource);
+ }
+
+ @Override
+ public boolean isReadable() {
+ return VfsUtils.isReadable(this.resource);
+ }
+
+ @Override
+ public URL getURL() throws IOException {
+ try {
+ return VfsUtils.getURL(this.resource);
+ }
+ catch (Exception ex) {
+ throw new IllegalStateException("Failed to obtain URL for file " + this.resource, ex);
+ }
+ }
+
+ @Override
+ public URI getURI() throws IOException {
+ try {
+ return VfsUtils.getURI(this.resource);
+ }
+ catch (Exception ex) {
+ throw new IllegalStateException("Failed to obtain URI for " + this.resource, ex);
+ }
+ }
+
+ @Override
+ public File getFile() throws IOException {
+ return VfsUtils.getFile(this.resource);
+ }
+
+ @Override
+ public long contentLength() throws IOException {
+ return VfsUtils.getSize(this.resource);
+ }
+
+ @Override
+ public long lastModified() throws IOException {
+ return VfsUtils.getLastModified(this.resource);
+ }
+
+ @Override
+ public Resource createRelative(String relativePath) throws IOException {
+ if (!relativePath.startsWith(".") && relativePath.contains("/")) {
+ try {
+ return new VfsResource(VfsUtils.getChild(this.resource, relativePath));
+ }
+ catch (IOException ex) {
+ // fall back to getRelative
+ }
+ }
+
+ return new VfsResource(VfsUtils.getRelative(new URL(getURL(), relativePath)));
+ }
+
+ @Override
+ public String getFilename() {
+ return VfsUtils.getName(this.resource);
+ }
+
+ @Override
+ public String getDescription() {
+ return this.resource.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return (obj == this || (obj instanceof VfsResource && this.resource.equals(((VfsResource) obj).resource)));
+ }
+
+ @Override
+ public int hashCode() {
+ return this.resource.hashCode();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/resources/io/VfsUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/resources/io/VfsUtils.java b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/VfsUtils.java
new file mode 100644
index 0000000..ff2636f
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/VfsUtils.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tamaya.core.internal.resources.io;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.*;
+import java.net.URI;
+import java.net.URL;
+
+/**
+ * Utility for detecting and accessing JBoss VFS in the classpath.
+ *
+ * <p>As of Spring 4.0, this class supports VFS 3.x on JBoss AS 6+ (package
+ * {@code org.jboss.vfs}) and is in particular compatible with JBoss AS 7 and
+ * WildFly 8.
+ *
+ * <p>Thanks go to Marius Bogoevici for the initial patch.
+ * <b>Note:</b> This is an internal class and should not be used outside the framework.
+ *
+ * @author Costin Leau
+ * @author Juergen Hoeller
+ * @since 3.0.3
+ */
+class VfsUtils {
+
+ private static final String VFS3_PKG = "org.jboss.vfs.";
+ private static final String VFS_NAME = "VFS";
+
+ private static Method VFS_METHOD_GET_ROOT_URL = null;
+ private static Method VFS_METHOD_GET_ROOT_URI = null;
+
+ private static Method VIRTUAL_FILE_METHOD_EXISTS = null;
+ private static Method VIRTUAL_FILE_METHOD_GET_INPUT_STREAM;
+ private static Method VIRTUAL_FILE_METHOD_GET_SIZE;
+ private static Method VIRTUAL_FILE_METHOD_GET_LAST_MODIFIED;
+ private static Method VIRTUAL_FILE_METHOD_TO_URL;
+ private static Method VIRTUAL_FILE_METHOD_TO_URI;
+ private static Method VIRTUAL_FILE_METHOD_GET_NAME;
+ private static Method VIRTUAL_FILE_METHOD_GET_PATH_NAME;
+ private static Method VIRTUAL_FILE_METHOD_GET_CHILD;
+
+ protected static Class<?> VIRTUAL_FILE_VISITOR_INTERFACE;
+ protected static Method VIRTUAL_FILE_METHOD_VISIT;
+
+ private static Field VISITOR_ATTRIBUTES_FIELD_RECURSE = null;
+ private static Method GET_PHYSICAL_FILE = null;
+
+ static {
+ ClassLoader loader = VfsUtils.class.getClassLoader();
+ try {
+ Class<?> vfsClass = loader.loadClass(VFS3_PKG + VFS_NAME);
+ VFS_METHOD_GET_ROOT_URL = ReflectionUtils.findMethod(vfsClass, "getChild", URL.class);
+ VFS_METHOD_GET_ROOT_URI = ReflectionUtils.findMethod(vfsClass, "getChild", URI.class);
+
+ Class<?> virtualFile = loader.loadClass(VFS3_PKG + "VirtualFile");
+ VIRTUAL_FILE_METHOD_EXISTS = ReflectionUtils.findMethod(virtualFile, "exists");
+ VIRTUAL_FILE_METHOD_GET_INPUT_STREAM = ReflectionUtils.findMethod(virtualFile, "openStream");
+ VIRTUAL_FILE_METHOD_GET_SIZE = ReflectionUtils.findMethod(virtualFile, "getSize");
+ VIRTUAL_FILE_METHOD_GET_LAST_MODIFIED = ReflectionUtils.findMethod(virtualFile, "getLastModified");
+ VIRTUAL_FILE_METHOD_TO_URI = ReflectionUtils.findMethod(virtualFile, "toURI");
+ VIRTUAL_FILE_METHOD_TO_URL = ReflectionUtils.findMethod(virtualFile, "toURL");
+ VIRTUAL_FILE_METHOD_GET_NAME = ReflectionUtils.findMethod(virtualFile, "getName");
+ VIRTUAL_FILE_METHOD_GET_PATH_NAME = ReflectionUtils.findMethod(virtualFile, "getPathName");
+ GET_PHYSICAL_FILE = ReflectionUtils.findMethod(virtualFile, "getPhysicalFile");
+ VIRTUAL_FILE_METHOD_GET_CHILD = ReflectionUtils.findMethod(virtualFile, "getChild", String.class);
+
+ VIRTUAL_FILE_VISITOR_INTERFACE = loader.loadClass(VFS3_PKG + "VirtualFileVisitor");
+ VIRTUAL_FILE_METHOD_VISIT = ReflectionUtils.findMethod(virtualFile, "visit", VIRTUAL_FILE_VISITOR_INTERFACE);
+
+ Class<?> visitorAttributesClass = loader.loadClass(VFS3_PKG + "VisitorAttributes");
+ VISITOR_ATTRIBUTES_FIELD_RECURSE = ReflectionUtils.findField(visitorAttributesClass, "RECURSE");
+ }
+ catch (ClassNotFoundException ex) {
+ throw new IllegalStateException("Could not detect JBoss VFS infrastructure", ex);
+ }
+ }
+
+ private VfsUtils(){}
+
+ static void visit(Object resource, InvocationHandler visitor) throws IOException {
+ Object visitorProxy = Proxy.newProxyInstance(
+ VIRTUAL_FILE_VISITOR_INTERFACE.getClassLoader(),
+ new Class<?>[]{VIRTUAL_FILE_VISITOR_INTERFACE}, visitor);
+ invokeVfsMethod(VIRTUAL_FILE_METHOD_VISIT, resource, visitorProxy);
+ }
+
+ protected static Object invokeVfsMethod(Method method, Object target, Object... args) throws IOException {
+ try {
+ return method.invoke(target, args);
+ }
+ catch (InvocationTargetException ex) {
+ Throwable targetEx = ex.getTargetException();
+ if (targetEx instanceof IOException) {
+ throw (IOException) targetEx;
+ }
+ ReflectionUtils.handleInvocationTargetException(ex);
+ }
+ catch (Exception ex) {
+ ReflectionUtils.handleReflectionException(ex);
+ }
+
+ throw new IllegalStateException("Invalid code path reached");
+ }
+
+ static boolean exists(Object vfsResource) {
+ try {
+ return (Boolean) invokeVfsMethod(VIRTUAL_FILE_METHOD_EXISTS, vfsResource);
+ }
+ catch (IOException ex) {
+ return false;
+ }
+ }
+
+ static boolean isReadable(Object vfsResource) {
+ try {
+ return ((Long) invokeVfsMethod(VIRTUAL_FILE_METHOD_GET_SIZE, vfsResource) > 0);
+ }
+ catch (IOException ex) {
+ return false;
+ }
+ }
+
+ static long getSize(Object vfsResource) throws IOException {
+ return (Long) invokeVfsMethod(VIRTUAL_FILE_METHOD_GET_SIZE, vfsResource);
+ }
+
+ static long getLastModified(Object vfsResource) throws IOException {
+ return (Long) invokeVfsMethod(VIRTUAL_FILE_METHOD_GET_LAST_MODIFIED, vfsResource);
+ }
+
+ static InputStream getInputStream(Object vfsResource) throws IOException {
+ return (InputStream) invokeVfsMethod(VIRTUAL_FILE_METHOD_GET_INPUT_STREAM, vfsResource);
+ }
+
+ static URL getURL(Object vfsResource) throws IOException {
+ return (URL) invokeVfsMethod(VIRTUAL_FILE_METHOD_TO_URL, vfsResource);
+ }
+
+ static URI getURI(Object vfsResource) throws IOException {
+ return (URI) invokeVfsMethod(VIRTUAL_FILE_METHOD_TO_URI, vfsResource);
+ }
+
+ static String getName(Object vfsResource) {
+ try {
+ return (String) invokeVfsMethod(VIRTUAL_FILE_METHOD_GET_NAME, vfsResource);
+ }
+ catch (IOException ex) {
+ throw new IllegalStateException("Cannot get resource name", ex);
+ }
+ }
+
+ static Object getRelative(URL url) throws IOException {
+ return invokeVfsMethod(VFS_METHOD_GET_ROOT_URL, null, url);
+ }
+
+ static Object getChild(Object vfsResource, String path) throws IOException {
+ return invokeVfsMethod(VIRTUAL_FILE_METHOD_GET_CHILD, vfsResource, path);
+ }
+
+ static File getFile(Object vfsResource) throws IOException {
+ return (File) invokeVfsMethod(GET_PHYSICAL_FILE, vfsResource);
+ }
+
+ static Object getRoot(URI url) throws IOException {
+ return invokeVfsMethod(VFS_METHOD_GET_ROOT_URI, null, url);
+ }
+
+ // protected methods used by the support sub-package
+
+ protected static Object getRoot(URL url) throws IOException {
+ return invokeVfsMethod(VFS_METHOD_GET_ROOT_URL, null, url);
+ }
+
+ protected static Object getVisitorAttribute() {
+ try{
+ return VISITOR_ATTRIBUTES_FIELD_RECURSE.get(null);
+ }
+ catch(Exception e){
+ ReflectionUtils.handleReflectionException(e);
+ return null; // never called
+ }
+ }
+
+ protected static String getPath(Object resource) {
+ try{
+ return (String) VIRTUAL_FILE_METHOD_GET_PATH_NAME.invoke(resource);
+ }
+ catch(Exception e){
+ ReflectionUtils.handleReflectionException(e);
+ return null; // never called
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/resources/io/WritableResource.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/resources/io/WritableResource.java b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/WritableResource.java
new file mode 100644
index 0000000..26fe268
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/WritableResource.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tamaya.core.internal.resources.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Extended interface for a resource that supports writing to it.
+ * Provides an {@link #getOutputStream() OutputStream accessor}.
+ *
+ * @author Juergen Hoeller
+ * @since 3.1
+ * @see java.io.OutputStream
+ */
+public interface WritableResource extends Resource {
+
+ /**
+ * Return whether the contents of this resource can be modified,
+ * e.g. via {@link #getOutputStream()} or {@link #getFile()}.
+ * <p>Will be {@code true} for typical resource descriptors;
+ * note that actual content writing may still fail when attempted.
+ * However, a value of {@code false} is a definitive indication
+ * that the resource content cannot be modified.
+ * @see #getOutputStream()
+ * @see #isReadable()
+ */
+ boolean isWritable();
+
+ /**
+ * Return an {@link OutputStream} for the underlying resource,
+ * allowing to (over-)write its content.
+ * @throws IOException if the stream could not be opened
+ * @see #getInputStream()
+ */
+ OutputStream getOutputStream() throws IOException;
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/properties/AbstractPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/AbstractPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/properties/AbstractPropertyProvider.java
index 8134db8..bb1d3ce 100644
--- a/core/src/main/java/org/apache/tamaya/core/properties/AbstractPropertyProvider.java
+++ b/core/src/main/java/org/apache/tamaya/core/properties/AbstractPropertyProvider.java
@@ -27,7 +27,7 @@ import java.util.*;
/**
* Abstract base class for implementing a {@link org.apache.tamaya.PropertyProvider}. Also
- * consider using {@link MapBasedPropertyProvider} instead of.
+ * consider using {@link org.apache.tamaya.core.internal.MapBasedPropertyProvider} instead of.
*/
@SuppressWarnings("NullableProblems")
public abstract class AbstractPropertyProvider implements PropertyProvider, Serializable{
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/properties/AggregatedPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/AggregatedPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/properties/AggregatedPropertyProvider.java
deleted file mode 100644
index 91ddf2b..0000000
--- a/core/src/main/java/org/apache/tamaya/core/properties/AggregatedPropertyProvider.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.tamaya.core.properties;
-
-import org.apache.tamaya.ConfigChangeSet;
-import org.apache.tamaya.MetaInfoBuilder;
-import org.apache.tamaya.PropertyProvider;
-import java.util.*;
-
-/**
- * Implementation for a {@link org.apache.tamaya.PropertyProvider} that is an aggregate of
- * multiple child instances. Controlled by an {@link AggregationPolicy} the
- * following aggregations are supported:
- * <ul>
- * <li><b>IGNORE: </b>Ignore all overrides.</li>
- * <li><b>: </b></li>
- * <li><b>: </b></li>
- * <li><b>: </b></li>
- * </ul>
- */
-class AggregatedPropertyProvider extends AbstractPropertyProvider{
-
- private static final long serialVersionUID = -1419376385695224799L;
- private AggregationPolicy policy = AggregationPolicy.IGNORE;
- private List<PropertyProvider> units = new ArrayList<PropertyProvider>();
- private PropertyProvider mutableProvider;
-
- /**
- * Creates a new instance.
- * @param mutableProvider the provider instance that would be used for delegating
- * change requests.
- * @param policy
- * The aggregation policy to be used.
- * @param propertyMaps
- * The property sets to be included.
- */
- public AggregatedPropertyProvider(PropertyProvider mutableProvider, AggregationPolicy policy, PropertyProvider... propertyMaps) {
- super(MetaInfoBuilder.of().setType("aggregated").set("policy", policy.toString()).build());
- Objects.requireNonNull(policy);
- this.policy = policy;
- units.addAll(Arrays.asList(propertyMaps));
- this.mutableProvider = mutableProvider;
- }
-
- /**
- * Get the {@link AggregationPolicy} for this instance.
- *
- * @return the {@link AggregationPolicy}, never {@code null}.
- */
- public AggregationPolicy getAggregationPolicy() {
- return policy;
- }
-
- /**
- * Return the names of the {@link org.apache.tamaya.PropertyProvider} instances to be
- * aggregated in this instance, in the order of precedence (the first are
- * the weakest).
- *
- * @return the ordered list of aggregated scope identifiers, never
- * {@code null}.
- */
- public List<PropertyProvider> getConfigurationUnits() {
- return Collections.unmodifiableList(units);
- }
-
- /**
- * Apply a config change to this item. Hereby the change must be related to the same instance.
- * @param change the config change
- * @throws org.apache.tamaya.ConfigException if an unrelated change was passed.
- * @throws UnsupportedOperationException when the configuration is not writable.
- */
- @Override
- public void apply(ConfigChangeSet change){
- if(mutableProvider!=null)
- mutableProvider.apply(change);
- else
- super.apply(change);
- }
-
- @Override
- public Map<String,String> toMap() {
- Map<String, String> value = new HashMap<>();
- for (PropertyProvider unit : units) {
- for (Map.Entry<String, String> entry : unit.toMap()
- .entrySet()) {
- switch (policy) {
- case IGNORE:
- if (!value.containsKey(entry.getKey())) {
- value.put(entry.getKey(), entry.getValue());
- }
- break;
- case EXCEPTION:
- if (value.containsKey(entry.getKey())) {
- throw new IllegalStateException("Duplicate key: "
- + entry.getKey()
- + " in " + this);
- }
- else {
- value.put(entry.getKey(), entry.getValue());
- }
- break;
- case OVERRIDE:
- value.put(entry.getKey(), entry.getValue());
- break;
- default:
- break;
- }
- }
- }
- return value;
- }
-
- @Override
- public ConfigChangeSet load() {
- for (PropertyProvider unit : units) {
- unit.load();
- }
- return super.load();
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/properties/AggregationPolicy.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/AggregationPolicy.java b/core/src/main/java/org/apache/tamaya/core/properties/AggregationPolicy.java
deleted file mode 100644
index 576652f..0000000
--- a/core/src/main/java/org/apache/tamaya/core/properties/AggregationPolicy.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.tamaya.core.properties;
-
-/**
- * Policy that defines how the different aggregates should be combined.
- */
-public enum AggregationPolicy{
- /** Ignore overrides, only extend (additive). */
- IGNORE,
- /**
- * Interpret later keys as override (additive and override), replacing
- * the key loaded earlier/from previous contained
- * {@link javax.config.PropertyMap}.
- */
- OVERRIDE,
- /**
- * Throw an exception, when keys are not disjunctive (strictly
- * additive).
- */
- EXCEPTION
-}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/properties/ContextualPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/ContextualPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/properties/ContextualPropertyProvider.java
deleted file mode 100644
index 1caa234..0000000
--- a/core/src/main/java/org/apache/tamaya/core/properties/ContextualPropertyProvider.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.tamaya.core.properties;
-
-import org.apache.tamaya.ConfigChangeSet;
-import org.apache.tamaya.MetaInfo;
-import org.apache.tamaya.MetaInfoBuilder;
-import org.apache.tamaya.PropertyProvider;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Supplier;
-
-/**
- * Created by Anatole on 12.04.2014.
- */
-class ContextualPropertyProvider implements PropertyProvider{
-
- private volatile Map<String,PropertyProvider> cachedMaps = new ConcurrentHashMap<>();
-
- private Supplier<PropertyProvider> mapSupplier;
- private Supplier<String> isolationKeySupplier;
- private MetaInfo metaInfo;
-
- /**
- * Creates a new contextual PropertyMap. Contextual maps delegate to different instances of PropertyMap depending
- * on the keys returned from the isolationP
- *
- * @param mapSupplier
- * @param isolationKeySupplier
- */
- public ContextualPropertyProvider(Supplier<PropertyProvider> mapSupplier, Supplier<String> isolationKeySupplier){
- this.metaInfo = MetaInfoBuilder.of().setType("contextual").set("mapSupplier", mapSupplier.toString())
- .set("isolationKeySupplier", isolationKeySupplier.toString()).build();
- Objects.requireNonNull(mapSupplier);
- Objects.requireNonNull(isolationKeySupplier);
- this.mapSupplier = mapSupplier;
- this.isolationKeySupplier = isolationKeySupplier;
- }
-
- /**
- * This method provides the contextual Map for the current environment. Hereby, ba default, for each different
- * key returned by the #isolationKeySupplier a separate PropertyMap instance is acquired from the #mapSupplier.
- * If the map supplier returns an instance it is cached in the local #cachedMaps.
- *
- * @return the current contextual PropertyMap.
- */
- protected PropertyProvider getContextualMap(){
- String environmentKey = this.isolationKeySupplier.get();
- if(environmentKey == null){
- return PropertyProviders.empty();
- }
- PropertyProvider map = this.cachedMaps.get(environmentKey);
- if(map == null){
- synchronized(cachedMaps){
- map = this.cachedMaps.get(environmentKey);
- if(map == null){
- map = this.mapSupplier.get();
- if(map == null){
- return PropertyProviders
- .empty(MetaInfoBuilder.of().setInfo(
- "No map provided by supplier " + mapSupplier + " for key " + environmentKey)
- .build());
- }
- this.cachedMaps.put(environmentKey, map);
- }
- }
- }
- return map;
- }
-
- @Override
- public ConfigChangeSet load(){
- return getContextualMap().load();
- }
-
- @Override
- public boolean containsKey(String key){
- return getContextualMap().containsKey(key);
- }
-
- @Override
- public Map<String,String> toMap(){
- return getContextualMap().toMap();
- }
-
- @Override
- public MetaInfo getMetaInfo(){
- return this.metaInfo;
- }
-
- @Override
- public Optional<String> get(String key){
- return getContextualMap().get(key);
- }
-
- @Override
- public Set<String> keySet(){
- return getContextualMap().keySet();
- }
-
- /**
- * Apply a config change to this item. Hereby the change must be related to the same instance.
- * @param change the config change
- * @throws org.apache.tamaya.ConfigException if an unrelated change was passed.
- * @throws UnsupportedOperationException when the configuration is not writable.
- */
- @Override
- public void apply(ConfigChangeSet change){
- getContextualMap().apply(change);
- }
-
- /**
- * Access a cached PropertyMap.
- *
- * @param key the target environment key as returned by the environment key supplier, not null.
- * @return the corresponding PropertyMap, or null.
- */
- public PropertyProvider getCachedMap(String key){
- return this.cachedMaps.get(key);
- }
-
- /**
- * Access the set of currently loaded/cached maps.
- *
- * @return the set of cached map keys, never null.
- */
- public Set<String> getCachedMapKeys(){
- return this.cachedMaps.keySet();
- }
-
- /**
- * Access the supplier for environment key, determining map isolation.
- *
- * @return the environment key supplier instance, not null.
- */
- public Supplier<String> getIsolationKeySupplier(){
- return this.isolationKeySupplier;
- }
-
- /**
- * Access the supplier for new PropertyMap instances.
- *
- * @return the PropertyMap supplier instance, not null.
- */
- public Supplier<PropertyProvider> getMapSupplier(){
- return this.mapSupplier;
- }
-
- @Override
- public String toString(){
- return "ContextualMap{" +
- "cachedMaps(key)=" + cachedMaps.keySet() +
- ", mapSupplier=" + mapSupplier +
- ", isolationKeySupplier=" + isolationKeySupplier +
- '}';
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/properties/DelegatingPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/DelegatingPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/properties/DelegatingPropertyProvider.java
deleted file mode 100644
index 69c4768..0000000
--- a/core/src/main/java/org/apache/tamaya/core/properties/DelegatingPropertyProvider.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.tamaya.core.properties;
-
-import org.apache.tamaya.ConfigChangeSet;
-import org.apache.tamaya.MetaInfo;
-import org.apache.tamaya.MetaInfoBuilder;
-import org.apache.tamaya.PropertyProvider;
-
-import java.util.*;
-
-/**
- * Implementation for a {@link org.apache.tamaya.PropertyProvider} that is an aggregate of
- * multiple child instances. Controlled by an {@link org.apache.tamaya.core.properties.AggregationPolicy} the
- * following aggregations are supported:
- * <ul>
- * <li><b>IGNORE: </b>Ignore all overrides.</li>
- * <li><b>: </b></li>
- * <li><b>: </b></li>
- * <li><b>: </b></li>
- * </ul>
- */
-class DelegatingPropertyProvider implements PropertyProvider{
-
- private static final long serialVersionUID = -1419376385695224799L;
- private PropertyProvider mainMap;
- private Map<String,String> parentMap;
- private MetaInfo metaInfo;
-
- /**
- * Creates a mew instance, with aggregation polilcy
- * {@code AggregationPolicy.OVERRIDE}.
- *
- * @param mainMap The main ConfigMap.
- * @param parentMap The delegated parent ConfigMap.
- */
- public DelegatingPropertyProvider(PropertyProvider mainMap, Map<String,String> parentMap){
- this.metaInfo =
- MetaInfoBuilder.of().setType("delegate").set("provider", mainMap.toString()).set("delegate", parentMap.toString())
- .build();
- Objects.requireNonNull(parentMap);
- this.parentMap = parentMap;
- Objects.requireNonNull(parentMap);
- this.parentMap = parentMap;
- }
-
- @Override
- public ConfigChangeSet load(){
- return mainMap.load();
- }
-
- @Override
- public boolean containsKey(String key){
- return keySet().contains(key);
- }
-
- @Override
- public Map<String,String> toMap(){
- return null;
- }
-
- @Override
- public MetaInfo getMetaInfo(){
- return this.metaInfo;
- }
-
- @Override
- public Optional<String> get(String key){
- Optional<String> val = mainMap.get(key);
- if(!val.isPresent()){
- return Optional.ofNullable(parentMap.get(key));
- }
- return val;
- }
-
- @Override
- public Set<String> keySet(){
- Set<String> keys = new HashSet<>(mainMap.keySet());
- keys.addAll(parentMap.keySet());
- return keys;
- }
-
- @Override
- public String toString(){
- return super.toString() + "(mainMap=" + mainMap + ", delegate=" + parentMap + ")";
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/properties/EnvironmentPropertyProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/properties/EnvironmentPropertyProvider.java b/core/src/main/java/org/apache/tamaya/core/properties/EnvironmentPropertyProvider.java
deleted file mode 100644
index 10927c6..0000000
--- a/core/src/main/java/org/apache/tamaya/core/properties/EnvironmentPropertyProvider.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.tamaya.core.properties;
-
-import org.apache.tamaya.MetaInfoBuilder;
-import java.util.Map;
-
-class EnvironmentPropertyProvider extends AbstractPropertyProvider{
-
- private static final long serialVersionUID = 4753258482658331010L;
-
- public Map<String,String> toMap(){
- return System.getenv();
- }
-
- public EnvironmentPropertyProvider(){
- super(MetaInfoBuilder.of().setType("env-properties").build());
- }
-
- @Override
- public String toString(){
- return "EnvironmentPropertyMap{" +
- "props=" + super.toString() +
- '}';
- }
-
-}
[4/8] incubator-tamaya git commit: TAMAYA-14: added resource support.
TAMAYA-15: Moved PropertyProviders to API,
added SPI. TAMAYA-8: Added/improved Javadoc.
Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/resources/io/PathMatchingResourcePatternResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/resources/io/PathMatchingResourcePatternResolver.java b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/PathMatchingResourcePatternResolver.java
new file mode 100644
index 0000000..caab938
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/PathMatchingResourcePatternResolver.java
@@ -0,0 +1,728 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tamaya.core.internal.resources.io;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import java.lang.reflect.InvocationHandler;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URLClassLoader;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * A {@code ResourcePatternResolver} implementation that is able to resolve a
+ * specified resource location path into one or more matching Resources.
+ * The source path may be a simple path which has a one-to-one mapping to a
+ * target {@code org.springframework.core.io.Resource}, or alternatively
+ * may contain the special "{@code classpath*:}" prefix and/or
+ * internal Ant-style regular expressions (matched using Spring's
+ * {@code org.springframework.util.AntPathMatcher} utility).
+ * Both of the latter are effectively wildcards.
+ *
+ * <p><b>No Wildcards:</b>
+ *
+ * <p>In the simple case, if the specified location path does not start with the
+ * {@code "classpath*:}" prefix, and does not contain a PathMatcher pattern,
+ * this resolver will simply return a single resource via a
+ * {@code getResource()} call on the underlying {@code ResourceLoader}.
+ * Examples are real URLs such as "{@code file:C:/context.xml}", pseudo-URLs
+ * such as "{@code classpath:/context.xml}", and simple unprefixed paths
+ * such as "{@code /WEB-INF/context.xml}". The latter will resolve in a
+ * fashion specific to the underlying {@code ResourceLoader} (e.g.
+ * {@code ServletContextResource} for a {@code WebApplicationContext}).
+ *
+ * <p><b>Ant-style Patterns:</b>
+ *
+ * <p>When the path location contains an Ant-style pattern, e.g.:
+ * <pre class="code">
+ * /WEB-INF/*-context.xml
+ * com/mycompany/**/applicationContext.xml
+ * file:C:/some/path/*-context.xml
+ * classpath:com/mycompany/**/applicationContext.xml</pre>
+ * the resolver follows a more complex but defined procedure to try to resolve
+ * the wildcard. It produces a {@code Resource} for the path up to the last
+ * non-wildcard segment and obtains a {@code URL} from it. If this URL is
+ * not a "{@code jar:}" URL or container-specific variant (e.g.
+ * "{@code zip:}" in WebLogic, "{@code wsjar}" in WebSphere", etc.),
+ * then a {@code java.io.File} is obtained from it, and used to resolve the
+ * wildcard by walking the filesystem. In the case of a jar URL, the resolver
+ * either gets a {@code java.net.JarURLConnection} from it, or manually parses
+ * the jar URL, and then traverses the contents of the jar file, to resolve the
+ * wildcards.
+ *
+ * <p><b>Implications on portability:</b>
+ *
+ * <p>If the specified path is already a file URL (either explicitly, or
+ * implicitly because the base {@code ResourceLoader} is a filesystem one,
+ * then wildcarding is guaranteed to work in a completely portable fashion.
+ *
+ * <p>If the specified path is a classpath location, then the resolver must
+ * obtain the last non-wildcard path segment URL via a
+ * {@code Classloader.getResource()} call. Since this is just a
+ * node of the path (not the file at the end) it is actually undefined
+ * (in the ClassLoader Javadocs) exactly what sort of a URL is returned in
+ * this case. In practice, it is usually a {@code java.io.File} representing
+ * the directory, where the classpath resource resolves to a filesystem
+ * location, or a jar URL of some sort, where the classpath resource resolves
+ * to a jar location. Still, there is a portability concern on this operation.
+ *
+ * <p>If a jar URL is obtained for the last non-wildcard segment, the resolver
+ * must be able to get a {@code java.net.JarURLConnection} from it, or
+ * manually parse the jar URL, to be able to walk the contents of the jar,
+ * and resolve the wildcard. This will work in most environments, but will
+ * fail in others, and it is strongly recommended that the wildcard
+ * resolution of resources coming from jars be thoroughly tested in your
+ * specific environment before you rely on it.
+ *
+ * <p><b>{@code classpath*:} Prefix:</b>
+ *
+ * <p>There is special support for retrieving multiple class path resources with
+ * the same name, via the "{@code classpath*:}" prefix. For example,
+ * "{@code classpath*:META-INF/beans.xml}" will find all "beans.xml"
+ * files in the class path, be it in "classes" directories or in JAR files.
+ * This is particularly useful for autodetecting config files of the same name
+ * at the same location within each jar file. Internally, this happens via a
+ * {@code ClassLoader.getResources()} call, and is completely portable.
+ *
+ * <p>The "classpath*:" prefix can also be combined with a PathMatcher pattern in
+ * the rest of the location path, for example "classpath*:META-INF/*-beans.xml".
+ * In this case, the resolution strategy is fairly simple: a
+ * {@code ClassLoader.getResources()} call is used on the last non-wildcard
+ * path segment to get all the matching resources in the class loader hierarchy,
+ * and then off each resource the same PathMatcher resolution strategy described
+ * above is used for the wildcard subpath.
+ *
+ * <p><b>Other notes:</b>
+ *
+ * <p><b>WARNING:</b> Note that "{@code classpath*:}" when combined with
+ * Ant-style patterns will only work reliably with at least one root directory
+ * before the pattern starts, unless the actual target files reside in the file
+ * system. This means that a pattern like "{@code classpath*:*.xml}" will
+ * <i>not</i> retrieve files from the root of jar files but rather only from the
+ * root of expanded directories. This originates from a limitation in the JDK's
+ * {@code ClassLoader.getResources()} method which only returns file system
+ * locations for a passed-in empty String (indicating potential roots to search).
+ *
+ * <p><b>WARNING:</b> Ant-style patterns with "classpath:" resources are not
+ * guaranteed to find matching resources if the root package to search is available
+ * in multiple class path locations. This is because a resource such as
+ * <pre class="code">
+ * com/mycompany/package1/service-context.xml
+ * </pre>
+ * may be in only one location, but when a path such as
+ * <pre class="code">
+ * classpath:com/mycompany/**/service-context.xml
+ * </pre>
+ * is used to try to resolve it, the resolver will work off the (first) URL
+ * returned by {@code getResource("com/mycompany");}. If this base package
+ * node exists in multiple classloader locations, the actual end resource may
+ * not be underneath. Therefore, preferably, use "{@code classpath*:}" with the same
+ * Ant-style pattern in such a case, which will search <i>all</i> class path
+ * locations that contain the root package.
+ *
+ * @author Juergen Hoeller
+ * @author Colin Sampaleanu
+ * @author Marius Bogoevici
+ * @author Costin Leau
+ * @since 1.0.2
+ * @see ClassLoader#getResources(String)
+ */
+public final class PathMatchingResourcePatternResolver{
+
+ private static final Logger logger = Logger.getLogger(PathMatchingResourcePatternResolver.class.getName());
+ private static final java.lang.String CLASSPATH_ALL_URL_PREFIX = "classpath:";
+
+ private static Method equinoxResolveMethod;
+
+ static {
+ try {
+ // Detect Equinox OSGi (e.g. on WebSphere 6.1)
+ Class<?> fileLocatorClass = ClassUtils.forName("org.eclipse.core.runtime.FileLocator",
+ PathMatchingResourcePatternResolver.class.getClassLoader());
+ equinoxResolveMethod = fileLocatorClass.getMethod("resolve", URL.class);
+ logger.finest("Found Equinox FileLocator for OSGi bundle URL resolution");
+ }
+ catch (Throwable ex) {
+ equinoxResolveMethod = null;
+ }
+ }
+
+
+ private final DefaultResourceLoader resourceLoader;
+
+ private AntPathMatcher pathMatcher = new AntPathMatcher();
+
+ private static Map<ClassLoader, PathMatchingResourcePatternResolver> resolvers = new ConcurrentHashMap<>();
+
+ public static PathMatchingResourcePatternResolver of(ClassLoader loader){
+ return resolvers.computeIfAbsent(loader, (cl) -> new PathMatchingResourcePatternResolver(cl));
+ }
+
+ /**
+ * Create a new PathMatchingResourcePatternResolver.
+ * <p>ClassLoader access will happen via the thread context class loader.
+ */
+ public PathMatchingResourcePatternResolver() {
+ this.resourceLoader = new DefaultResourceLoader();
+ }
+
+ /**
+ * Create a new PathMatchingResourcePatternResolver with a DefaultResourceLoader.
+ * @param classLoader the ClassLoader to load classpath resources with,
+ * or {@code null} for using the thread context class loader
+ * at the time of actual resource access
+ * @see DefaultResourceLoader
+ */
+ public PathMatchingResourcePatternResolver(ClassLoader classLoader) {
+ this.resourceLoader = new DefaultResourceLoader(classLoader);
+ }
+
+ public ClassLoader getClassLoader() {
+ return resourceLoader.getClassLoader();
+ }
+
+ /**
+ * Return the PathMatcher that this resource pattern resolver uses.
+ */
+ public AntPathMatcher getPathMatcher() {
+ return this.pathMatcher;
+ }
+
+ public Resource getResource(String location) {
+ return resourceLoader.getResource(location);
+ }
+
+ public Resource[] getResources(String locationPattern) throws IOException {
+ Objects.requireNonNull(locationPattern, "Location pattern must not be null");
+ if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
+ // a class path resource (multiple resources for same name possible)
+ if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
+ // a class path resource pattern
+ return findPathMatchingResources(locationPattern);
+ }
+ else {
+ // all class path resources with the given name
+ return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
+ }
+ }
+ else {
+ // Only look for a pattern after a prefix here
+ // (to not get fooled by a pattern symbol in a strange prefix).
+ int prefixEnd = locationPattern.indexOf(":") + 1;
+ if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
+ // a file pattern
+ return findPathMatchingResources(locationPattern);
+ }
+ else {
+ // a single resource with the given name
+ return new Resource[] {this.resourceLoader.getResource(locationPattern)};
+ }
+ }
+ }
+
+ /**
+ * Find all class location resources with the given location via the ClassLoader.
+ * Delegates to {@link #doFindAllClassPathResources(String)}.
+ * @param location the absolute path within the classpath
+ * @return the result as Resource array
+ * @throws IOException in case of I/O errors
+ * @see java.lang.ClassLoader#getResources
+ * @see #convertClassLoaderURL
+ */
+ protected Resource[] findAllClassPathResources(String location) throws IOException {
+ String path = location;
+ if (path.startsWith("/")) {
+ path = path.substring(1);
+ }
+ Set<Resource> result = doFindAllClassPathResources(path);
+ return result.toArray(new Resource[result.size()]);
+ }
+
+ /**
+ * Find all class location resources with the given path via the ClassLoader.
+ * Called by {@link #findAllClassPathResources(String)}.
+ * @param path the absolute path within the classpath (never a leading slash)
+ * @return a mutable Set of matching Resource instances
+ */
+ protected Set<Resource> doFindAllClassPathResources(String path) throws IOException {
+ Set<Resource> result = new LinkedHashSet<Resource>(16);
+ ClassLoader cl = getClassLoader();
+ Enumeration<URL> resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path));
+ while (resourceUrls.hasMoreElements()) {
+ URL url = resourceUrls.nextElement();
+ result.add(convertClassLoaderURL(url));
+ }
+ if ("".equals(path)) {
+ // The above result is likely to be incomplete, i.e. only containing file system references.
+ // We need to have pointers to each of the jar files on the classpath as well...
+ addAllClassLoaderJarRoots(cl, result);
+ }
+ return result;
+ }
+
+ /**
+ * Convert the given URL as returned from the ClassLoader into a {@link Resource}.
+ * <p>The default implementation simply creates a {@link UrlResource} instance.
+ * @param url a URL as returned from the ClassLoader
+ * @return the corresponding Resource object
+ * @see java.lang.ClassLoader#getResources
+ * @see Resource
+ */
+ protected Resource convertClassLoaderURL(URL url) {
+ return new UrlResource(url);
+ }
+
+ /**
+ * Search all {@link URLClassLoader} URLs for jar file references and add them to the
+ * given set of resources in the form of pointers to the root of the jar file content.
+ * @param classLoader the ClassLoader to search (including its ancestors)
+ * @param result the set of resources to add jar roots to
+ */
+ protected void addAllClassLoaderJarRoots(ClassLoader classLoader, Set<Resource> result) {
+ if (classLoader instanceof URLClassLoader) {
+ try {
+ for (URL url : ((URLClassLoader) classLoader).getURLs()) {
+ if (ResourceUtils.isJarFileURL(url)) {
+ try {
+ UrlResource jarResource = new UrlResource(
+ ResourceUtils.JAR_URL_PREFIX + url.toString() + ResourceUtils.JAR_URL_SEPARATOR);
+ if (jarResource.exists()) {
+ result.add(jarResource);
+ }
+ }
+ catch (MalformedURLException ex) {
+ logger.finest(() -> "Cannot search for matching files underneath [" + url +
+ "] because it cannot be converted to a valid 'jar:' URL: " + ex.getMessage());
+ }
+ }
+ }
+ }
+ catch (Exception ex) {
+ logger.finest(() -> "Cannot introspect jar files since ClassLoader [" + classLoader +
+ "] does not support 'getURLs()': " + ex);
+ }
+ }
+ if (classLoader != null) {
+ try {
+ addAllClassLoaderJarRoots(classLoader.getParent(), result);
+ }
+ catch (Exception ex) {
+ logger.finest(() -> "Cannot introspect jar files in parent ClassLoader since [" + classLoader +
+ "] does not support 'getParent()': " + ex);
+ }
+ }
+ }
+
+ /**
+ * Find all resources that match the given location pattern via the
+ * Ant-style PathMatcher. Supports resources in jar files and zip files
+ * and in the file system.
+ * @param locationPattern the location pattern to match
+ * @return the result as Resource array
+ * @throws IOException in case of I/O errors
+ * @see #doFindPathMatchingJarResources
+ * @see #doFindPathMatchingFileResources
+ */
+ protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
+ String rootDirPath = determineRootDir(locationPattern);
+ String subPattern = locationPattern.substring(rootDirPath.length());
+ Resource[] rootDirResources = getResources(rootDirPath);
+ Set<Resource> result = new LinkedHashSet<Resource>(16);
+ for (Resource rootDirResource : rootDirResources) {
+ rootDirResource = resolveRootDirResource(rootDirResource);
+ if (rootDirResource.getURL().getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
+ result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirResource, subPattern, getPathMatcher()));
+ }
+ else if (isJarResource(rootDirResource)) {
+ result.addAll(doFindPathMatchingJarResources(rootDirResource, subPattern));
+ }
+ else {
+ result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
+ }
+ }
+ logger.finest(() -> "Resolved location pattern [" + locationPattern + "] to resources " + result);
+ return result.toArray(new Resource[result.size()]);
+ }
+
+ /**
+ * Determine the root directory for the given location.
+ * <p>Used for determining the starting point for file matching,
+ * resolving the root directory location to a {@code java.io.File}
+ * and passing it into {@code retrieveMatchingFiles}, with the
+ * remainder of the location as pattern.
+ * <p>Will return "/WEB-INF/" for the pattern "/WEB-INF/*.xml",
+ * for example.
+ * @param location the location to check
+ * @return the part of the location that denotes the root directory
+ * @see #retrieveMatchingFiles
+ */
+ protected String determineRootDir(String location) {
+ int prefixEnd = location.indexOf(":") + 1;
+ int rootDirEnd = location.length();
+ while (rootDirEnd > prefixEnd && getPathMatcher().isPattern(location.substring(prefixEnd, rootDirEnd))) {
+ rootDirEnd = location.lastIndexOf('/', rootDirEnd - 2) + 1;
+ }
+ if (rootDirEnd == 0) {
+ rootDirEnd = prefixEnd;
+ }
+ return location.substring(0, rootDirEnd);
+ }
+
+ /**
+ * Resolve the specified resource for path matching.
+ * <p>The default implementation detects an Equinox OSGi "bundleresource:"
+ * / "bundleentry:" URL and resolves it into a standard jar file URL that
+ * can be traversed using Spring's standard jar file traversal algorithm.
+ * @param original the resource to resolve
+ * @return the resolved resource (may be identical to the passed-in resource)
+ * @throws IOException in case of resolution failure
+ */
+ protected Resource resolveRootDirResource(Resource original) throws IOException {
+ if (equinoxResolveMethod != null) {
+ URL url = original.getURL();
+ if (url.getProtocol().startsWith("bundle")) {
+ try {
+ return new UrlResource((URL) equinoxResolveMethod.invoke(url));
+ } catch (Exception e) {
+ ReflectionUtils.handleReflectionException(e);
+ }
+ }
+ }
+ return original;
+ }
+
+ /**
+ * Return whether the given resource handle indicates a jar resource
+ * that the {@code doFindPathMatchingJarResources} method can handle.
+ * <p>The default implementation checks against the URL protocols
+ * "jar", "zip" and "wsjar" (the latter are used by BEA WebLogic Server
+ * and IBM WebSphere, respectively, but can be treated like jar files).
+ * @param resource the resource handle to check
+ * (usually the root directory to start path matching from)
+ * @see #doFindPathMatchingJarResources
+ * @see ResourceUtils#isJarURL
+ */
+ protected boolean isJarResource(Resource resource) throws IOException {
+ return ResourceUtils.isJarURL(resource.getURL());
+ }
+
+ /**
+ * Find all resources in jar files that match the given location pattern
+ * via the Ant-style PathMatcher.
+ * @param rootDirResource the root directory as Resource
+ * @param subPattern the sub pattern to match (below the root directory)
+ * @return a mutable Set of matching Resource instances
+ * @throws IOException in case of I/O errors
+ * @see java.net.JarURLConnection
+ */
+ protected Set<Resource> doFindPathMatchingJarResources(Resource rootDirResource, String subPattern)
+ throws IOException {
+
+ URLConnection con = rootDirResource.getURL().openConnection();
+ JarFile jarFile;
+ String jarFileUrl;
+ String rootEntryPath;
+ boolean newJarFile = false;
+
+ if (con instanceof JarURLConnection) {
+ // Should usually be the case for traditional JAR files.
+ JarURLConnection jarCon = (JarURLConnection) con;
+ ResourceUtils.useCachesIfNecessary(jarCon);
+ jarFile = jarCon.getJarFile();
+ jarFileUrl = jarCon.getJarFileURL().toExternalForm();
+ JarEntry jarEntry = jarCon.getJarEntry();
+ rootEntryPath = (jarEntry != null ? jarEntry.getName() : "");
+ }
+ else {
+ // No JarURLConnection -> need to resort to URL file parsing.
+ // We'll assume URLs of the format "jar:path!/entry", with the protocol
+ // being arbitrary as long as following the entry format.
+ // We'll also handle paths with and without leading "file:" prefix.
+ String urlFile = rootDirResource.getURL().getFile();
+ int separatorIndex = urlFile.indexOf(ResourceUtils.JAR_URL_SEPARATOR);
+ if (separatorIndex != -1) {
+ jarFileUrl = urlFile.substring(0, separatorIndex);
+ rootEntryPath = urlFile.substring(separatorIndex + ResourceUtils.JAR_URL_SEPARATOR.length());
+ jarFile = getJarFile(jarFileUrl);
+ }
+ else {
+ jarFile = new JarFile(urlFile);
+ jarFileUrl = urlFile;
+ rootEntryPath = "";
+ }
+ newJarFile = true;
+ }
+
+ try {
+ logger.finest("Looking for matching resources in jar file [" + jarFileUrl + "]");
+ if (!"".equals(rootEntryPath) && !rootEntryPath.endsWith("/")) {
+ // Root entry path must end with slash to allow for proper matching.
+ // The Sun JRE does not return a slash here, but BEA JRockit does.
+ rootEntryPath = rootEntryPath + "/";
+ }
+ Set<Resource> result = new LinkedHashSet<Resource>(8);
+ for (Enumeration<JarEntry> entries = jarFile.entries(); entries.hasMoreElements();) {
+ JarEntry entry = entries.nextElement();
+ String entryPath = entry.getName();
+ if (entryPath.startsWith(rootEntryPath)) {
+ String relativePath = entryPath.substring(rootEntryPath.length());
+ if (getPathMatcher().match(subPattern, relativePath)) {
+ result.add(rootDirResource.createRelative(relativePath));
+ }
+ }
+ }
+ return result;
+ }
+ finally {
+ // Close jar file, but only if freshly obtained -
+ // not from JarURLConnection, which might cache the file reference.
+ if (newJarFile) {
+ jarFile.close();
+ }
+ }
+ }
+
+ /**
+ * Resolve the given jar file URL into a JarFile object.
+ */
+ protected JarFile getJarFile(String jarFileUrl) throws IOException {
+ if (jarFileUrl.startsWith(ResourceUtils.FILE_URL_PREFIX)) {
+ try {
+ return new JarFile(ResourceUtils.toURI(jarFileUrl).getSchemeSpecificPart());
+ }
+ catch (URISyntaxException ex) {
+ // Fallback for URLs that are not valid URIs (should hardly ever happen).
+ return new JarFile(jarFileUrl.substring(ResourceUtils.FILE_URL_PREFIX.length()));
+ }
+ }
+ else {
+ return new JarFile(jarFileUrl);
+ }
+ }
+
+ /**
+ * Find all resources in the file system that match the given location pattern
+ * via the Ant-style PathMatcher.
+ * @param rootDirResource the root directory as Resource
+ * @param subPattern the sub pattern to match (below the root directory)
+ * @return a mutable Set of matching Resource instances
+ * @throws IOException in case of I/O errors
+ * @see #retrieveMatchingFiles
+ */
+ protected Set<Resource> doFindPathMatchingFileResources(Resource rootDirResource, String subPattern)
+ throws IOException {
+
+ File rootDir;
+ try {
+ rootDir = rootDirResource.getFile().getAbsoluteFile();
+ }
+ catch (IOException ex) {
+ logger.log(Level.WARNING, ex, () -> "Cannot search for matching files underneath " + rootDirResource +
+ " because it does not correspond to a directory in the file system");
+ return Collections.emptySet();
+ }
+ return doFindMatchingFileSystemResources(rootDir, subPattern);
+ }
+
+ /**
+ * Find all resources in the file system that match the given location pattern
+ * via the Ant-style PathMatcher.
+ * @param rootDir the root directory in the file system
+ * @param subPattern the sub pattern to match (below the root directory)
+ * @return a mutable Set of matching Resource instances
+ * @throws IOException in case of I/O errors
+ * @see #retrieveMatchingFiles
+ */
+ protected Set<Resource> doFindMatchingFileSystemResources(File rootDir, String subPattern) throws IOException {
+ logger.finest(() -> "Looking for matching resources in directory tree [" + rootDir.getPath() + "]");
+ Set<File> matchingFiles = retrieveMatchingFiles(rootDir, subPattern);
+ Set<Resource> result = new LinkedHashSet<Resource>(matchingFiles.size());
+ for (File file : matchingFiles) {
+ result.add(new FileSystemResource(file));
+ }
+ return result;
+ }
+
+ /**
+ * Retrieve files that match the given path pattern,
+ * checking the given directory and its subdirectories.
+ * @param rootDir the directory to start from
+ * @param pattern the pattern to match against,
+ * relative to the root directory
+ * @return a mutable Set of matching Resource instances
+ * @throws IOException if directory contents could not be retrieved
+ */
+ protected Set<File> retrieveMatchingFiles(File rootDir, String pattern) throws IOException {
+ if (!rootDir.exists()) {
+ // Silently skip non-existing directories.
+ logger.finest(() -> "Skipping [" + rootDir.getAbsolutePath() + "] because it does not exist");
+ return Collections.emptySet();
+ }
+ if (!rootDir.isDirectory()) {
+ // Complain louder if it exists but is no directory.
+ logger.log(Level.WARNING, () -> "Skipping [" + rootDir.getAbsolutePath() + "] because it does not denote a directory");
+ return Collections.emptySet();
+ }
+ if (!rootDir.canRead()) {
+ logger.log(Level.WARNING, () -> "Cannot search for matching files underneath directory [" + rootDir.getAbsolutePath() +
+ "] because the application is not allowed to read the directory");
+ return Collections.emptySet();
+ }
+ String fullPattern = StringUtils.replace(rootDir.getAbsolutePath(), File.separator, "/");
+ if (!pattern.startsWith("/")) {
+ fullPattern += "/";
+ }
+ fullPattern = fullPattern + StringUtils.replace(pattern, File.separator, "/");
+ Set<File> result = new LinkedHashSet<File>(8);
+ doRetrieveMatchingFiles(fullPattern, rootDir, result);
+ return result;
+ }
+
+ /**
+ * Recursively retrieve files that match the given pattern,
+ * adding them to the given result list.
+ * @param fullPattern the pattern to match against,
+ * with prepended root directory path
+ * @param dir the current directory
+ * @param result the Set of matching File instances to add to
+ * @throws IOException if directory contents could not be retrieved
+ */
+ protected void doRetrieveMatchingFiles(String fullPattern, File dir, Set<File> result) throws IOException {
+ logger.finest(() -> "Searching directory [" + dir.getAbsolutePath() +
+ "] for files matching pattern [" + fullPattern + "]");
+ File[] dirContents = dir.listFiles();
+ if (dirContents == null) {
+ logger.log(Level.WARNING, () -> "Could not retrieve contents of directory [" + dir.getAbsolutePath() + "]");
+ return;
+ }
+ for (File content : dirContents) {
+ String currPath = StringUtils.replace(content.getAbsolutePath(), File.separator, "/");
+ if (content.isDirectory() && getPathMatcher().matchStart(fullPattern, currPath + "/")) {
+ if (!content.canRead()) {
+ logger.finest(() -> "Skipping subdirectory [" + dir.getAbsolutePath() +
+ "] because the application is not allowed to read the directory");
+ }
+ else {
+ doRetrieveMatchingFiles(fullPattern, content, result);
+ }
+ }
+ if (getPathMatcher().match(fullPattern, currPath)) {
+ result.add(content);
+ }
+ }
+ }
+
+
+ /**
+ * Inner delegate class, avoiding a hard JBoss VFS API dependency at runtime.
+ */
+ private static class VfsResourceMatchingDelegate {
+
+ public static Set<Resource> findMatchingResources(
+ Resource rootResource, String locationPattern, AntPathMatcher pathMatcher) throws IOException {
+ Object root = VfsUtils.getRoot(rootResource.getURL());
+ PatternVirtualFileVisitor visitor =
+ new PatternVirtualFileVisitor(VfsUtils.getPath(root), locationPattern, pathMatcher);
+ VfsUtils.visit(root, visitor);
+ return visitor.getResources();
+ }
+ }
+
+
+ /**
+ * VFS visitor for path matching purposes.
+ */
+ @SuppressWarnings("unused")
+ private static class PatternVirtualFileVisitor implements InvocationHandler {
+
+ private final String subPattern;
+
+ private final AntPathMatcher pathMatcher;
+
+ private final String rootPath;
+
+ private final Set<Resource> resources = new LinkedHashSet<Resource>();
+
+ public PatternVirtualFileVisitor(String rootPath, String subPattern, AntPathMatcher pathMatcher) {
+ this.subPattern = subPattern;
+ this.pathMatcher = pathMatcher;
+ this.rootPath = (rootPath.length() == 0 || rootPath.endsWith("/") ? rootPath : rootPath + "/");
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ String methodName = method.getName();
+ if (Object.class.equals(method.getDeclaringClass())) {
+ if (methodName.equals("equals")) {
+ // Only consider equal when proxies are identical.
+ return (proxy == args[0]);
+ }
+ else if (methodName.equals("hashCode")) {
+ return System.identityHashCode(proxy);
+ }
+ }
+ else if ("getAttributes".equals(methodName)) {
+ return getAttributes();
+ }
+ else if ("visit".equals(methodName)) {
+ visit(args[0]);
+ return null;
+ }
+ else if ("toString".equals(methodName)) {
+ return toString();
+ }
+
+ throw new IllegalStateException("Unexpected method invocation: " + method);
+ }
+
+ public void visit(Object vfsResource) {
+ if (this.pathMatcher.match(this.subPattern,
+ VfsUtils.getPath(vfsResource).substring(this.rootPath.length()))) {
+ this.resources.add(new VfsResource(vfsResource));
+ }
+ }
+
+ public Object getAttributes() {
+ return VfsUtils.getVisitorAttribute();
+ }
+
+ public Set<Resource> getResources() {
+ return this.resources;
+ }
+
+ public int size() {
+ return this.resources.size();
+ }
+
+ @Override
+ public String toString() {
+ return "sub-pattern: " + this.subPattern + ", resources: " + this.resources;
+ }
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/resources/io/ReflectionUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/resources/io/ReflectionUtils.java b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/ReflectionUtils.java
new file mode 100644
index 0000000..2bffcce
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/ReflectionUtils.java
@@ -0,0 +1,191 @@
+/*
+* Copyright 2002-2014 the original author or authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package org.apache.tamaya.core.internal.resources.io;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+* Simple utility class for working with the reflection API and handling
+* reflection exceptions.
+*
+* <p>Only intended for internal use.
+*
+* @author Juergen Hoeller
+* @author Rob Harrop
+* @author Rod Johnson
+* @author Costin Leau
+* @author Sam Brannen
+* @author Chris Beams
+* @since 1.2.2
+*/
+public abstract class ReflectionUtils {
+ /**
+ * Cache for {@link Class#getDeclaredMethods()}, allowing for fast resolution.
+ */
+ private static final Map<Class<?>, Method[]> declaredMethodsCache =
+ new ConcurrentHashMap<>(256);
+
+
+ /**
+ * Attempt to find a {@link Field field} on the supplied {@link Class} with the
+ * supplied {@code name}. Searches all superclasses up to {@link Object}.
+ * @param clazz the class to introspect
+ * @param name the name of the field
+ * @return the corresponding Field object, or {@code null} if not found
+ */
+ public static Field findField(Class<?> clazz, String name) {
+ return findField(clazz, name, null);
+ }
+
+ /**
+ * Attempt to find a {@link Field field} on the supplied {@link Class} with the
+ * supplied {@code name} and/or {@link Class type}. Searches all superclasses
+ * up to {@link Object}.
+ * @param clazz the class to introspect
+ * @param name the name of the field (may be {@code null} if type is specified)
+ * @param type the type of the field (may be {@code null} if name is specified)
+ * @return the corresponding Field object, or {@code null} if not found
+ */
+ public static Field findField(Class<?> clazz, String name, Class<?> type) {
+ Objects.requireNonNull(clazz, "Class must not be null");
+ if(name == null && type == null) throw new IllegalArgumentException("Either name or type of the field must be specified");
+ Class<?> searchType = clazz;
+ while (!Object.class.equals(searchType) && searchType != null) {
+ Field[] fields = searchType.getDeclaredFields();
+ for (Field field : fields) {
+ if ((name == null || name.equals(field.getName())) && (type == null || type.equals(field.getType()))) {
+ return field;
+ }
+ }
+ searchType = searchType.getSuperclass();
+ }
+ return null;
+ }
+
+ /**
+ * Attempt to find a {@link Method} on the supplied class with the supplied name
+ * and no parameters. Searches all superclasses up to {@code Object}.
+ * <p>Returns {@code null} if no {@link Method} can be found.
+ * @param clazz the class to introspect
+ * @param name the name of the method
+ * @return the Method object, or {@code null} if none found
+ */
+ public static Method findMethod(Class<?> clazz, String name) {
+ return findMethod(clazz, name, new Class<?>[0]);
+ }
+
+ /**
+ * Attempt to find a {@link Method} on the supplied class with the supplied name
+ * and parameter types. Searches all superclasses up to {@code Object}.
+ * <p>Returns {@code null} if no {@link Method} can be found.
+ * @param clazz the class to introspect
+ * @param name the name of the method
+ * @param paramTypes the parameter types of the method
+ * (may be {@code null} to indicate any signature)
+ * @return the Method object, or {@code null} if none found
+ */
+ public static Method findMethod(Class<?> clazz, String name, Class<?>... paramTypes) {
+ Objects.requireNonNull(clazz, "Class must not be null");
+ Objects.requireNonNull(name, "Method name must not be null");
+ Class<?> searchType = clazz;
+ while (searchType != null) {
+ Method[] methods = (searchType.isInterface() ? searchType.getMethods() : getDeclaredMethods(searchType));
+ for (Method method : methods) {
+ if (name.equals(method.getName()) &&
+ (paramTypes == null || Arrays.equals(paramTypes, method.getParameterTypes()))) {
+ return method;
+ }
+ }
+ searchType = searchType.getSuperclass();
+ }
+ return null;
+ }
+
+ /**
+ * Handle the given reflection exception. Should only be called if no
+ * checked exception is expected to be thrown by the target method.
+ * <p>Throws the underlying RuntimeException or Error in case of an
+ * InvocationTargetException with such a root cause. Throws an
+ * IllegalStateException with an appropriate message else.
+ * @param ex the reflection exception to handle
+ */
+ public static void handleReflectionException(Exception ex) {
+ if (ex instanceof NoSuchMethodException) {
+ throw new IllegalStateException("Method not found: " + ex.getMessage());
+ }
+ if (ex instanceof IllegalAccessException) {
+ throw new IllegalStateException("Could not access method: " + ex.getMessage());
+ }
+ if (ex instanceof InvocationTargetException) {
+ handleInvocationTargetException((InvocationTargetException) ex);
+ }
+ if (ex instanceof RuntimeException) {
+ throw (RuntimeException) ex;
+ }
+ throw new UndeclaredThrowableException(ex);
+ }
+
+ /**
+ * Handle the given invocation target exception. Should only be called if no
+ * checked exception is expected to be thrown by the target method.
+ * <p>Throws the underlying RuntimeException or Error in case of such a root
+ * cause. Throws an IllegalStateException else.
+ * @param ex the invocation target exception to handle
+ */
+ public static void handleInvocationTargetException(InvocationTargetException ex) {
+ rethrowRuntimeException(ex.getTargetException());
+ }
+
+ /**
+ * Rethrow the given {@link Throwable exception}, which is presumably the
+ * <em>target exception</em> of an {@link InvocationTargetException}. Should
+ * only be called if no checked exception is expected to be thrown by the
+ * target method.
+ * <p>Rethrows the underlying exception cast to an {@link RuntimeException} or
+ * {@link Error} if appropriate; otherwise, throws an
+ * {@link IllegalStateException}.
+ * @param ex the exception to rethrow
+ * @throws RuntimeException the rethrown exception
+ */
+ public static void rethrowRuntimeException(Throwable ex) {
+ if (ex instanceof RuntimeException) {
+ throw (RuntimeException) ex;
+ }
+ if (ex instanceof Error) {
+ throw (Error) ex;
+ }
+ throw new UndeclaredThrowableException(ex);
+ }
+
+ /**
+ * This method retrieves {@link Class#getDeclaredMethods()} from a local cache
+ * in order to avoid the JVM's SecurityManager check and defensive array copying.
+ */
+ private static Method[] getDeclaredMethods(Class<?> clazz) {
+ Method[] result = declaredMethodsCache.get(clazz);
+ if (result == null) {
+ result = clazz.getDeclaredMethods();
+ declaredMethodsCache.put(clazz, result);
+ }
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/resources/io/Resource.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/resources/io/Resource.java b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/Resource.java
new file mode 100644
index 0000000..696412e
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/Resource.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tamaya.core.internal.resources.io;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+import java.util.function.Supplier;
+
+/**
+ * Interface for a resource descriptor that abstracts from the actual
+ * type of underlying resource, such as a file or class path resource.
+ *
+ * <p>An InputStream can be opened for every resource if it exists in
+ * physical form, but a URL or File handle can just be returned for
+ * certain resources. The actual behavior is implementation-specific.
+ *
+ * @author Juergen Hoeller
+ * @since 28.12.2003
+ * @see #getInputStream()
+ * @see #getURL()
+ * @see #getURI()
+ * @see #getFile()
+ * @see FileSystemResource
+ * @see ClassPathResource
+ * @see UrlResource
+ * @see ByteArrayResource
+ * @see InputStreamResource
+ * @see PathResource
+ */
+public interface Resource extends InputStreamSource {
+
+ /**
+ * Return whether this resource actually exists in physical form.
+ * <p>This method performs a definitive existence check, whereas the
+ * existence of a {@code Resource} handle only guarantees a
+ * valid descriptor handle.
+ */
+ boolean exists();
+
+ /**
+ * Return whether the contents of this resource can be read,
+ * e.g. via {@link #getInputStream()} or {@link #getFile()}.
+ * <p>Will be {@code true} for typical resource descriptors;
+ * note that actual content reading may still fail when attempted.
+ * However, a value of {@code false} is a definitive indication
+ * that the resource content cannot be read.
+ * @see #getInputStream
+ */
+ boolean isReadable();
+
+ /**
+ * Return whether this resource represents a handle with an open
+ * stream. If true, the InputStream cannot be read multiple times,
+ * and must be read and closed to avoid resource leaks.
+ * <p>Will be {@code false} for typical resource descriptors.
+ */
+ boolean isOpen();
+
+ /**
+ * Return a URL handle for this resource.
+ * @throws IOException if the resource cannot be resolved as URL,
+ * i.e. if the resource is not available as descriptor
+ */
+ URL getURL() throws IOException;
+
+ /**
+ * Return a URI handle for this resource.
+ * @throws IOException if the resource cannot be resolved as URI,
+ * i.e. if the resource is not available as descriptor
+ */
+ URI getURI() throws IOException;
+
+ /**
+ * Return a File handle for this resource.
+ * @throws IOException if the resource cannot be resolved as absolute
+ * file path, i.e. if the resource is not available in a file system
+ */
+ File getFile() throws IOException;
+
+ /**
+ * Determine the content length for this resource.
+ * @throws IOException if the resource cannot be resolved
+ * (in the file system or as some other known physical resource type)
+ */
+ long contentLength() throws IOException;
+
+ /**
+ * Determine the last-modified timestamp for this resource.
+ * @throws IOException if the resource cannot be resolved
+ * (in the file system or as some other known physical resource type)
+ */
+ long lastModified() throws IOException;
+
+ /**
+ * Create a resource relative to this resource.
+ * @param relativePath the relative path (relative to this resource)
+ * @return the resource handle for the relative resource
+ * @throws IOException if the relative resource cannot be determined
+ */
+ Resource createRelative(String relativePath) throws IOException;
+
+ /**
+ * Determine a filename for this resource, i.e. typically the last
+ * part of the path: for example, "myfile.txt".
+ * <p>Returns {@code null} if this type of resource does not
+ * have a filename.
+ */
+ String getFilename();
+
+ /**
+ * Return a description for this resource,
+ * to be used for error output when working with the resource.
+ * <p>Implementations are also encouraged to return this value
+ * from their {@code toString} method.
+ * @see Object#toString()
+ */
+ String getDescription();
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/a55d1c97/core/src/main/java/org/apache/tamaya/core/internal/resources/io/ResourceUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/tamaya/core/internal/resources/io/ResourceUtils.java b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/ResourceUtils.java
new file mode 100644
index 0000000..6835ffa
--- /dev/null
+++ b/core/src/main/java/org/apache/tamaya/core/internal/resources/io/ResourceUtils.java
@@ -0,0 +1,350 @@
+/*
+* Copyright 2002-2014 the original author or authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package org.apache.tamaya.core.internal.resources.io;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Objects;
+
+/**
+* Utility methods for resolving resource locations to files in the
+* file system. Mainly for internal use within the framework.
+*
+* <p>Consider using Spring's Resource abstraction in the core package
+* for handling all kinds of file resources in a uniform manner.
+* {@code org.springframework.core.io.ResourceLoader}'s {@code getResource()}
+* method can resolve any location to a {@code org.springframework.core.io.Resource}
+* object, which in turn allows one to obtain a {@code java.io.File} in the
+* file system through its {@code getFile()} method.
+*
+* <p>The main reason for these utility methods for resource location handling
+* is to support {@code Log4jConfigurer}, which must be able to resolve
+* resource locations <i>before the logging system has been initialized</i>.
+* Spring's {@code Resource} abstraction in the core package, on the other hand,
+* already expects the logging system to be available.
+*
+* @author Juergen Hoeller
+* @since 1.1.5
+*/
+public abstract class ResourceUtils {
+
+ /** URL prefix for loading from the file system: "file:" */
+ public static final String FILE_URL_PREFIX = "file:";
+
+ /** URL prefix for loading from the file system: "jar:" */
+ public static final String JAR_URL_PREFIX = "jar:";
+
+ /** URL protocol for a file in the file system: "file" */
+ public static final String URL_PROTOCOL_FILE = "file";
+
+ /** URL protocol for an entry from a jar file: "jar" */
+ public static final String URL_PROTOCOL_JAR = "jar";
+
+ /** URL protocol for an entry from a zip file: "zip" */
+ public static final String URL_PROTOCOL_ZIP = "zip";
+
+ /** URL protocol for an entry from a WebSphere jar file: "wsjar" */
+ public static final String URL_PROTOCOL_WSJAR = "wsjar";
+
+ /** URL protocol for an entry from a JBoss jar file: "vfszip" */
+ public static final String URL_PROTOCOL_VFSZIP = "vfszip";
+
+ /** URL protocol for a JBoss file system resource: "vfsfile" */
+ public static final String URL_PROTOCOL_VFSFILE = "vfsfile";
+
+ /** URL protocol for a general JBoss VFS resource: "vfs" */
+ public static final String URL_PROTOCOL_VFS = "vfs";
+
+ /** File extension for a regular jar file: ".jar" */
+ public static final String JAR_FILE_EXTENSION = ".jar";
+
+ /** Separator between JAR URL and file path within the JAR: "!/" */
+ public static final String JAR_URL_SEPARATOR = "!/";
+
+
+ /**
+ * Return whether the given resource location is a URL:
+ * either a special "classpath" pseudo URL or a standard URL.
+ * @param resourceLocation the location String to check
+ * @return whether the location qualifies as a URL
+ * @see DefaultResourceLoader#CLASSPATH_URL_PREFIX
+ * @see java.net.URL
+ */
+ public static boolean isUrl(String resourceLocation) {
+ if (resourceLocation == null) {
+ return false;
+ }
+ if (resourceLocation.startsWith(DefaultResourceLoader.CLASSPATH_URL_PREFIX)) {
+ return true;
+ }
+ try {
+ new URL(resourceLocation);
+ return true;
+ }
+ catch (MalformedURLException ex) {
+ return false;
+ }
+ }
+
+ /**
+ * Resolve the given resource location to a {@code java.net.URL}.
+ * <p>Does not check whether the URL actually exists; simply returns
+ * the URL that the given location would correspond to.
+ * @param resourceLocation the resource location to resolve: either a
+ * "classpath:" pseudo URL, a "file:" URL, or a plain file path
+ * @return a corresponding URL object
+ * @throws FileNotFoundException if the resource cannot be resolved to a URL
+ */
+ public static URL getURL(String resourceLocation) throws FileNotFoundException {
+ Objects.requireNonNull(resourceLocation, "Resource location must not be null");
+ if (resourceLocation.startsWith(DefaultResourceLoader.CLASSPATH_URL_PREFIX)) {
+ String path = resourceLocation.substring(DefaultResourceLoader.CLASSPATH_URL_PREFIX.length());
+ ClassLoader cl = ClassUtils.getDefaultClassLoader();
+ URL url = (cl != null ? cl.getResource(path) : ClassLoader.getSystemResource(path));
+ if (url == null) {
+ String description = "class path resource [" + path + "]";
+ throw new FileNotFoundException(description +
+ " cannot be resolved to URL because it does not exist");
+ }
+ return url;
+ }
+ try {
+ // try URL
+ return new URL(resourceLocation);
+ }
+ catch (MalformedURLException ex) {
+ // no URL -> treat as file path
+ try {
+ return new File(resourceLocation).toURI().toURL();
+ }
+ catch (MalformedURLException ex2) {
+ throw new FileNotFoundException("Resource location [" + resourceLocation +
+ "] is neither a URL not a well-formed file path");
+ }
+ }
+ }
+
+ /**
+ * Resolve the given resource location to a {@code java.io.File},
+ * i.e. to a file in the file system.
+ * <p>Does not check whether the file actually exists; simply returns
+ * the File that the given location would correspond to.
+ * @param resourceLocation the resource location to resolve: either a
+ * "classpath:" pseudo URL, a "file:" URL, or a plain file path
+ * @return a corresponding File object
+ * @throws FileNotFoundException if the resource cannot be resolved to
+ * a file in the file system
+ */
+ public static File getFile(String resourceLocation) throws FileNotFoundException {
+ Objects.requireNonNull(resourceLocation, "Resource location must not be null");
+ if (resourceLocation.startsWith(DefaultResourceLoader.CLASSPATH_URL_PREFIX)) {
+ String path = resourceLocation.substring(DefaultResourceLoader.CLASSPATH_URL_PREFIX.length());
+ String description = "class path resource [" + path + "]";
+ ClassLoader cl = ClassUtils.getDefaultClassLoader();
+ URL url = (cl != null ? cl.getResource(path) : ClassLoader.getSystemResource(path));
+ if (url == null) {
+ throw new FileNotFoundException(description +
+ " cannot be resolved to absolute file path because it does not exist");
+ }
+ return getFile(url, description);
+ }
+ try {
+ // try URL
+ return getFile(new URL(resourceLocation));
+ }
+ catch (MalformedURLException ex) {
+ // no URL -> treat as file path
+ return new File(resourceLocation);
+ }
+ }
+
+ /**
+ * Resolve the given resource URL to a {@code java.io.File},
+ * i.e. to a file in the file system.
+ * @param resourceUrl the resource URL to resolve
+ * @return a corresponding File object
+ * @throws FileNotFoundException if the URL cannot be resolved to
+ * a file in the file system
+ */
+ public static File getFile(URL resourceUrl) throws FileNotFoundException {
+ return getFile(resourceUrl, "URL");
+ }
+
+ /**
+ * Resolve the given resource URL to a {@code java.io.File},
+ * i.e. to a file in the file system.
+ * @param resourceUrl the resource URL to resolve
+ * @param description a description of the original resource that
+ * the URL was created for (for example, a class path location)
+ * @return a corresponding File object
+ * @throws FileNotFoundException if the URL cannot be resolved to
+ * a file in the file system
+ */
+ public static File getFile(URL resourceUrl, String description) throws FileNotFoundException {
+ Objects.requireNonNull(resourceUrl, "Resource URL must not be null");
+ if (!URL_PROTOCOL_FILE.equals(resourceUrl.getProtocol())) {
+ throw new FileNotFoundException(
+ description + " cannot be resolved to absolute file path " +
+ "because it does not reside in the file system: " + resourceUrl);
+ }
+ try {
+ return new File(toURI(resourceUrl).getSchemeSpecificPart());
+ }
+ catch (URISyntaxException ex) {
+ // Fallback for URLs that are not valid URIs (should hardly ever happen).
+ return new File(resourceUrl.getFile());
+ }
+ }
+
+ /**
+ * Resolve the given resource URI to a {@code java.io.File},
+ * i.e. to a file in the file system.
+ * @param resourceUri the resource URI to resolve
+ * @return a corresponding File object
+ * @throws FileNotFoundException if the URL cannot be resolved to
+ * a file in the file system
+ */
+ public static File getFile(URI resourceUri) throws FileNotFoundException {
+ return getFile(resourceUri, "URI");
+ }
+
+ /**
+ * Resolve the given resource URI to a {@code java.io.File},
+ * i.e. to a file in the file system.
+ * @param resourceUri the resource URI to resolve
+ * @param description a description of the original resource that
+ * the URI was created for (for example, a class path location)
+ * @return a corresponding File object
+ * @throws FileNotFoundException if the URL cannot be resolved to
+ * a file in the file system
+ */
+ public static File getFile(URI resourceUri, String description) throws FileNotFoundException {
+ Objects.requireNonNull(resourceUri, "Resource URI must not be null");
+ if (!URL_PROTOCOL_FILE.equals(resourceUri.getScheme())) {
+ throw new FileNotFoundException(
+ description + " cannot be resolved to absolute file path " +
+ "because it does not reside in the file system: " + resourceUri);
+ }
+ return new File(resourceUri.getSchemeSpecificPart());
+ }
+
+ /**
+ * Determine whether the given URL points to a resource in the file system,
+ * that is, has protocol "file", "vfsfile" or "vfs".
+ * @param url the URL to check
+ * @return whether the URL has been identified as a file system URL
+ */
+ public static boolean isFileURL(URL url) {
+ String protocol = url.getProtocol();
+ return (URL_PROTOCOL_FILE.equals(protocol) || URL_PROTOCOL_VFSFILE.equals(protocol) ||
+ URL_PROTOCOL_VFS.equals(protocol));
+ }
+
+ /**
+ * Determine whether the given URL points to a resource in a jar file,
+ * that is, has protocol "jar", "zip", "vfszip" or "wsjar".
+ * @param url the URL to check
+ * @return whether the URL has been identified as a JAR URL
+ */
+ public static boolean isJarURL(URL url) {
+ String protocol = url.getProtocol();
+ return (URL_PROTOCOL_JAR.equals(protocol) || URL_PROTOCOL_ZIP.equals(protocol) ||
+ URL_PROTOCOL_VFSZIP.equals(protocol) || URL_PROTOCOL_WSJAR.equals(protocol));
+ }
+
+ /**
+ * Determine whether the given URL points to a jar file itself,
+ * that is, has protocol "file" and ends with the ".jar" extension.
+ * @param url the URL to check
+ * @return whether the URL has been identified as a JAR file URL
+ * @since 4.1
+ */
+ public static boolean isJarFileURL(URL url) {
+ return (URL_PROTOCOL_FILE.equals(url.getProtocol()) &&
+ url.getPath().toLowerCase().endsWith(JAR_FILE_EXTENSION));
+ }
+
+ /**
+ * Extract the URL for the actual jar file from the given URL
+ * (which may point to a resource in a jar file or to a jar file itself).
+ * @param jarUrl the original URL
+ * @return the URL for the actual jar file
+ * @throws MalformedURLException if no valid jar file URL could be extracted
+ */
+ public static URL extractJarFileURL(URL jarUrl) throws MalformedURLException {
+ String urlFile = jarUrl.getFile();
+ int separatorIndex = urlFile.indexOf(JAR_URL_SEPARATOR);
+ if (separatorIndex != -1) {
+ String jarFile = urlFile.substring(0, separatorIndex);
+ try {
+ return new URL(jarFile);
+ }
+ catch (MalformedURLException ex) {
+ // Probably no protocol in original jar URL, like "jar:C:/mypath/myjar.jar".
+ // This usually indicates that the jar file resides in the file system.
+ if (!jarFile.startsWith("/")) {
+ jarFile = "/" + jarFile;
+ }
+ return new URL(FILE_URL_PREFIX + jarFile);
+ }
+ }
+ else {
+ return jarUrl;
+ }
+ }
+
+ /**
+ * Create a URI instance for the given URL,
+ * replacing spaces with "%20" URI encoding first.
+ * <p>Furthermore, this method works on JDK 1.4 as well,
+ * in contrast to the {@code URL.toURI()} method.
+ * @param url the URL to convert into a URI instance
+ * @return the URI instance
+ * @throws URISyntaxException if the URL wasn't a valid URI
+ * @see java.net.URL#toURI()
+ */
+ public static URI toURI(URL url) throws URISyntaxException {
+ return toURI(url.toString());
+ }
+
+ /**
+ * Create a URI instance for the given location String,
+ * replacing spaces with "%20" URI encoding first.
+ * @param location the location String to convert into a URI instance
+ * @return the URI instance
+ * @throws URISyntaxException if the location wasn't a valid URI
+ */
+ public static URI toURI(String location) throws URISyntaxException {
+ return new URI(location.replaceAll(" ", "%20"));
+ }
+
+ /**
+ * Set the {@link URLConnection#setUseCaches "useCaches"} flag on the
+ * given connection, preferring {@code false} but leaving the
+ * flag at {@code true} for JNLP based resources.
+ * @param con the URLConnection to set the flag on
+ */
+ public static void useCachesIfNecessary(URLConnection con) {
+ con.setUseCaches(con.getClass().getSimpleName().startsWith("JNLP"));
+ }
+
+}