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 2015/09/14 08:25:16 UTC

incubator-tamaya git commit: Added initial early draft version of CL aware service context implementation.

Repository: incubator-tamaya
Updated Branches:
  refs/heads/master aa36ff9f3 -> 459510e33


Added initial early draft version of CL aware service context implementation.


Project: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/commit/459510e3
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/tree/459510e3
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/diff/459510e3

Branch: refs/heads/master
Commit: 459510e33974ec28f9c9df9868fa4693c9ae905a
Parents: aa36ff9
Author: anatole <an...@apache.org>
Authored: Mon Sep 14 08:24:36 2015 +0200
Committer: anatole <an...@apache.org>
Committed: Mon Sep 14 08:24:57 2015 +0200

----------------------------------------------------------------------
 sandbox/classloader-support/pom.xml             |  53 ++++
 .../internal/CLAwareServiceContext.java         | 191 ++++++++++++
 .../apache/tamaya/clsupport/internal/Item.java  |  30 ++
 .../tamaya/clsupport/internal/ItemSet.java      | 140 +++++++++
 .../clsupport/internal/ServiceContainer.java    | 287 +++++++++++++++++++
 .../org.apache.tamaya.spi.ServiceContext        |  19 ++
 6 files changed, 720 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/459510e3/sandbox/classloader-support/pom.xml
----------------------------------------------------------------------
diff --git a/sandbox/classloader-support/pom.xml b/sandbox/classloader-support/pom.xml
new file mode 100644
index 0000000..c5cb480
--- /dev/null
+++ b/sandbox/classloader-support/pom.xml
@@ -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 current 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.tamaya.ext</groupId>
+        <artifactId>tamaya-sandbox</artifactId>
+        <version>0.2-incubating-SNAPSHOT</version>
+        <relativePath>..</relativePath>
+    </parent>
+
+    <artifactId>tamaya-classloader-support</artifactId>
+    <name>Apache Tamaya Modules -Classloader Support</name>
+    <description>Apache Tamaya Classloader Support registers a ConfigurationContext that leverages
+    classloader hierarchies. Also visibility of features and components is aligned with the
+    corresponding hierarchy of classloaders.</description>
+    <packaging>jar</packaging>
+
+    <properties>
+        <jdkVersion>1.7</jdkVersion>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.tamaya</groupId>
+            <artifactId>tamaya-java7-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/459510e3/sandbox/classloader-support/src/main/java/org/apache/tamaya/clsupport/internal/CLAwareServiceContext.java
----------------------------------------------------------------------
diff --git a/sandbox/classloader-support/src/main/java/org/apache/tamaya/clsupport/internal/CLAwareServiceContext.java b/sandbox/classloader-support/src/main/java/org/apache/tamaya/clsupport/internal/CLAwareServiceContext.java
new file mode 100644
index 0000000..7d418d9
--- /dev/null
+++ b/sandbox/classloader-support/src/main/java/org/apache/tamaya/clsupport/internal/CLAwareServiceContext.java
@@ -0,0 +1,191 @@
+/*
+ * 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.clsupport.internal;
+
+import org.apache.tamaya.ConfigException;
+import org.apache.tamaya.spi.ServiceContext;
+
+import javax.annotation.Priority;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Created by Anatole on 06.09.2015.
+ */
+@Priority(250)
+public class CLAwareServiceContext extends AbstractClassloaderAwareItemLoader<ServiceContainer>
+        implements ServiceContext{
+
+    public static final String PREFIX = "META-INF/services/";
+
+    /**
+     * Singletons as valid for this classloader. Other classloaders or parent contexts may have alternate
+     * singleton instances being active.
+     */
+    private final Map<Class<?>, Object> singletons = new ConcurrentHashMap<>();
+
+    public CLAwareServiceContext(){
+        super();
+    }
+
+    public CLAwareServiceContext(String resourceLocation) {
+        super();
+    }
+
+    public CLAwareServiceContext(String resourceLocation, ClassLoader classLoader) {
+        super(classLoader);
+    }
+
+
+    @Override
+    protected ServiceContainer createItem(ClassLoader classLoader) {
+        return new ServiceContainer(classLoader);
+    }
+
+    @Override
+    protected void updateItem(ServiceContainer currentContainer, ClassLoader classLoader) {
+
+    }
+
+    @Override
+    public int ordinal() {
+        return 100;
+    }
+
+    @Override
+    public <T> T getService(Class<T> serviceType) {
+        Object cached = singletons.get(serviceType);
+        if (cached == null) {
+            Collection<? extends T> services = getServices(serviceType);
+            if (services.isEmpty()) {
+                cached = null;
+            } else {
+                cached = getServiceWithHighestPriority(services, serviceType);
+            }
+            if(cached!=null) {
+                singletons.put(serviceType, cached);
+            }
+        }
+        return serviceType.cast(cached);
+    }
+
+    public <T> T getService(Class<T> serviceType, ClassLoader classLoader) {
+        ClassLoader cl = classLoader;
+        List<T> services = new ArrayList<>();
+        while(cl!=null) {
+            ServiceContainer container = getItemNoParent(classLoader, true);
+            services.addAll(container.getServices(serviceType));
+        }
+        return select(services, serviceType);
+    }
+
+    private <T> T select(Collection<T> services, Class<T> serviceType) {
+        return getServiceWithHighestPriority(services, serviceType);
+    }
+
+    @Override
+    public <T> Collection<T> getServices(Class<T> serviceType) {
+        return getServices(serviceType, AbstractClassloaderAwareItemLoader.getDefaultClassLoader());
+    }
+
+    public <T> Collection<T> getServices(Class<T> serviceType, ClassLoader classLoader) {
+        List<T> services = new ArrayList<>();
+        ClassLoader cl = classLoader;
+        List<ServiceContainer> containers = new ArrayList<>();
+        while(cl!=null) {
+            ServiceContainer container = getItemNoParent(cl, true);
+            containers.add(container);
+            cl = cl.getParent();
+        }
+        List<ServiceContainer> prevContainers = new ArrayList<>();
+        Collections.reverse(containers);
+        for(ServiceContainer container: containers) {
+            if (!container.isTypeLoaded(serviceType)) {
+                container.loadServices(serviceType, prevContainers);
+                prevContainers.add(container);
+                services.addAll(container.getServices(serviceType));
+            }
+        }
+        // TODO Sort services
+        // TODO Get first
+        return services;
+    }
+
+    /**
+     * @param services to scan
+     * @param <T>      type of the service
+     *
+     * @return the service with the highest {@link javax.annotation.Priority#value()}
+     *
+     * @throws ConfigException if there are multiple service implementations with the maximum priority
+     */
+    private <T> T getServiceWithHighestPriority(Collection<? extends T> services, Class<T> serviceType) {
+
+        // we do not need the priority stuff if the list contains only one element
+        if (services.size() == 1) {
+            return services.iterator().next();
+        }
+
+        Integer highestPriority = null;
+        int highestPriorityServiceCount = 0;
+        T highestService = null;
+
+        for (T service : services) {
+            int prio = getPriority(service);
+            if (highestPriority == null || highestPriority < prio) {
+                highestService = service;
+                highestPriorityServiceCount = 1;
+                highestPriority = prio;
+            } else if (highestPriority == prio) {
+                highestPriorityServiceCount++;
+            }
+        }
+
+        if (highestPriorityServiceCount > 1) {
+            throw new ConfigException(MessageFormat.format("Found {0} implementations for Service {1} with Priority {2}: {3}",
+                    highestPriorityServiceCount,
+                    serviceType.getName(),
+                    highestPriority,
+                    services));
+        }
+
+        return highestService;
+    }
+
+    /**
+     * Checks the given instance for a @Priority annotation. If present the annotation's value s evaluated. If no such
+     * annotation is present, a default priority is returned (1);
+     * @param o the instance, not null.
+     * @return a priority, by default 1.
+     */
+    public static int getPriority(Object o){
+        int prio = 0;
+        Priority priority = o.getClass().getAnnotation(Priority.class);
+        if (priority != null) {
+            prio = priority.value();
+        }
+        return prio;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/459510e3/sandbox/classloader-support/src/main/java/org/apache/tamaya/clsupport/internal/Item.java
----------------------------------------------------------------------
diff --git a/sandbox/classloader-support/src/main/java/org/apache/tamaya/clsupport/internal/Item.java b/sandbox/classloader-support/src/main/java/org/apache/tamaya/clsupport/internal/Item.java
new file mode 100644
index 0000000..9ed1f2b
--- /dev/null
+++ b/sandbox/classloader-support/src/main/java/org/apache/tamaya/clsupport/internal/Item.java
@@ -0,0 +1,30 @@
+/*
+ * 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.clsupport.internal;
+
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.List;
+
+/**
+ * Created by Anatole on 08.09.2015.
+ */
+public interface Item<T extends Item<T>> {
+    <T> void update(ClassLoader classLoader, Enumeration<URL> resources, List<T> itemsMatching);
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/459510e3/sandbox/classloader-support/src/main/java/org/apache/tamaya/clsupport/internal/ItemSet.java
----------------------------------------------------------------------
diff --git a/sandbox/classloader-support/src/main/java/org/apache/tamaya/clsupport/internal/ItemSet.java b/sandbox/classloader-support/src/main/java/org/apache/tamaya/clsupport/internal/ItemSet.java
new file mode 100644
index 0000000..53b8e67
--- /dev/null
+++ b/sandbox/classloader-support/src/main/java/org/apache/tamaya/clsupport/internal/ItemSet.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tamaya.clsupport.internal;
+
+import java.lang.ref.WeakReference;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Class that acts as a container for items loaded/evaialbe/bound to exactly one classloader.
+ */
+final class ItemSet<T> implements Iterable<T>{
+    /** The managed items. */
+    private Set<T> items = new HashSet<>();
+    /** the corresponding classloader. */
+    private WeakReference<ClassLoader> classLoaderRef;
+    /** Timestamp in ms, when the last time the resource set was updated/checked for additional items. */
+    private long lastUpdate;
+
+    /**
+     * Creates a new resource set.
+     * @param cl the classloader, not null.
+     * @param items the items to be added, not null.
+     */
+    public ItemSet(ClassLoader cl, Collection<T> items){
+        this.classLoaderRef = new WeakReference<ClassLoader>(Objects.requireNonNull(cl));
+        this.items.addAll(items);
+    }
+
+    /**
+     * Removes all items.
+     */
+    public void clear(){
+        Set<T> res = this.items;
+        if(res!=null) {
+            res.clear();
+        }
+    }
+
+    /**
+     * Adds the given items. If the resource set is not active this method return false and
+     * no resource will be added.
+     * @param resources the items to be added, not null.
+     * @return true, if adding was succesful.
+     */
+    public boolean addResources(Collection<T> resources) {
+        if(!isActive()){
+            return false;
+        }
+        Set<T> res = this.items;
+        if(res!=null) {
+            res.addAll(resources);
+            this.lastUpdate = System.currentTimeMillis();
+        }
+        return true;
+    }
+
+    public boolean contains(T resource) {
+        return getItems().contains(resource);
+    }
+
+    public Set<T> getItems(){
+        if(classLoaderRef==null) {
+            return Collections.emptySet();
+        }
+        return Collections.unmodifiableSet(this.items);
+    }
+
+    @Override
+    public Iterator<T> iterator() {
+        return getItems().iterator();
+    }
+
+    /**
+     * Checks, if the referenced classloader was garbage collected.
+     * @return true if the referenced classloader was garbage collected.
+     */
+    public boolean isActive(){
+        return classLoaderRef==null;
+    }
+
+    /**
+     * Gets the timestamp, when the resource was updated the last time.
+     * @return the timestamp, when the resource was updated the last time.
+     */
+    public long getLastUpdate(){
+        return lastUpdate;
+    }
+
+    /**
+     * Updates the items, e.g. by adding new items found.
+     * @param resources the updated items.
+     */
+    public void update(Collection<T> resources) {
+        if(classLoaderRef!=null) {
+            if (classLoaderRef.get()==null) {
+                this.items = null;
+            }
+            else{
+                if(this.items !=null){
+                    Set<T> newRes = new HashSet<>(resources.size());
+                    newRes.addAll(resources);
+                    this.items = newRes;
+                }
+            }
+            this.lastUpdate = System.currentTimeMillis();
+        }
+    }
+
+
+    @Override
+    public String toString() {
+        return "ItemSet{" +
+                "items=" + items +
+                ", classLoaderRef=" + classLoaderRef +
+                ", lastUpdate=" + lastUpdate +
+                '}';
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/459510e3/sandbox/classloader-support/src/main/java/org/apache/tamaya/clsupport/internal/ServiceContainer.java
----------------------------------------------------------------------
diff --git a/sandbox/classloader-support/src/main/java/org/apache/tamaya/clsupport/internal/ServiceContainer.java b/sandbox/classloader-support/src/main/java/org/apache/tamaya/clsupport/internal/ServiceContainer.java
new file mode 100644
index 0000000..a8293be
--- /dev/null
+++ b/sandbox/classloader-support/src/main/java/org/apache/tamaya/clsupport/internal/ServiceContainer.java
@@ -0,0 +1,287 @@
+/*
+ * 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.clsupport.internal;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.ref.WeakReference;
+import java.net.URL;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.ServiceConfigurationError;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Created by Anatole on 08.09.2015.
+ */
+class ServiceContainer {
+
+    private static final Logger LOG = Logger.getLogger(ServiceContainer.class.getName());
+
+    private static final String PREFIX = "META-INF/services/";
+
+    // The access control context taken when the ServiceLoader is created
+    private final AccessControlContext acc;
+
+    private WeakReference<ClassLoader> classLoaderRef;
+
+    /**
+     * List current services loaded using this classloader, per class.
+     */
+    private final Map<Class<?>, Map<String, Object>> servicesLoaded = new ConcurrentHashMap<>();
+
+    /**
+     * List current services loaded using this classloader, per class.
+     */
+    private final Map<Class, List<URL>> configsLoaded = new ConcurrentHashMap<>();
+
+    ServiceContainer(ClassLoader classLoader){
+        acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
+        this.classLoaderRef = new WeakReference<>(classLoader);
+    }
+
+    public ClassLoader getClassLoader(){
+        ClassLoader cl = classLoaderRef.get();
+        if(cl==null){
+            throw new IllegalStateException("Classloader reference removed, not active anynire.");
+        }
+        return cl;
+    }
+
+
+    public <T> void loadServices(Class<?> type,
+                                 Collection<ServiceContainer> preceedingContainers){
+        Map<String, Object> services = (Map<String, Object>)this.servicesLoaded.get(type);
+        if(services==null) {
+            services = new LinkedHashMap<>();
+            this.servicesLoaded.put(type, services);
+        }
+        loop:for(URL config:getConfigs(type)) {
+            for (ServiceContainer cont : preceedingContainers) {
+                if (cont.getConfigs(type).contains(config)) {
+                    LOG.finer("Ignoring already loaded config: " + config);
+                    continue loop;
+                }
+            }
+            Collection<String> serviceNames = parse(type, config);
+            for (String s : serviceNames) {
+                for (ServiceContainer cont : preceedingContainers) {
+                    if (cont.containsService(type, s)) {
+                        LOG.finest("Ignoring duplicate service: " + s);
+                        continue;
+                    }
+                }
+                LOG.info("Loading component: " + s);
+                services.put(s, create(type, s));
+            }
+        }
+    }
+
+    private Collection<URL> getConfigs(Class<?> type){
+        List<URL> result = this.configsLoaded.get(type);
+        if(result==null){
+            ClassLoader cl = this.classLoaderRef.get();
+            if(cl==null){
+                throw new IllegalStateException("CLassLoader dereferenced already.");
+            }
+            result = new ArrayList<>();
+            try {
+                Enumeration<URL> resources = cl.getResources(PREFIX + type.getName());
+                while (resources.hasMoreElements()) {
+                    result.add(resources.nextElement());
+                }
+            }
+            catch(Exception e){
+                LOG.log(Level.WARNING, "Failed to read service config for " + type.getName()+" from " + cl, e);
+            }
+            this.configsLoaded.put(type, result);
+            LOG.log(Level.FINE, "Found service config for " + type.getName() + ": " + result);
+        }
+        return result;
+    }
+
+    private boolean containsService(Class<?> type, String serviceClassName) {
+        Map<String,Object> services = servicesLoaded.get(type);
+        if(services==null){
+            return false;
+        }
+        return services.containsKey(serviceClassName);
+    }
+
+
+    private <S> S create(Class<S> serviceType, String className) {
+        Class<?> c = null;
+        ClassLoader classLoader = getClassLoader();
+        try {
+            c = Class.forName(className, false, classLoader);
+        } catch (ClassNotFoundException x) {
+            fail(serviceType,
+                    "Provider " + className + " not found");
+        }
+        if (!serviceType.isAssignableFrom(c)) {
+            fail(serviceType,
+                    "Provider " + className + " not a subtype");
+        }
+        try {
+            S p = serviceType.cast(c.newInstance());
+            return p;
+        } catch (Throwable x) {
+            fail(serviceType,
+                    "Provider " + className + " could not be instantiated",
+                    x);
+        }
+        throw new Error();          // This cannot happen
+    }
+
+    public <T> Collection<T> getServices(Class<T> serviceType) {
+        Map<String, Object> services = this.servicesLoaded.get(serviceType);
+        if(services!=null) {
+            return (Collection<T>)services.values();
+        }
+        return Collections.emptySet();
+    }
+
+    public boolean isTypeLoaded(Class<?> serviceType){
+        return this.servicesLoaded.containsKey(serviceType);
+    }
+
+    public Collection<URL> load(Class<?> serviceType) {
+        return load(serviceType, Collection.class.cast(Collections.emptySet()));
+    }
+
+    public Collection<URL> load(Class<?> serviceType, Collection<URL> configsLoaded) {
+        List<URL> result = new ArrayList<>();
+        try{
+            Enumeration<URL> resources = getClassLoader().getResources(PREFIX + serviceType.getName());
+            while(resources.hasMoreElements()){
+                URL res = resources.nextElement();
+                if(!configsLoaded.contains(res)){
+                    result.add(res);
+                }
+            }
+            return result;
+        }
+        catch(Exception e){
+            fail(serviceType, "Failed to load service config: " + PREFIX+serviceType.getName(), e);
+        }
+        return result;
+    }
+
+
+
+    // Parse a single line from the given configuration file, adding the name
+    // on the line to the names list.
+    //
+    private int parseLine(Class<?> serviceType, URL u, BufferedReader r, int lc,
+                          List<String> names)
+            throws IOException, ServiceConfigurationError {
+        String ln = r.readLine();
+        if (ln == null) {
+            return -1;
+        }
+        int ci = ln.indexOf('#');
+        if (ci >= 0) ln = ln.substring(0, ci);
+        ln = ln.trim();
+        int n = ln.length();
+        if (n != 0) {
+            if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0))
+                fail(serviceType, u, lc, "Illegal configuration-file syntax");
+            int cp = ln.codePointAt(0);
+            if (!Character.isJavaIdentifierStart(cp))
+                fail(serviceType, u, lc, "Illegal provider-class name: " + ln);
+            for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) {
+                cp = ln.codePointAt(i);
+                if (!Character.isJavaIdentifierPart(cp) && (cp != '.'))
+                    fail(serviceType, u, lc, "Illegal provider-class name: " + ln);
+            }
+            Map<String, Object> services = this.servicesLoaded.get(serviceType);
+            if (services == null || !services.containsKey(ln) && !names.contains(ln))
+                names.add(ln);
+        }
+        return lc + 1;
+    }
+
+
+    // Parse the content of the given URL as a provider-configuration file.
+    //
+    // @param  service
+    //         The service type for which providers are being sought;
+    //         used to construct error detail strings
+    //
+    // @param  u
+    //         The URL naming the configuration file to be parsed
+    //
+    // @return A (possibly empty) iterator that will yield the provider-class
+    //         names in the given configuration file that are not yet members
+    //         of the returned set
+    //
+    // @throws ServiceConfigurationError
+    //         If an I/O error occurs while reading from the given URL, or
+    //         if a configuration-file format error is detected
+    //
+    private Collection<String> parse(Class<?> service, URL u)
+            throws ServiceConfigurationError {
+        InputStream in = null;
+        BufferedReader r = null;
+        ArrayList<String> names = new ArrayList<>();
+        try {
+            in = u.openStream();
+            r = new BufferedReader(new InputStreamReader(in, "utf-8"));
+            int lc = 1;
+            while ((lc = parseLine(service, u, r, lc, names)) >= 0) ;
+        } catch (IOException x) {
+            fail(service, "Error reading configuration file", x);
+        } finally {
+            try {
+                if (r != null) r.close();
+                if (in != null) in.close();
+            } catch (IOException y) {
+                fail(service, "Error closing configuration file", y);
+            }
+        }
+        return names;
+    }
+
+
+    private static void fail(Class<?> service, String msg, Throwable cause)
+            throws ServiceConfigurationError {
+        LOG.log(Level.SEVERE, "Failed to load: " + service.getName() + ": " + msg, cause);
+    }
+
+    private static void fail(Class<?> service, String msg)
+            throws ServiceConfigurationError {
+        LOG.log(Level.SEVERE, "Failed to load: " + service.getName() + ": " + msg);
+    }
+
+    private static void fail(Class<?> service, URL u, int line, String msg)
+            throws ServiceConfigurationError {
+        fail(service, u + ":" + line + ": " + msg);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/459510e3/sandbox/classloader-support/src/main/resources/META-INF/services/org.apache.tamaya.spi.ServiceContext
----------------------------------------------------------------------
diff --git a/sandbox/classloader-support/src/main/resources/META-INF/services/org.apache.tamaya.spi.ServiceContext b/sandbox/classloader-support/src/main/resources/META-INF/services/org.apache.tamaya.spi.ServiceContext
new file mode 100644
index 0000000..c3b6acd
--- /dev/null
+++ b/sandbox/classloader-support/src/main/resources/META-INF/services/org.apache.tamaya.spi.ServiceContext
@@ -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 current 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.clsupport.internal.CLAwareServiceContext