TAMAYA-174: Move classloader stuff to its subfolder. * The project does not build yet, but does not belong into the root folder of the repo.
Project: http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/commit/c2d2c093 Tree: http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/tree/c2d2c093 Diff: http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/diff/c2d2c093 Branch: refs/heads/master Commit: c2d2c09302ec5ac9d37e73cd9f2f37563780123a Parents: 2fdbcc8 Author: Phil Ottlinger <pottlin...@apache.org> Authored: Fri Oct 14 20:40:46 2016 +0200 Committer: Phil Ottlinger <pottlin...@apache.org> Committed: Fri Oct 14 20:41:41 2016 +0200 ---------------------------------------------------------------------- pom.xml | 74 ----- .../AbstractClassloaderAwareItemLoader.java | 268 ---------------- .../clsupport/CLAwareConfigurationContext.java | 105 ------- .../tamaya/clsupport/CLAwareServiceContext.java | 248 --------------- .../tamaya/clsupport/ServiceContainer.java | 306 ------------------- .../apache/tamaya/clsupport/package-info.java | 22 -- .../org.apache.tamaya.spi.ServiceContext | 19 -- ...he.tamaya.environment.spi.ContextProviderSpi | 23 -- ...org.apache.tamaya.environment.spi.ContextSpi | 19 -- tamaya-classloader-support/pom.xml | 73 +++++ .../AbstractClassloaderAwareItemLoader.java | 268 ++++++++++++++++ .../clsupport/CLAwareConfigurationContext.java | 105 +++++++ .../tamaya/clsupport/CLAwareServiceContext.java | 248 +++++++++++++++ .../tamaya/clsupport/ServiceContainer.java | 306 +++++++++++++++++++ .../apache/tamaya/clsupport/package-info.java | 22 ++ .../org.apache.tamaya.spi.ServiceContext | 19 ++ ...he.tamaya.environment.spi.ContextProviderSpi | 23 ++ ...org.apache.tamaya.environment.spi.ContextSpi | 19 ++ tamaya-classloader-support/target/.plxarc | 1 + .../target/checkstyle-result.xml | 0 .../target/classes/META-INF/DEPENDENCIES | 19 ++ .../target/classes/META-INF/LICENSE | 202 ++++++++++++ .../target/classes/META-INF/NOTICE | 8 + .../org.apache.tamaya.spi.ServiceContext | 19 ++ .../AbstractClassloaderAwareItemLoader.class | Bin 0 -> 5277 bytes .../CLAwareConfigurationContext$1.class | Bin 0 -> 285 bytes ...areConfigurationContext$ContextManager.class | Bin 0 -> 1741 bytes .../clsupport/CLAwareConfigurationContext.class | Bin 0 -> 3312 bytes .../clsupport/CLAwareServiceContext.class | Bin 0 -> 7127 bytes .../tamaya/clsupport/ServiceContainer.class | Bin 0 -> 12391 bytes .../target/feature/feature.xml | 11 + .../target/findbugs-exclude.xml | 136 +++++++++ .../target/findbugsXml.xml | 2 + .../META-INF/DEPENDENCIES | 19 ++ .../META-INF/LICENSE | 202 ++++++++++++ .../META-INF/NOTICE | 8 + .../compile/default-compile/createdFiles.lst | 6 + .../compile/default-compile/inputFiles.lst | 5 + tamaya-classloader-support/target/rat.txt | 35 +++ 39 files changed, 1756 insertions(+), 1084 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/c2d2c093/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml deleted file mode 100644 index adea1d3..0000000 --- a/pom.xml +++ /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 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-extensions</artifactId> - <version>0.3-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>bundle</packaging> - - <properties> - <jdkVersion>1.7</jdkVersion> - </properties> - - <dependencies> - <dependency> - <groupId>org.apache.tamaya</groupId> - <artifactId>tamaya-api</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>org.apache.tamaya.ext</groupId> - <artifactId>tamaya-spisupport</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - </dependency> - </dependencies> - - <build> - <plugins> - <plugin> - <groupId>org.apache.felix</groupId> - <artifactId>maven-bundle-plugin</artifactId> - <extensions>true</extensions> - <configuration> - <instructions> - <Export-Package> - org.apache.tamaya.clsupport - </Export-Package> - </instructions> - </configuration> - </plugin> - </plugins> - </build> - -</project> http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/c2d2c093/src/main/java/org/apache/tamaya/clsupport/AbstractClassloaderAwareItemLoader.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/clsupport/AbstractClassloaderAwareItemLoader.java b/src/main/java/org/apache/tamaya/clsupport/AbstractClassloaderAwareItemLoader.java deleted file mode 100644 index 0145d1f..0000000 --- a/src/main/java/org/apache/tamaya/clsupport/AbstractClassloaderAwareItemLoader.java +++ /dev/null @@ -1,268 +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.clsupport; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; - - -/** - * <p>This class implements an abstract base class, which basically provides a loading mechanism that supports - * loading and managing resources along the classloader hierarchies individually. It ensures resources are loaded - * and stored related to the each target classloader within the hierarchy individually. Additionally it enables - * mechanisms to ensure an item T is not loaded multiple times, when traversing up the classloader hierarchy.</p> - * - * <p>Finally classloaders are not stored by reference by this class, to ensure they still can be garbage collected. - * Instead this class uses the fully qualified class name of the loader and the corresponsing hashCode as returned - * by {@link Objects#hashCode(Object)}.</p> - * - * @param <T> the managed item type. - */ -public abstract class AbstractClassloaderAwareItemLoader<T> { - /** - * The logger used. - */ - private static final Logger LOG = Logger.getLogger(AbstractClassloaderAwareItemLoader.class.getName()); - /** - * The items managed, related to their classloader. - */ - private final Map<String, T> items = new ConcurrentHashMap<>(); - - /** - * Creates a new instance, using the current Thread context classloader, or - if null - the classloader that - * loaded this class for initially initializing the loader instance. - */ - public AbstractClassloaderAwareItemLoader() { - this(getDefaultClassLoader()); - } - - /** - * Creates a new instance, using the class loader given for initializing the resources loaded. - * - * @param classLoader the target top level classloader, not null. - */ - public AbstractClassloaderAwareItemLoader(ClassLoader classLoader) { - loadItems(classLoader); - } - - /** - * Loads the items for the given classloader and all its parent classloaders. This method will not update - * the items already found for any class loader involved. - * - * @param classLoader the target top level classloader, not null. - */ - public void loadItems(ClassLoader classLoader) { - loadItems(classLoader, false); - } - - /** - * Loads the items for the given classloader and all its parent classloaders. - * - * @param classLoader the target top level classloader, not null. - * @param update if set to true, resources not visible on former runs are added during this load. - */ - public void loadItems(ClassLoader classLoader, boolean update) { - this.items.clear(); - List<ClassLoader> cls = new ArrayList<>(); - cls.add(classLoader); - ClassLoader cl = classLoader.getParent(); - while (cl != null) { - cls.add(cl); - cl = cl.getParent(); - } - // Start with the parent classloader and then go up... - for (int i = cls.size() - 1; i <= 0; i--) { - ClassLoader curCL = cls.get(i); - String clKey = getClassLoaderID(curCL); - T itemFound = items.get(clKey); - try { - if (itemFound != null) { - updateItem(itemFound, curCL); - } else { - items.put(clKey, createItem(curCL)); - } - } catch (Exception e) { - LOG.log(Level.SEVERE, - "Error loading from classloader: " + curCL, e); - } - } - } - - /** - * Creates a new item for being stored linked with the given lassloader. - * - * @param classLoader the classloader, not null. - * @return the new item loaded. - */ - protected abstract T createItem(ClassLoader classLoader); - - /** - * Creates a new item for being stored linked with the given lassloader. - * - * @param currentItemSet the current found ItemContainer instance to be updated. - * @param classLoader the classloader, not null. - */ - protected abstract void updateItem(T currentItemSet, ClassLoader classLoader); - - /** - * Evaluates a String key for identfying a classloader instance, based on the loader class and its hashCode. - * This prevents the storage of classloader references as keys and therefore enables classloaders not used anymore - * to be garbage collected. - * - * @param classLoader {@link ClassLoader} to be identified, must not be {@code null}. - * @return the unique key for the given classloader - */ - public static String getClassLoaderID(ClassLoader classLoader) { - return classLoader.getClass().getName() + Objects.hash(classLoader); - } - - /** - * Evaluates a String key for identfying a classloader instance, based on the loader class and its hashCode. - * This prevents the storage of classloader references as keys and therefore enables classloaders not used anymore - * to be garbage collected. - * - * @return the unique key for the current default classloader as returned by #getDefaultClassLoader. - */ - public static String getClassLoaderID() { - return getClassLoaderID(getDefaultClassLoader()); - } - - /** - * Get all items valid for the current thread context class loader, or - if null - the classloader that loaded - * this class. - * - * @return the items found, never null. - */ - public Set<T> getItems() { - return getItems(getDefaultClassLoader()); - } - - /** - * Get all items found for the given classloader and all its parent classloaders. - * - * @param classLoader the target top level classloader, not null. - * @return the items found, never null. - */ - public Set<T> getItems(ClassLoader classLoader) { - Set<T> result = new HashSet<>(); - ClassLoader cl = classLoader; - while (cl != null) { - T item = getItemNoParent(cl, true); - result.add(item); - cl = cl.getParent(); - } - return result; - } - - /** - * Get all items valid for the parent class loader of the current thread context class loader, or - if null - the - * parent of the classloader that loaded this class. This allows - * to build a delta list of instances only visible on the target classloader given. - * - * @return the items found, never null. - */ - public Set<T> getParentItems() { - return getParentItems(getDefaultClassLoader()); - } - - /** - * Get all items found for the parent of the given classloader and all its parent classloaders. This allows - * to build a delta list of instances only visible on the target classloader given. - * - * @param classLoader the target top level classloader, not null. - * @return the items found, never null. - */ - public Set<T> getParentItems(ClassLoader classLoader) { - Set<T> result = new HashSet<>(); - ClassLoader cl = classLoader.getParent(); - while (cl != null) { - T item = getItemNoParent(cl, true); - result.add(item); - cl = cl.getParent(); - } - return result; - } - - /** - * Return the item assigned to the current thread context class loader or - if null - the class that loaded - * this class. If not yet loaded this method will NOT trigger a load. - * - * @return the item attached, or null. - */ - public T getItemNoParent() { - return getItemNoParent(getDefaultClassLoader(), false); - } - - /** - * Return the item assigned to the current thread context class loader or - if null - the class that loaded - * this class. - * - * @param loadIfMissing Flag that allows to define if this method will trigger an item load, when no item is loaded - * for the current class loader. - * @return the item attached, or null. - */ - public T getItemNoParent(boolean loadIfMissing) { - return getItemNoParent(getDefaultClassLoader(), loadIfMissing); - } - - /** - * Return the item assigned to the current thread context class loader or - if null - the class that loaded - * this class. - * - * @param classLoader the target top level classloader, not null. - * @param loadIfMissing Flag that allows to define if this method will trigger an item load, when no item is loaded - * for the current class loader. - * @return the item attached, or null. If {@code loadIfMissing} is set to true, the result is normally not to be - * expected to be null. - */ - public T getItemNoParent(ClassLoader classLoader, boolean loadIfMissing) { - String clKey = getClassLoaderID(classLoader); - T item = items.get(clKey); - if (item == null) { - if (loadIfMissing) { - item = createItem(classLoader); - this.items.put(clKey, item); - } - } - return item; - } - - - /** - * Utility method that either returns the current thread context classloader or - * - if not available - the classloader that loaded this class. - * @return the default classloader to be used, if no explicit classloader has been passed. - */ - public static ClassLoader getDefaultClassLoader() { - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - if (cl == null) { - cl = AbstractClassloaderAwareItemLoader.class.getClassLoader(); - } - return cl; - } - - -} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/c2d2c093/src/main/java/org/apache/tamaya/clsupport/CLAwareConfigurationContext.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/clsupport/CLAwareConfigurationContext.java b/src/main/java/org/apache/tamaya/clsupport/CLAwareConfigurationContext.java deleted file mode 100644 index 94e5fb5..0000000 --- a/src/main/java/org/apache/tamaya/clsupport/CLAwareConfigurationContext.java +++ /dev/null @@ -1,105 +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.clsupport; - -import org.apache.tamaya.TypeLiteral; -import org.apache.tamaya.spisupport.DefaultConfigurationContext; -import org.apache.tamaya.spi.ConfigurationContext; -import org.apache.tamaya.spi.ConfigurationContextBuilder; -import org.apache.tamaya.spi.PropertyConverter; -import org.apache.tamaya.spi.PropertyFilter; -import org.apache.tamaya.spi.PropertySource; -import org.apache.tamaya.spi.PropertyValueCombinationPolicy; - -import javax.annotation.Priority; -import java.util.List; -import java.util.Map; -import java.util.logging.Logger; - -/** - * Default Implementation of a simple ConfigurationContext. - */ -@Priority(100) -public class CLAwareConfigurationContext implements ConfigurationContext { - - /** The logger used. */ - private final static Logger LOG = Logger.getLogger(CLAwareConfigurationContext.class.getName()); - - private final ContextManager contextManager = new ContextManager(); - - - @Override - public void addPropertySources(PropertySource... propertySourcesToAdd) { - contextManager.getItemNoParent(true).addPropertySources(propertySourcesToAdd); - } - - @Override - public List<PropertySource> getPropertySources() { - return contextManager.getItemNoParent(true).getPropertySources(); - } - - @Override - public <T> void addPropertyConverter(TypeLiteral<T> typeToConvert, PropertyConverter<T> propertyConverter) { - contextManager.getItemNoParent(true).addPropertyConverter(typeToConvert, propertyConverter); - } - - @Override - public Map<TypeLiteral<?>, List<PropertyConverter<?>>> getPropertyConverters() { - return contextManager.getItemNoParent(true).getPropertyConverters(); - } - - @Override - public <T> List<PropertyConverter<T>> getPropertyConverters(TypeLiteral<T> targetType) { - return contextManager.getItemNoParent(true).getPropertyConverters(targetType); - } - - @Override - public List<PropertyFilter> getPropertyFilters() { - return contextManager.getItemNoParent(true).getPropertyFilters(); - } - - @Override - public PropertyValueCombinationPolicy getPropertyValueCombinationPolicy(){ - return contextManager.getItemNoParent(true).getPropertyValueCombinationPolicy(); - } - - @Override - public ConfigurationContextBuilder toBuilder() { - return contextManager.getItemNoParent(true).toBuilder(); - } - - - /** - * Subcomponent managing {@link ConfigurationContext} instances, one per classloader. - */ - private static final class ContextManager extends AbstractClassloaderAwareItemLoader<ConfigurationContext>{ - - @Override - protected ConfigurationContext createItem(ClassLoader classLoader) { - // Simply create a complete configuration manager for every classloader. Maybe we will optimize this at a - // later stage in the project but as for now it is the most simple working solution. - return new DefaultConfigurationContext(); - } - - @Override - protected void updateItem(ConfigurationContext currentItemSet, ClassLoader classLoader) { - // ignore, currently not supported. - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/c2d2c093/src/main/java/org/apache/tamaya/clsupport/CLAwareServiceContext.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/clsupport/CLAwareServiceContext.java b/src/main/java/org/apache/tamaya/clsupport/CLAwareServiceContext.java deleted file mode 100644 index a5d1700..0000000 --- a/src/main/java/org/apache/tamaya/clsupport/CLAwareServiceContext.java +++ /dev/null @@ -1,248 +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.clsupport; - -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.logging.Level; -import java.util.logging.Logger; - - -/** - * <p>This class implements a {@link ServiceContext}, which basically provides a similar loading mechanism as used - * by the {@link java.util.ServiceLoader}. Whereas the {@link java.util.ServiceLoader} only loads configurations - * and instances from one classloader, this loader manages configs found and the related instances for each - * classloader along the classloader hierarchies individually. It ensures instances are loaded on the classloader - * level, where they first are visible. Additionally it ensures the same configuration resource (and its - * declared services) are loaded multiple times, when going up the classloader hierarchy.</p> - * - * <p>Finally classloaders are not stored by reference by this class, to ensure they still can be garbage collected. - * Refer also the inherited parent class for further details.</p> - * - * <p>This class uses an ordinal of {@code 10}, so it overrides any default {@link ServiceContext} implementations - * provided with the Tamaya core modules.</p> - */ -@Priority(10) -public class CLAwareServiceContext extends AbstractClassloaderAwareItemLoader<ServiceContainer> - implements ServiceContext{ - - private static final Logger LOG = Logger.getLogger(CLAwareServiceContext.class.getName()); - - /** - * Default location for service loader files. - */ - private static final String PREFIX = "META-INF/services/"; - - /** - * Constructor, using the current default classloader as defined by - * {@link AbstractClassloaderAwareItemLoader#getDefaultClassLoader()}. - */ - public CLAwareServiceContext(){ - super(); - } - - /** - * Constructor, using the given classloader. - * @param classLoader the target classloader for initializing of services, not null. - */ - public CLAwareServiceContext(ClassLoader classLoader) { - super(classLoader); - } - - - /** - * Implementation that creates a {@link ServiceContainer}, which manages all configs and instances loaded - * for a given classloader. - * @param classLoader the classloader, not null. - * @return a new empty, {@link ServiceContainer} instance. - */ - @Override - protected ServiceContainer createItem(ClassLoader classLoader) { - if(LOG.isLoggable(Level.INFO)) { - LOG.info("Loading services for classloader: " + classLoader); - } - return new ServiceContainer(classLoader); - } - - @Override - protected void updateItem(ServiceContainer currentContainer, ClassLoader classLoader) { - // nothing to be done here, since we dont have a specific target type. - } - - @Override - public int ordinal() { - return 10; - } - - /** - * This method tries to evaluate the current singleton from the {@link ServiceContainer} attached to the - * current classloader. If not found the singleton instance is evaluated based on the priorities - * assigned for all known providers. The resulting instance is then cached and always returned as - * singleton instance fomr this loader, when the same current classloader instance is active. - * @param serviceType the service type. - * @param <T> the type - * @return the item found, or null. - */ - @Override - public <T> T getService(Class<T> serviceType) { - return getService(serviceType, getDefaultClassLoader()); - } - - /** - * Evaluates the current singleton instance using the given classloader context. - * @param serviceType the service type. - * @param classLoader the classloader, not null. - * @param <T> the type - * @return the item found, or null. - */ - public <T> T getService(Class<T> serviceType, ClassLoader classLoader) { - if(LOG.isLoggable(Level.INFO)) { - LOG.info("Evaluating services for classloader: " + classLoader); - } - ServiceContainer container = getItemNoParent(classLoader, true); - T singleton = container.getSingleton(serviceType); - if(singleton!=null){ - if(LOG.isLoggable(Level.FINEST)) { - LOG.finest("Evaluated singleton of type " + serviceType.getName() + " to " + singleton); - } - return singleton; - } - Collection<? extends T> services = getServices(serviceType, classLoader); - if (services.isEmpty()) { - singleton = null; - } else { - singleton = getServiceWithHighestPriority(services, serviceType); - } - if(singleton!=null) { - container.setSingleton(serviceType, singleton); - } - if(LOG.isLoggable(Level.FINEST)) { - LOG.finest("Evaluated singleton of type " + serviceType.getName() + " to " + singleton); - } - return singleton; - } - - /** - * Gets the services visible. - * @param serviceType - * the service type. - * @param <T> the type param - * @return the services visible for the current classloader. - */ - @Override - public <T> List<T> getServices(Class<T> serviceType) { - return getServices(serviceType, AbstractClassloaderAwareItemLoader.getDefaultClassLoader()); - } - - /** - * Gets the services visible. - * @param serviceType the service type. - * @param classLoader the classloader - * @param <T> the type param - * @return the services visible for the current classloader. - */ - public <T> List<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); - } - services.addAll(container.getServices(serviceType)); - prevContainers.add(container); - } - if(LOG.isLoggable(Level.FINEST)) { - LOG.finest("Evaluated services of type " + serviceType.getName() + " to " + services); - } - 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(); - } - if(LOG.isLoggable(Level.FINEST)) { - LOG.finest("Evaluated priority for " + o.getClass().getName() + " to " + prio); - } - return prio; - } - -} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/c2d2c093/src/main/java/org/apache/tamaya/clsupport/ServiceContainer.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/clsupport/ServiceContainer.java b/src/main/java/org/apache/tamaya/clsupport/ServiceContainer.java deleted file mode 100644 index 23e73c0..0000000 --- a/src/main/java/org/apache/tamaya/clsupport/ServiceContainer.java +++ /dev/null @@ -1,306 +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.clsupport; - -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; - -/** - * Classloader managed ServiceContainer. - */ -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 final WeakReference<ClassLoader> classLoaderRef; - - /** - * List current services loaded using this classloader, per class. - */ - private final Map<Class<?>, Map<String, Object>> servicesLoaded = new ConcurrentHashMap<>(); - /** - * The cached singletons for the given classloader. - */ - private final Map<Class, Object> singletons = 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 = 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); - } - } - 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); - return services != null && 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 { - return serviceType.cast(c.newInstance()); - } 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) { - // go ahead - } - } 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); - } - - public <T> T getSingleton(Class<T> serviceType) { - return (T) this.singletons.get(serviceType); - } - - <T> void setSingleton(Class<T> type, T instance) { - LOG.info("Caching singleton for " + type.getName() + " and classloader: " + - getClassLoader().toString() + ": " + instance); - this.singletons.put(type, instance); - } -} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/c2d2c093/src/main/java/org/apache/tamaya/clsupport/package-info.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/tamaya/clsupport/package-info.java b/src/main/java/org/apache/tamaya/clsupport/package-info.java deleted file mode 100644 index d8e3953..0000000 --- a/src/main/java/org/apache/tamaya/clsupport/package-info.java +++ /dev/null @@ -1,22 +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. - */ -/** - * Programmatic API of the classloader support. - */ -package org.apache.tamaya.clsupport; \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/c2d2c093/src/main/resources/META-INF/services/org.apache.tamaya.spi.ServiceContext ---------------------------------------------------------------------- diff --git a/src/main/resources/META-INF/services/org.apache.tamaya.spi.ServiceContext b/src/main/resources/META-INF/services/org.apache.tamaya.spi.ServiceContext deleted file mode 100644 index 7016afb..0000000 --- a/src/main/resources/META-INF/services/org.apache.tamaya.spi.ServiceContext +++ /dev/null @@ -1,19 +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 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.CLAwareServiceContext http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/c2d2c093/src/test/resources/META-INF/services/org.apache.tamaya.environment.spi.ContextProviderSpi ---------------------------------------------------------------------- diff --git a/src/test/resources/META-INF/services/org.apache.tamaya.environment.spi.ContextProviderSpi b/src/test/resources/META-INF/services/org.apache.tamaya.environment.spi.ContextProviderSpi deleted file mode 100644 index 7f71c15..0000000 --- a/src/test/resources/META-INF/services/org.apache.tamaya.environment.spi.ContextProviderSpi +++ /dev/null @@ -1,23 +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 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.environment.internal.ClassLoaderDependentAppEnvironmentProvider -org.apache.tamaya.environment.internal.ClassLoaderDependentEarEnvironmentProvider -org.apache.tamaya.environment.internal.InitialEnvironmentProviderSpi -org.apache.tamaya.environment.internal.SystemClassLoaderEnvironmentProviderSpi -org.apache.tamaya.metamodel.environment.TestEnvironmentProvider \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/c2d2c093/src/test/resources/META-INF/services/org.apache.tamaya.environment.spi.ContextSpi ---------------------------------------------------------------------- diff --git a/src/test/resources/META-INF/services/org.apache.tamaya.environment.spi.ContextSpi b/src/test/resources/META-INF/services/org.apache.tamaya.environment.spi.ContextSpi deleted file mode 100644 index 166dd67..0000000 --- a/src/test/resources/META-INF/services/org.apache.tamaya.environment.spi.ContextSpi +++ /dev/null @@ -1,19 +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 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.environment.internal.SingleEnvironmentManager http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/c2d2c093/tamaya-classloader-support/pom.xml ---------------------------------------------------------------------- diff --git a/tamaya-classloader-support/pom.xml b/tamaya-classloader-support/pom.xml new file mode 100644 index 0000000..2add22a --- /dev/null +++ b/tamaya-classloader-support/pom.xml @@ -0,0 +1,73 @@ +<!-- +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-extensions</artifactId> + <version>0.3-incubating-SNAPSHOT</version> + </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>bundle</packaging> + + <properties> + <jdkVersion>1.7</jdkVersion> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.tamaya</groupId> + <artifactId>tamaya-api</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.tamaya.ext</groupId> + <artifactId>tamaya-spisupport</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Export-Package> + org.apache.tamaya.clsupport + </Export-Package> + </instructions> + </configuration> + </plugin> + </plugins> + </build> + +</project> http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/c2d2c093/tamaya-classloader-support/src/main/java/org/apache/tamaya/clsupport/AbstractClassloaderAwareItemLoader.java ---------------------------------------------------------------------- diff --git a/tamaya-classloader-support/src/main/java/org/apache/tamaya/clsupport/AbstractClassloaderAwareItemLoader.java b/tamaya-classloader-support/src/main/java/org/apache/tamaya/clsupport/AbstractClassloaderAwareItemLoader.java new file mode 100644 index 0000000..0145d1f --- /dev/null +++ b/tamaya-classloader-support/src/main/java/org/apache/tamaya/clsupport/AbstractClassloaderAwareItemLoader.java @@ -0,0 +1,268 @@ +/* + * 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; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + + +/** + * <p>This class implements an abstract base class, which basically provides a loading mechanism that supports + * loading and managing resources along the classloader hierarchies individually. It ensures resources are loaded + * and stored related to the each target classloader within the hierarchy individually. Additionally it enables + * mechanisms to ensure an item T is not loaded multiple times, when traversing up the classloader hierarchy.</p> + * + * <p>Finally classloaders are not stored by reference by this class, to ensure they still can be garbage collected. + * Instead this class uses the fully qualified class name of the loader and the corresponsing hashCode as returned + * by {@link Objects#hashCode(Object)}.</p> + * + * @param <T> the managed item type. + */ +public abstract class AbstractClassloaderAwareItemLoader<T> { + /** + * The logger used. + */ + private static final Logger LOG = Logger.getLogger(AbstractClassloaderAwareItemLoader.class.getName()); + /** + * The items managed, related to their classloader. + */ + private final Map<String, T> items = new ConcurrentHashMap<>(); + + /** + * Creates a new instance, using the current Thread context classloader, or - if null - the classloader that + * loaded this class for initially initializing the loader instance. + */ + public AbstractClassloaderAwareItemLoader() { + this(getDefaultClassLoader()); + } + + /** + * Creates a new instance, using the class loader given for initializing the resources loaded. + * + * @param classLoader the target top level classloader, not null. + */ + public AbstractClassloaderAwareItemLoader(ClassLoader classLoader) { + loadItems(classLoader); + } + + /** + * Loads the items for the given classloader and all its parent classloaders. This method will not update + * the items already found for any class loader involved. + * + * @param classLoader the target top level classloader, not null. + */ + public void loadItems(ClassLoader classLoader) { + loadItems(classLoader, false); + } + + /** + * Loads the items for the given classloader and all its parent classloaders. + * + * @param classLoader the target top level classloader, not null. + * @param update if set to true, resources not visible on former runs are added during this load. + */ + public void loadItems(ClassLoader classLoader, boolean update) { + this.items.clear(); + List<ClassLoader> cls = new ArrayList<>(); + cls.add(classLoader); + ClassLoader cl = classLoader.getParent(); + while (cl != null) { + cls.add(cl); + cl = cl.getParent(); + } + // Start with the parent classloader and then go up... + for (int i = cls.size() - 1; i <= 0; i--) { + ClassLoader curCL = cls.get(i); + String clKey = getClassLoaderID(curCL); + T itemFound = items.get(clKey); + try { + if (itemFound != null) { + updateItem(itemFound, curCL); + } else { + items.put(clKey, createItem(curCL)); + } + } catch (Exception e) { + LOG.log(Level.SEVERE, + "Error loading from classloader: " + curCL, e); + } + } + } + + /** + * Creates a new item for being stored linked with the given lassloader. + * + * @param classLoader the classloader, not null. + * @return the new item loaded. + */ + protected abstract T createItem(ClassLoader classLoader); + + /** + * Creates a new item for being stored linked with the given lassloader. + * + * @param currentItemSet the current found ItemContainer instance to be updated. + * @param classLoader the classloader, not null. + */ + protected abstract void updateItem(T currentItemSet, ClassLoader classLoader); + + /** + * Evaluates a String key for identfying a classloader instance, based on the loader class and its hashCode. + * This prevents the storage of classloader references as keys and therefore enables classloaders not used anymore + * to be garbage collected. + * + * @param classLoader {@link ClassLoader} to be identified, must not be {@code null}. + * @return the unique key for the given classloader + */ + public static String getClassLoaderID(ClassLoader classLoader) { + return classLoader.getClass().getName() + Objects.hash(classLoader); + } + + /** + * Evaluates a String key for identfying a classloader instance, based on the loader class and its hashCode. + * This prevents the storage of classloader references as keys and therefore enables classloaders not used anymore + * to be garbage collected. + * + * @return the unique key for the current default classloader as returned by #getDefaultClassLoader. + */ + public static String getClassLoaderID() { + return getClassLoaderID(getDefaultClassLoader()); + } + + /** + * Get all items valid for the current thread context class loader, or - if null - the classloader that loaded + * this class. + * + * @return the items found, never null. + */ + public Set<T> getItems() { + return getItems(getDefaultClassLoader()); + } + + /** + * Get all items found for the given classloader and all its parent classloaders. + * + * @param classLoader the target top level classloader, not null. + * @return the items found, never null. + */ + public Set<T> getItems(ClassLoader classLoader) { + Set<T> result = new HashSet<>(); + ClassLoader cl = classLoader; + while (cl != null) { + T item = getItemNoParent(cl, true); + result.add(item); + cl = cl.getParent(); + } + return result; + } + + /** + * Get all items valid for the parent class loader of the current thread context class loader, or - if null - the + * parent of the classloader that loaded this class. This allows + * to build a delta list of instances only visible on the target classloader given. + * + * @return the items found, never null. + */ + public Set<T> getParentItems() { + return getParentItems(getDefaultClassLoader()); + } + + /** + * Get all items found for the parent of the given classloader and all its parent classloaders. This allows + * to build a delta list of instances only visible on the target classloader given. + * + * @param classLoader the target top level classloader, not null. + * @return the items found, never null. + */ + public Set<T> getParentItems(ClassLoader classLoader) { + Set<T> result = new HashSet<>(); + ClassLoader cl = classLoader.getParent(); + while (cl != null) { + T item = getItemNoParent(cl, true); + result.add(item); + cl = cl.getParent(); + } + return result; + } + + /** + * Return the item assigned to the current thread context class loader or - if null - the class that loaded + * this class. If not yet loaded this method will NOT trigger a load. + * + * @return the item attached, or null. + */ + public T getItemNoParent() { + return getItemNoParent(getDefaultClassLoader(), false); + } + + /** + * Return the item assigned to the current thread context class loader or - if null - the class that loaded + * this class. + * + * @param loadIfMissing Flag that allows to define if this method will trigger an item load, when no item is loaded + * for the current class loader. + * @return the item attached, or null. + */ + public T getItemNoParent(boolean loadIfMissing) { + return getItemNoParent(getDefaultClassLoader(), loadIfMissing); + } + + /** + * Return the item assigned to the current thread context class loader or - if null - the class that loaded + * this class. + * + * @param classLoader the target top level classloader, not null. + * @param loadIfMissing Flag that allows to define if this method will trigger an item load, when no item is loaded + * for the current class loader. + * @return the item attached, or null. If {@code loadIfMissing} is set to true, the result is normally not to be + * expected to be null. + */ + public T getItemNoParent(ClassLoader classLoader, boolean loadIfMissing) { + String clKey = getClassLoaderID(classLoader); + T item = items.get(clKey); + if (item == null) { + if (loadIfMissing) { + item = createItem(classLoader); + this.items.put(clKey, item); + } + } + return item; + } + + + /** + * Utility method that either returns the current thread context classloader or + * - if not available - the classloader that loaded this class. + * @return the default classloader to be used, if no explicit classloader has been passed. + */ + public static ClassLoader getDefaultClassLoader() { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if (cl == null) { + cl = AbstractClassloaderAwareItemLoader.class.getClassLoader(); + } + return cl; + } + + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/c2d2c093/tamaya-classloader-support/src/main/java/org/apache/tamaya/clsupport/CLAwareConfigurationContext.java ---------------------------------------------------------------------- diff --git a/tamaya-classloader-support/src/main/java/org/apache/tamaya/clsupport/CLAwareConfigurationContext.java b/tamaya-classloader-support/src/main/java/org/apache/tamaya/clsupport/CLAwareConfigurationContext.java new file mode 100644 index 0000000..94e5fb5 --- /dev/null +++ b/tamaya-classloader-support/src/main/java/org/apache/tamaya/clsupport/CLAwareConfigurationContext.java @@ -0,0 +1,105 @@ +/* + * 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; + +import org.apache.tamaya.TypeLiteral; +import org.apache.tamaya.spisupport.DefaultConfigurationContext; +import org.apache.tamaya.spi.ConfigurationContext; +import org.apache.tamaya.spi.ConfigurationContextBuilder; +import org.apache.tamaya.spi.PropertyConverter; +import org.apache.tamaya.spi.PropertyFilter; +import org.apache.tamaya.spi.PropertySource; +import org.apache.tamaya.spi.PropertyValueCombinationPolicy; + +import javax.annotation.Priority; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; + +/** + * Default Implementation of a simple ConfigurationContext. + */ +@Priority(100) +public class CLAwareConfigurationContext implements ConfigurationContext { + + /** The logger used. */ + private final static Logger LOG = Logger.getLogger(CLAwareConfigurationContext.class.getName()); + + private final ContextManager contextManager = new ContextManager(); + + + @Override + public void addPropertySources(PropertySource... propertySourcesToAdd) { + contextManager.getItemNoParent(true).addPropertySources(propertySourcesToAdd); + } + + @Override + public List<PropertySource> getPropertySources() { + return contextManager.getItemNoParent(true).getPropertySources(); + } + + @Override + public <T> void addPropertyConverter(TypeLiteral<T> typeToConvert, PropertyConverter<T> propertyConverter) { + contextManager.getItemNoParent(true).addPropertyConverter(typeToConvert, propertyConverter); + } + + @Override + public Map<TypeLiteral<?>, List<PropertyConverter<?>>> getPropertyConverters() { + return contextManager.getItemNoParent(true).getPropertyConverters(); + } + + @Override + public <T> List<PropertyConverter<T>> getPropertyConverters(TypeLiteral<T> targetType) { + return contextManager.getItemNoParent(true).getPropertyConverters(targetType); + } + + @Override + public List<PropertyFilter> getPropertyFilters() { + return contextManager.getItemNoParent(true).getPropertyFilters(); + } + + @Override + public PropertyValueCombinationPolicy getPropertyValueCombinationPolicy(){ + return contextManager.getItemNoParent(true).getPropertyValueCombinationPolicy(); + } + + @Override + public ConfigurationContextBuilder toBuilder() { + return contextManager.getItemNoParent(true).toBuilder(); + } + + + /** + * Subcomponent managing {@link ConfigurationContext} instances, one per classloader. + */ + private static final class ContextManager extends AbstractClassloaderAwareItemLoader<ConfigurationContext>{ + + @Override + protected ConfigurationContext createItem(ClassLoader classLoader) { + // Simply create a complete configuration manager for every classloader. Maybe we will optimize this at a + // later stage in the project but as for now it is the most simple working solution. + return new DefaultConfigurationContext(); + } + + @Override + protected void updateItem(ConfigurationContext currentItemSet, ClassLoader classLoader) { + // ignore, currently not supported. + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/c2d2c093/tamaya-classloader-support/src/main/java/org/apache/tamaya/clsupport/CLAwareServiceContext.java ---------------------------------------------------------------------- diff --git a/tamaya-classloader-support/src/main/java/org/apache/tamaya/clsupport/CLAwareServiceContext.java b/tamaya-classloader-support/src/main/java/org/apache/tamaya/clsupport/CLAwareServiceContext.java new file mode 100644 index 0000000..a5d1700 --- /dev/null +++ b/tamaya-classloader-support/src/main/java/org/apache/tamaya/clsupport/CLAwareServiceContext.java @@ -0,0 +1,248 @@ +/* + * 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; + +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.logging.Level; +import java.util.logging.Logger; + + +/** + * <p>This class implements a {@link ServiceContext}, which basically provides a similar loading mechanism as used + * by the {@link java.util.ServiceLoader}. Whereas the {@link java.util.ServiceLoader} only loads configurations + * and instances from one classloader, this loader manages configs found and the related instances for each + * classloader along the classloader hierarchies individually. It ensures instances are loaded on the classloader + * level, where they first are visible. Additionally it ensures the same configuration resource (and its + * declared services) are loaded multiple times, when going up the classloader hierarchy.</p> + * + * <p>Finally classloaders are not stored by reference by this class, to ensure they still can be garbage collected. + * Refer also the inherited parent class for further details.</p> + * + * <p>This class uses an ordinal of {@code 10}, so it overrides any default {@link ServiceContext} implementations + * provided with the Tamaya core modules.</p> + */ +@Priority(10) +public class CLAwareServiceContext extends AbstractClassloaderAwareItemLoader<ServiceContainer> + implements ServiceContext{ + + private static final Logger LOG = Logger.getLogger(CLAwareServiceContext.class.getName()); + + /** + * Default location for service loader files. + */ + private static final String PREFIX = "META-INF/services/"; + + /** + * Constructor, using the current default classloader as defined by + * {@link AbstractClassloaderAwareItemLoader#getDefaultClassLoader()}. + */ + public CLAwareServiceContext(){ + super(); + } + + /** + * Constructor, using the given classloader. + * @param classLoader the target classloader for initializing of services, not null. + */ + public CLAwareServiceContext(ClassLoader classLoader) { + super(classLoader); + } + + + /** + * Implementation that creates a {@link ServiceContainer}, which manages all configs and instances loaded + * for a given classloader. + * @param classLoader the classloader, not null. + * @return a new empty, {@link ServiceContainer} instance. + */ + @Override + protected ServiceContainer createItem(ClassLoader classLoader) { + if(LOG.isLoggable(Level.INFO)) { + LOG.info("Loading services for classloader: " + classLoader); + } + return new ServiceContainer(classLoader); + } + + @Override + protected void updateItem(ServiceContainer currentContainer, ClassLoader classLoader) { + // nothing to be done here, since we dont have a specific target type. + } + + @Override + public int ordinal() { + return 10; + } + + /** + * This method tries to evaluate the current singleton from the {@link ServiceContainer} attached to the + * current classloader. If not found the singleton instance is evaluated based on the priorities + * assigned for all known providers. The resulting instance is then cached and always returned as + * singleton instance fomr this loader, when the same current classloader instance is active. + * @param serviceType the service type. + * @param <T> the type + * @return the item found, or null. + */ + @Override + public <T> T getService(Class<T> serviceType) { + return getService(serviceType, getDefaultClassLoader()); + } + + /** + * Evaluates the current singleton instance using the given classloader context. + * @param serviceType the service type. + * @param classLoader the classloader, not null. + * @param <T> the type + * @return the item found, or null. + */ + public <T> T getService(Class<T> serviceType, ClassLoader classLoader) { + if(LOG.isLoggable(Level.INFO)) { + LOG.info("Evaluating services for classloader: " + classLoader); + } + ServiceContainer container = getItemNoParent(classLoader, true); + T singleton = container.getSingleton(serviceType); + if(singleton!=null){ + if(LOG.isLoggable(Level.FINEST)) { + LOG.finest("Evaluated singleton of type " + serviceType.getName() + " to " + singleton); + } + return singleton; + } + Collection<? extends T> services = getServices(serviceType, classLoader); + if (services.isEmpty()) { + singleton = null; + } else { + singleton = getServiceWithHighestPriority(services, serviceType); + } + if(singleton!=null) { + container.setSingleton(serviceType, singleton); + } + if(LOG.isLoggable(Level.FINEST)) { + LOG.finest("Evaluated singleton of type " + serviceType.getName() + " to " + singleton); + } + return singleton; + } + + /** + * Gets the services visible. + * @param serviceType + * the service type. + * @param <T> the type param + * @return the services visible for the current classloader. + */ + @Override + public <T> List<T> getServices(Class<T> serviceType) { + return getServices(serviceType, AbstractClassloaderAwareItemLoader.getDefaultClassLoader()); + } + + /** + * Gets the services visible. + * @param serviceType the service type. + * @param classLoader the classloader + * @param <T> the type param + * @return the services visible for the current classloader. + */ + public <T> List<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); + } + services.addAll(container.getServices(serviceType)); + prevContainers.add(container); + } + if(LOG.isLoggable(Level.FINEST)) { + LOG.finest("Evaluated services of type " + serviceType.getName() + " to " + services); + } + 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(); + } + if(LOG.isLoggable(Level.FINEST)) { + LOG.finest("Evaluated priority for " + o.getClass().getName() + " to " + prio); + } + return prio; + } + +}