blob: df464dc87dad9fd677568340b69134332dd371f6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2017 Christian Pontesegger and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License_Identifier: EPL-2.0
*
* Contributors:
* Christian Pontesegger - initial API and implementation
*******************************************************************************/
package org.eclipse.ease.classloader;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.jobs.Job;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.wiring.BundleWiring;
/**
* A classloader using 'Eclipse-BuddyPolicy: global' as class loading strategy. It further allows to register additional jar files to be looked up.
*/
public class EaseClassLoader extends ClassLoader {
private final Map<Job, URLClassLoader> fRegisteredJars = new HashMap<>();
/** Marker that we are currently looking within a specific URLClassLoader. */
private final Collection<URLClassLoader> fTraversingURLClassLoader = new HashSet<>();
/**
* Constructor for the class loader.
*/
public EaseClassLoader() {
super(FrameworkUtil.getBundle(EaseClassLoader.class).adapt(BundleWiring.class).getClassLoader());
}
/**
* Constructor using a given parent classloader. When using this classloader the Eclipse-BuddyPolicy from the parent classloader bundle will be used.
*
* @param parent
* parent classloader
*/
public EaseClassLoader(ClassLoader parent) {
super(parent);
}
@Override
public Class<?> findClass(final String name) throws ClassNotFoundException {
// try to load from jars
final Job currentJob = Job.getJobManager().currentJob();
final URLClassLoader classLoader = fRegisteredJars.get(currentJob);
if (classLoader != null) {
// the EaseClassLoader will query its parent (this) if it cannot find the requested class, keep a marker to break the cycle
if (!fTraversingURLClassLoader.contains(classLoader)) {
fTraversingURLClassLoader.add(classLoader);
try {
final Class<?> clazz = classLoader.loadClass(name);
if (clazz != null)
return clazz;
} catch (final ClassNotFoundException e) {
// ignore, class not found in registered JARs
} finally {
// clear marker
fTraversingURLClassLoader.remove(classLoader);
}
}
}
// not found in jars, delegate to eclipse loader
return super.findClass(name);
}
/**
* Add a URL to the search path of the classloader. Currently detects classes only, not resources.
*
* @param engine
* script engine used
* @param url
* url to add to classpath
*/
public void registerURL(final Job engine, final URL url) {
// engine needs to be registered as we use a single classloader for multiple script engines.
if (!fRegisteredJars.containsKey(engine))
fRegisteredJars.put(engine, URLClassLoader.newInstance(new URL[] { url }, this));
else {
final List<URL> urlList = new ArrayList<>(Arrays.asList(fRegisteredJars.get(engine).getURLs()));
if (!urlList.contains(url)) {
urlList.add(url);
fRegisteredJars.put(engine, URLClassLoader.newInstance(urlList.toArray(new URL[0]), this));
}
}
}
public void unregisterEngine(final Job engine) {
final URLClassLoader classLoader = fRegisteredJars.remove(engine);
if (classLoader != null) {
try {
classLoader.close();
} catch (final IOException e) {
// gracefully ignore
}
}
}
}