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/&#42;&#42;/test.jsp} - matches all
+ * {@code test.jsp} files underneath the {@code com} path</li> <li>{@code org/springframework/&#42;&#42;/*.jsp}
+ * - matches all {@code .jsp} files underneath the {@code org/springframework} path</li>
+ * <li>{@code org/&#42;&#42;/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/&#42;&#42;</td><td>/bookings</td><td>/hotels/&#42;&#42;/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/&#42;&#42;</td><td>{hotel}</td><td>/hotels/&#42;&#42;/{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/**&#47;applicationContext.xml
+ * file:C:/some/path/*-context.xml
+ * classpath:com/mycompany/**&#47;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/**&#47;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"));
+	}
+
+}