/* * Copyright (C) 2010 Brockmann Consult GmbH (info@brockmann-consult.de) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) * any later version. * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, see http://www.gnu.org/licenses/ */ package com.bc.ceres.core.runtime.internal; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; /** * A class loader used for modules. It adds the following features to the {@link URLClassLoader}. * <ol> * <li>Besides a parent class loader, it can have multiple delegate class loaders.</li> * <li>It can find and load native libraries from arbitrary locations.</li> * </ol> */ class ModuleClassLoader extends URLClassLoader { private ClassLoader[] delegates; private URL[] nativeUrls; private Map<ClassLoader, Map<String, List<URL>>> resolvedResources; public ModuleClassLoader(ClassLoader[] delegates, URL[] dependencyUrls, URL[] nativeUrls, ClassLoader parent) { super(dependencyUrls, parent); this.nativeUrls = nativeUrls; this.delegates = delegates; resolvedResources = new HashMap<>(); } @Override protected String findLibrary(String libname) { for (URL url : nativeUrls) { if (url.toExternalForm().endsWith(System.mapLibraryName(libname))) { String absolutePath = UrlHelper.urlToFile(url).getAbsolutePath(); Logger logger = Logger.getLogger(System.getProperty("ceres.context", "ceres")); Throwable throwable = new Throwable("This is not an exception."); logger.log(Level.FINEST, "Native library found: " + absolutePath, throwable); return absolutePath; } } for (ClassLoader classLoader : delegates) { if (classLoader instanceof ModuleClassLoader) { String path = ((ModuleClassLoader) classLoader).findLibrary(libname); if (path != null) { return path; } } } ClassLoader parent = getParent(); if (parent instanceof ModuleClassLoader) { // Must cast, otherwise we cannot call protected ClassLoader.findLibrary() method String path = ((ModuleClassLoader) parent).findLibrary(libname); if (path != null) { return path; } } return super.findLibrary(libname); } @Override public URL findResource(final String name) { final URL localResource = super.findResource(name); if(localResource != null) { return localResource; } for (ClassLoader delegate : delegates) { URL resource = delegate.getResource(name); if (resource != null) { return resource; } } return null; } @Override public Enumeration<URL> findResources(final String name) throws IOException { final Enumeration<URL> resources = super.findResources(name); if (delegates.length == 0) { return resources; } final Set<URL> urls = new HashSet<>(Collections.list(resources)); for (ClassLoader delegate : delegates) { final Map<String, List<URL>> resourceMap = getResourceMap(delegate); if(resourceMap.containsKey(name)) { urls.addAll(resourceMap.get(name)); }else { final List<URL> urlArrayList = Collections.list(delegate.getResources(name)); urls.addAll(urlArrayList); resourceMap.put(name, urlArrayList); } } return Collections.enumeration(urls) ; } private Map<String, List<URL>> getResourceMap(ClassLoader delegate) { Map<String, List<URL>> resourceMap; if(resolvedResources.containsKey(delegate)) { resourceMap = resolvedResources.get(delegate); }else { resourceMap = new HashMap<>(); resolvedResources.put(delegate, resourceMap); } return resourceMap; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { try { return super.findClass(name); } catch (ClassNotFoundException e) { for (ClassLoader delegate : delegates) { try { return delegate.loadClass(name); } catch (ClassNotFoundException e2) { // ignore, we still can try more } } throw e; } } public ClassLoader[] getDelegates() { return delegates; } public URL[] getNativeUrls() { return nativeUrls; } }