com.redhat.ceylon.launcher.CeylonClassLoader Maven / Gradle / Ivy
package com.redhat.ceylon.launcher;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.jar.JarFile;
import com.redhat.ceylon.common.Versions;
/**
* Ceylon-specific class loader that knows how to find and add
* all needed dependencies for compiler and runtime.
* Implements child-first class loading to prevent mix-ups with
* Java's own tool-chain.
*
* @author Tako Schotanus
*
*/
public class CeylonClassLoader extends URLClassLoader {
public static CeylonClassLoader newInstance() throws URISyntaxException, MalformedURLException, FileNotFoundException {
return new CeylonClassLoader(getClassPath());
}
public static CeylonClassLoader newInstance(List classPath) throws URISyntaxException, MalformedURLException, FileNotFoundException {
return new CeylonClassLoader(classPath);
}
private String signature;
private CeylonClassLoader(List classPath) throws URISyntaxException, MalformedURLException, FileNotFoundException {
super(toUrls(classPath));
this.signature = toString(classPath);
}
private CeylonClassLoader(List classPath, ClassLoader parentLoader) throws URISyntaxException, MalformedURLException, FileNotFoundException {
super(toUrls(classPath), parentLoader);
this.signature = toString(classPath);
}
public String getSignature(){
return signature;
}
public boolean hasSignature(String signature){
return signature != null && this.signature.equals(signature);
}
private static URL[] toUrls(List cp) throws MalformedURLException {
URL[] urls = new URL[cp.size()];
int i = 0;
for (File f : cp) {
urls[i++] = f.toURI().toURL();
}
return urls;
}
private static String toString(List cp) {
StringBuilder classPath = new StringBuilder();
for (File f : cp) {
if (classPath.length() > 0) {
classPath.append(File.pathSeparatorChar);
}
classPath.append(f.getAbsolutePath());
}
return classPath.toString();
}
public static String getClassPathAsString() throws URISyntaxException, FileNotFoundException {
return toString(getClassPath());
}
public static String getClassPathSignature(List cp) {
return toString(cp);
}
public static List getClassPath() throws URISyntaxException, FileNotFoundException {
// Determine the necessary folders
File ceylonHome = LauncherUtil.determineHome();
File ceylonRepo = LauncherUtil.determineRepo(ceylonHome);
boolean includeSlf4j = LauncherUtil.isIncludeSlf4j();
// Perform some sanity checks
checkFolders(ceylonHome, ceylonRepo);
List archives = new LinkedList();
// List all the necessary Ceylon JARs and CARs
String version = LauncherUtil.determineSystemVersion();
archives.add(getRepoJar(ceylonRepo, "com.redhat.ceylon.compiler.java", version));
archives.add(getRepoCar(ceylonRepo, "ceylon.language", version));
archives.add(getRepoJar(ceylonRepo, "ceylon.runtime", version));
archives.add(getRepoJar(ceylonRepo, "com.redhat.ceylon.compiler.js", version));
archives.add(getRepoJar(ceylonRepo, "com.redhat.ceylon.typechecker", version));
archives.add(getRepoJar(ceylonRepo, "com.redhat.ceylon.common", version));
archives.add(getRepoJar(ceylonRepo, "com.redhat.ceylon.cli", version));
archives.add(getRepoJar(ceylonRepo, "com.redhat.ceylon.model", version));
archives.add(getRepoJar(ceylonRepo, "com.redhat.ceylon.module-loader", version));
archives.add(getRepoJar(ceylonRepo, "com.redhat.ceylon.module-resolver", version));
archives.add(getRepoJar(ceylonRepo, "com.redhat.ceylon.module-resolver-aether", version)); // optional
// sardine depends on slf4j
if(includeSlf4j)
archives.add(getRepoJar(ceylonRepo, "com.redhat.ceylon.module-resolver-webdav", version)); // optional
archives.add(getRepoJar(ceylonRepo, "com.redhat.ceylon.module-resolver-javascript", version)); // optional
archives.add(getRepoJar(ceylonRepo, "com.redhat.ceylon.langtools.classfile", version));
archives.add(getRepoJar(ceylonRepo, "com.redhat.ceylon.tool.provider", version));
archives.add(getRepoJar(ceylonRepo, "com.redhat.ceylon.tools", version));
archives.add(getRepoJar(ceylonRepo, "org.jboss.modules", Versions.DEPENDENCY_JBOSS_MODULES_VERSION));
archives.add(getRepoJar(ceylonRepo, "org.jboss.logmanager", Versions.DEPENDENCY_LOGMANAGER_VERSION));
// Maven support for CMR
archives.add(getRepoJar(ceylonRepo, "org.eclipse.aether.api", "1.1.0")); // optional
archives.add(getRepoJar(ceylonRepo, "org.eclipse.aether.spi", "1.1.0")); // optional
archives.add(getRepoJar(ceylonRepo, "org.eclipse.aether.util", "1.1.0")); // optional
archives.add(getRepoJar(ceylonRepo, "org.eclipse.aether.impl", "1.1.0")); // optional
archives.add(getRepoJar(ceylonRepo, "org.eclipse.aether.connector.basic", "1.1.0")); // optional
archives.add(getRepoJar(ceylonRepo, "org.eclipse.aether.transport.file", "1.1.0")); // optional
archives.add(getRepoJar(ceylonRepo, "org.eclipse.aether.transport.http", "1.1.0")); // optional
archives.add(getRepoJar(ceylonRepo, "com.google.guava", "18.0")); // optional
archives.add(getRepoJar(ceylonRepo, "org.apache.commons.lang3", "3.4")); // optional
archives.add(getRepoJar(ceylonRepo, "org.apache.maven.maven-artifact", "3.3.9")); // optional
archives.add(getRepoJar(ceylonRepo, "org.apache.maven.maven-model", "3.3.9")); // optional
archives.add(getRepoJar(ceylonRepo, "org.apache.maven.maven-model-builder", "3.3.9")); // optional
archives.add(getRepoJar(ceylonRepo, "org.apache.maven.maven-repository-metadata", "3.3.9")); // optional
archives.add(getRepoJar(ceylonRepo, "org.apache.maven.maven-builder-support", "3.3.9")); // optional
archives.add(getRepoJar(ceylonRepo, "org.apache.maven.maven-settings", "3.3.9")); // optional
archives.add(getRepoJar(ceylonRepo, "org.apache.maven.maven-settings-builder", "3.3.9")); // optional
archives.add(getRepoJar(ceylonRepo, "org.apache.maven.maven-aether-provider", "3.3.9")); // optional
archives.add(getRepoJar(ceylonRepo, "org.codehaus.plexus.plexus-interpolation", "1.22")); // optional
archives.add(getRepoJar(ceylonRepo, "org.codehaus.plexus.plexus-utils", "3.0.22")); // optional
// For the typechecker
archives.add(getRepoJar(ceylonRepo, "org.antlr.runtime", "3.4"));
// For the JS backend
archives.add(getRepoJar(ceylonRepo, "net.minidev.json-smart", "1.1.1"));
// For the "doc" tool
archives.add(getRepoJar(ceylonRepo, "org.tautua.markdownpapers.core", "1.2.7"));
archives.add(getRepoJar(ceylonRepo, "com.github.rjeschke.txtmark", "0.13"));
// For the --out http:// functionality of the compiler (sardine)
if(includeSlf4j){
archives.add(getRepoJar(ceylonRepo, "com.github.lookfirst.sardine", "5.1")); // optional
archives.add(getRepoJar(ceylonRepo, "org.slf4j.api", "1.6.1")); // optional
archives.add(getRepoJar(ceylonRepo, "org.slf4j.simple", "1.6.1")); // optional
}
// For aether and webdav/sardine
archives.add(getRepoJar(ceylonRepo, "org.apache.httpcomponents.httpclient", "4.3.2")); // optional
archives.add(getRepoJar(ceylonRepo, "org.apache.httpcomponents.httpcore", "4.3.2")); // optional
archives.add(getRepoJar(ceylonRepo, "org.apache.commons.logging", "1.1.1")); // optional
archives.add(getRepoJar(ceylonRepo, "org.apache.commons.codec", "1.8")); // optional
return archives;
}
private static File getRepoJar(File repo, String moduleName, String version) {
return getRepoUrl(repo, moduleName, version, "jar");
}
private static File getRepoCar(File repo, String moduleName, String version) {
return getRepoUrl(repo, moduleName, version, "car");
}
private static File getRepoUrl(File repo, String moduleName, String version, String extension) {
return new File(repo, moduleName.replace('.', '/') + "/" + version + "/" + moduleName + "-" + version + "." + extension);
}
public static File getRepoJar(String moduleName, String version) throws FileNotFoundException, URISyntaxException {
return getRepoUrl(moduleName, version, "jar");
}
public static File getRepoCar(String moduleName, String version) throws FileNotFoundException, URISyntaxException {
return getRepoUrl(moduleName, version, "car");
}
public static File getRepoUrl(String moduleName, String version, String extension) throws URISyntaxException, FileNotFoundException {
// Determine the necessary folders
File ceylonHome = LauncherUtil.determineHome();
File ceylonRepo = LauncherUtil.determineRepo(ceylonHome);
// Perform some sanity checks
checkFolders(ceylonHome, ceylonRepo);
return new File(ceylonRepo, moduleName.replace('.', '/') + "/" + version + "/" + moduleName + "-" + version + "." + extension);
}
private static void checkFolders(File ceylonHome, File ceylonRepo) throws FileNotFoundException {
if (!ceylonHome.isDirectory()) {
throw new FileNotFoundException("Could not determine the Ceylon home directory (" + ceylonHome + ")");
}
if (!ceylonRepo.isDirectory()) {
throw new FileNotFoundException("The Ceylon system repository could not be found (" + ceylonRepo + ")");
}
}
@Override
protected synchronized Class> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
// First, check if the class has already been loaded
Class> c = findLoadedClass(name);
if (c == null) {
try {
// checking local
c = findClass(name);
} catch (ClassNotFoundException e) {
// checking parent
// This call to loadClass may eventually call findClass again, in case the parent doesn't find anything.
c = super.loadClass(name, resolve);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
@Override
public URL getResource(String name) {
URL url = findResource(name);
if (url == null) {
// This call to getResource may eventually call findResource again, in case the parent doesn't find anything.
url = super.getResource(name);
}
return url;
}
@Override
public Enumeration getResources(String name) throws IOException {
/**
* Similar to super, but local resources are enumerated before parent resources
*/
Enumeration localUrls = findResources(name);
Enumeration parentUrls = null;
if (getParent() != null) {
parentUrls = getParent().getResources(name);
}
final List urls = new ArrayList();
if (localUrls != null) {
while (localUrls.hasMoreElements()) {
urls.add(localUrls.nextElement());
}
}
if (parentUrls != null) {
while (parentUrls.hasMoreElements()) {
urls.add(parentUrls.nextElement());
}
}
return Collections.enumeration(urls);
}
@Override
public InputStream getResourceAsStream(String name) {
URL url = getResource(name);
if (url != null) {
try {
URLConnection con = url.openConnection();
con.setUseCaches(false);
return con.getInputStream();
} catch (IOException e) {
}
}
return null;
}
/**
* Cleans up any resource associated with this class loader. This class loader will not be usable after calling this
* method, so any code using it to run better not be running anymore.
* DO NOT call this method "clearCache" as Spring Boot has reserved that name:
* https://github.com/ceylon/ceylon/issues/6681
*/
public void clearCacheButNotWithThisNameToKeepSpringBootHappy() {
try {
Class> klass = java.net.URLClassLoader.class;
Field ucp = klass.getDeclaredField("ucp");
ucp.setAccessible(true);
Object sunMiscURLClassPath = ucp.get(this);
Field loaders = sunMiscURLClassPath.getClass().getDeclaredField("loaders");
loaders.setAccessible(true);
Object collection = loaders.get(sunMiscURLClassPath);
for (Object sunMiscURLClassPathJarLoader : ((Collection>) collection).toArray()) {
try {
Field loader = sunMiscURLClassPathJarLoader.getClass().getDeclaredField("jar");
loader.setAccessible(true);
Object jarFile = loader.get(sunMiscURLClassPathJarLoader);
((JarFile) jarFile).close();
} catch (Throwable t) {
// not a JAR loader?
t.printStackTrace();
}
}
} catch (Throwable t) {
// Something's wrong
t.printStackTrace();
}
return;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy