blob: 6d19649031a6da4cd6849ee76d97a18c451e7e8a [file] [log] [blame]
/**
* <copyright>
*
* Copyright (c) 2014 itemis and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* itemis - Initial API and implementation
*
* </copyright>
*/
package org.eclipse.sphinx.jdt.loaders
import java.net.URL
import java.net.URLClassLoader
import java.util.List
import org.eclipse.core.runtime.Assert
import org.eclipse.jdt.core.IClasspathEntry
import org.eclipse.jdt.core.IJavaProject
import org.osgi.framework.Bundle
import static extension org.eclipse.sphinx.jdt.util.JavaExtensions.*
class ProjectClassLoader extends URLClassLoader {
private IJavaProject javaProject;
protected static def URL[] getJavaOutputURLs(IJavaProject javaProject) {
Assert.isNotNull(javaProject)
Assert.isLegal(javaProject.exists && javaProject.isOpen)
// Retrieve and return URL of absolute file system location behind default output location of given Java project
val outputURL = javaProject.outputLocation.location?.toFile?.toURI?.toURL
if(outputURL != null) #[outputURL] else #[]
}
protected static def ClassLoader hookupDependencyClassLoaders(IJavaProject javaProject, ClassLoader parent) {
Assert.isNotNull(javaProject)
Assert.isLegal(javaProject.exists && javaProject.isOpen)
var ClassLoader lastClassLoader = parent
// Create class loader for required other projects in the workspace and add it to parent class loader hierarchy
val entries = javaProject.getResolvedClasspath(true)
val Iterable<String> requiredProjectNames = entries.filter[entryKind == IClasspathEntry.CPE_PROJECT].map[
path.segment(0)]
for (requiredProjectName : requiredProjectNames) {
lastClassLoader = new ProjectClassLoader(requiredProjectName.javaProject, lastClassLoader)
}
// Create class loaders for required plug-ins and Java libraries and add them to parent class loader hierarchy
val libraryURLs = entries.filter[entryKind == IClasspathEntry.CPE_LIBRARY].map[file].filter[exists].map[
toURI.toURL]
val javaLibraryURLs = newArrayList()
val List<Bundle> requiredBundles = newArrayList();
for (URL libraryURL : libraryURLs) {
// See if current library a required plug-in or an ordinary Java library and keep track of them as such
val Bundle requiredBundle = libraryURL.bundle
if (requiredBundle != null) {
requiredBundles += requiredBundle
} else {
javaLibraryURLs += libraryURL
}
}
// Create and use delegating composite bundle class loader for required plug-ins
if (!requiredBundles.empty) {
lastClassLoader = new DelegatingCompositeBundleClassLoader(lastClassLoader, requiredBundles)
}
// Create and use URL class loader for Java libraries
if (!javaLibraryURLs.empty) {
lastClassLoader = new URLClassLoader(javaLibraryURLs, lastClassLoader)
}
lastClassLoader
}
new(IJavaProject javaProject) {
this(javaProject, Thread.currentThread().getContextClassLoader())
}
new(IJavaProject javaProject, ClassLoader parent) {
super(getJavaOutputURLs(javaProject), hookupDependencyClassLoaders(javaProject, parent))
this.javaProject = javaProject
}
override protected loadClass(String name, boolean resolve) throws ClassNotFoundException {
// Class to be loaded located in Java project behind this project class loader?
val type = javaProject.findType(name)
if (type != null && !type.binary && javaProject.path.isPrefixOf(type.path)) {
// Make sure that given class gets loaded by this project class loader, i.e. don't consult the
// parent class loader hierarchy before
/*
* !! Important Note !! This is necessary to ensure that classes from projects in the runtime workspace
* take precedence over the equally named classes in installed plug-ins or "dev mode" plug-ins.
*/
return findClass(name);
}
// Load all other classes normally
super.loadClass(name, resolve)
}
override toString() {
class.name + " [project=" + javaProject.project.name + "]";
}
public def IJavaProject getProject() {
javaProject
}
}