mirror of
https://github.com/sbt/sbt.git
synced 2026-09-04 16:54:29 +02:00
Multiple isolation levels support in generated boostraps
This commit is contained in:
@@ -11,6 +11,8 @@ import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.file.Files;
|
||||
import java.security.CodeSource;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.regex.Matcher;
|
||||
@@ -37,14 +39,41 @@ public class Bootstrap {
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
static String[] readJarUrls() throws IOException {
|
||||
final static String defaultURLResource = "bootstrap-jar-urls";
|
||||
final static String isolationIDsResource = "bootstrap-isolation-ids";
|
||||
|
||||
static String[] readStringSequence(String resource) throws IOException {
|
||||
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||
InputStream is = loader.getResourceAsStream("bootstrap-jar-urls");
|
||||
InputStream is = loader.getResourceAsStream(resource);
|
||||
if (is == null)
|
||||
return new String[] {};
|
||||
byte[] rawContent = readFullySync(is);
|
||||
String content = new String(rawContent, "UTF-8");
|
||||
if (content.length() == 0)
|
||||
return new String[] {};
|
||||
return content.split("\n");
|
||||
}
|
||||
|
||||
static Map<String, URL[]> readIsolationContexts(File jarDir, String[] isolationIDs) throws IOException {
|
||||
final Map<String, URL[]> perContextURLs = new LinkedHashMap<>();
|
||||
|
||||
for (String isolationID: isolationIDs) {
|
||||
String[] contextURLs = readStringSequence("bootstrap-isolation-" + isolationID + "-jar-urls");
|
||||
List<URL> urls = new ArrayList<>();
|
||||
for (String strURL : contextURLs) {
|
||||
URL url = new URL(strURL);
|
||||
File local = localFile(jarDir, url);
|
||||
if (local.exists())
|
||||
urls.add(local.toURI().toURL());
|
||||
else
|
||||
System.err.println("Warning: " + local + " not found.");
|
||||
}
|
||||
perContextURLs.put(isolationID, urls.toArray(new URL[urls.size()]));
|
||||
}
|
||||
|
||||
return perContextURLs;
|
||||
}
|
||||
|
||||
final static int concurrentDownloadCount = 6;
|
||||
|
||||
// http://stackoverflow.com/questions/872272/how-to-reference-another-property-in-java-util-properties/27724276#27724276
|
||||
@@ -86,6 +115,33 @@ public class Bootstrap {
|
||||
return resolved;
|
||||
}
|
||||
|
||||
static String mainJarPath() {
|
||||
ProtectionDomain protectionDomain = Bootstrap.class.getProtectionDomain();
|
||||
if (protectionDomain != null) {
|
||||
CodeSource source = protectionDomain.getCodeSource();
|
||||
if (source != null) {
|
||||
URL location = source.getLocation();
|
||||
if (location != null && location.getProtocol().equals("file")) {
|
||||
return location.getPath();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
static File localFile(File jarDir, URL url) {
|
||||
if (url.getProtocol().equals("file"))
|
||||
return new File(url.getPath());
|
||||
|
||||
String path = url.getPath();
|
||||
int idx = path.lastIndexOf('/');
|
||||
// FIXME Add other components in path to prevent conflicts?
|
||||
String fileName = path.substring(idx + 1);
|
||||
return new File(jarDir, fileName);
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
|
||||
ThreadFactory threadFactory = new ThreadFactory() {
|
||||
@@ -100,6 +156,12 @@ public class Bootstrap {
|
||||
|
||||
ExecutorService pool = Executors.newFixedThreadPool(concurrentDownloadCount, threadFactory);
|
||||
|
||||
System.setProperty("coursier.mainJar", mainJarPath());
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
System.setProperty("coursier.main.arg-" + i, args[i]);
|
||||
}
|
||||
|
||||
Map<String,String> properties = loadPropertiesMap(Thread.currentThread().getContextClassLoader().getResourceAsStream("bootstrap.properties"));
|
||||
for (Map.Entry<String, String> ent : properties.entrySet()) {
|
||||
System.setProperty(ent.getKey(), ent.getValue());
|
||||
@@ -118,7 +180,7 @@ public class Bootstrap {
|
||||
} else if (!jarDir.mkdirs())
|
||||
System.err.println("Warning: cannot create " + jarDir0 + ", continuing anyway.");
|
||||
|
||||
String[] jarStrUrls = readJarUrls();
|
||||
String[] jarStrUrls = readStringSequence(defaultURLResource);
|
||||
|
||||
List<String> errors = new ArrayList<>();
|
||||
List<URL> urls = new ArrayList<>();
|
||||
@@ -154,11 +216,7 @@ public class Bootstrap {
|
||||
completionService.submit(new Callable<URL>() {
|
||||
@Override
|
||||
public URL call() throws Exception {
|
||||
String path = url0.getPath();
|
||||
int idx = path.lastIndexOf('/');
|
||||
// FIXME Add other components in path to prevent conflicts?
|
||||
String fileName = path.substring(idx + 1);
|
||||
File dest = new File(jarDir, fileName);
|
||||
File dest = localFile(jarDir, url0);
|
||||
|
||||
if (!dest.exists()) {
|
||||
System.err.println("Downloading " + url0);
|
||||
@@ -198,9 +256,17 @@ public class Bootstrap {
|
||||
exit("Interrupted");
|
||||
}
|
||||
|
||||
final String[] isolationIDs = readStringSequence(isolationIDsResource);
|
||||
final Map<String, URL[]> perIsolationContextURLs = readIsolationContexts(jarDir, isolationIDs);
|
||||
|
||||
Thread thread = Thread.currentThread();
|
||||
ClassLoader parentClassLoader = thread.getContextClassLoader();
|
||||
|
||||
for (String isolationID: isolationIDs) {
|
||||
URL[] contextURLs = perIsolationContextURLs.get(isolationID);
|
||||
parentClassLoader = new IsolatedClassLoader(contextURLs, parentClassLoader, new String[]{ isolationID });
|
||||
}
|
||||
|
||||
URLClassLoader classLoader = new URLClassLoader(localURLs.toArray(new URL[localURLs.size()]), parentClassLoader);
|
||||
|
||||
Class<?> mainClass = null;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package coursier;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
|
||||
public class IsolatedClassLoader extends URLClassLoader {
|
||||
|
||||
private String[] isolationTargets;
|
||||
|
||||
public IsolatedClassLoader(
|
||||
URL[] urls,
|
||||
ClassLoader parent,
|
||||
String[] isolationTargets
|
||||
) {
|
||||
super(urls, parent);
|
||||
this.isolationTargets = isolationTargets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applications wanting to access an isolated `ClassLoader` should inspect the hierarchy of
|
||||
* loaders, and look into each of them for this method, by reflection. Then they should
|
||||
* call it (still by reflection), and look for an agreed in advance target in it. If it is found,
|
||||
* then the corresponding `ClassLoader` is the one with isolated dependencies.
|
||||
*/
|
||||
public String[] getIsolationTargets() {
|
||||
return isolationTargets;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user