Allowing multiple jars
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
package org.jetbrains.jet.preloading;/*
|
/*
|
||||||
* Copyright 2010-2013 JetBrains s.r.o.
|
* Copyright 2010-2013 JetBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -14,6 +14,8 @@ package org.jetbrains.jet.preloading;/*
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.jet.preloading;
|
||||||
|
|
||||||
import sun.misc.CompoundEnumeration;
|
import sun.misc.CompoundEnumeration;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
@@ -21,33 +23,32 @@ import java.net.MalformedURLException;
|
|||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
import java.net.URLStreamHandler;
|
import java.net.URLStreamHandler;
|
||||||
import java.util.Collections;
|
import java.util.*;
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
public class ClassPreloadingUtils {
|
public class ClassPreloadingUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a class loader that loads all classes from {@code jarFile} into memory to make loading faster (avoid skipping through zip archives).
|
* Creates a class loader that loads all classes from {@code jarFiles} into memory to make loading faster (avoid skipping through zip archives).
|
||||||
* @param jarFile a jar to load all classes from
|
*
|
||||||
* @param classCountEstimation an estimated number of classes in a the jar
|
* NOTE: if many resources with the same name exist, only the first one will be loaded
|
||||||
|
*
|
||||||
|
* @param jarFiles jars to load all classes from
|
||||||
|
* @param classCountEstimation an estimated number of classes in a the jars
|
||||||
* @param parent (nullable) parent class loader
|
* @param parent (nullable) parent class loader
|
||||||
* @return a class loader that reads classes from memory
|
* @return a class loader that reads classes from memory
|
||||||
* @throws IOException on from reading the jar
|
* @throws IOException on from reading the jar
|
||||||
*/
|
*/
|
||||||
public static ClassLoader preloadClasses(File jarFile, int classCountEstimation, ClassLoader parent) throws IOException {
|
public static ClassLoader preloadClasses(Collection<File> jarFiles, int classCountEstimation, ClassLoader parent) throws IOException {
|
||||||
Map<String, byte[]> entries = loadAllClassesFromJar(jarFile, classCountEstimation);
|
Map<String, ResourceData> entries = loadAllClassesFromJars(jarFiles, classCountEstimation);
|
||||||
|
|
||||||
return createMemoryBasedClassLoader(parent, jarFile, entries);
|
return createMemoryBasedClassLoader(parent, entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ClassLoader createMemoryBasedClassLoader(
|
private static ClassLoader createMemoryBasedClassLoader(
|
||||||
final ClassLoader parent,
|
final ClassLoader parent,
|
||||||
final File jarFile,
|
final Map<String, ResourceData> preloadedResources
|
||||||
final Map<String, byte[]> preloadedResources
|
|
||||||
) {
|
) {
|
||||||
return new ClassLoader(null) {
|
return new ClassLoader(null) {
|
||||||
@Override
|
@Override
|
||||||
@@ -63,38 +64,17 @@ public class ClassPreloadingUtils {
|
|||||||
@Override
|
@Override
|
||||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||||
String internalName = name.replace('.', '/').concat(".class");
|
String internalName = name.replace('.', '/').concat(".class");
|
||||||
byte[] bytes = preloadedResources.get(internalName);
|
ResourceData resourceData = preloadedResources.get(internalName);
|
||||||
if (bytes == null) return null;
|
if (resourceData == null) return null;
|
||||||
|
|
||||||
return defineClass(name, bytes, 0, bytes.length);
|
return defineClass(name, resourceData.bytes, 0, resourceData.bytes.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected URL findResource(String name) {
|
protected URL findResource(String name) {
|
||||||
final byte[] bytes = preloadedResources.get(name);
|
ResourceData resourceData = preloadedResources.get(name);
|
||||||
if (bytes == null) return null;
|
if (resourceData == null) return null;
|
||||||
|
return resourceData.getURL();
|
||||||
try {
|
|
||||||
String path = "file:" + jarFile + "!" + name;
|
|
||||||
return new URL("jar", null, 0, path, new URLStreamHandler() {
|
|
||||||
@Override
|
|
||||||
protected URLConnection openConnection(URL u) throws IOException {
|
|
||||||
return new URLConnection(u) {
|
|
||||||
@Override
|
|
||||||
public void connect() throws IOException {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InputStream getInputStream() throws IOException {
|
|
||||||
return new ByteArrayInputStream(bytes);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (MalformedURLException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -103,41 +83,46 @@ public class ClassPreloadingUtils {
|
|||||||
if (resource == null) {
|
if (resource == null) {
|
||||||
return new CompoundEnumeration<URL>(new Enumeration[0]);
|
return new CompoundEnumeration<URL>(new Enumeration[0]);
|
||||||
}
|
}
|
||||||
|
// Only the first resource is loaded
|
||||||
return Collections.enumeration(Collections.singletonList(resource));
|
return Collections.enumeration(Collections.singletonList(resource));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Map<String, byte[]> loadAllClassesFromJar(File jarFile, int classNumberEstimate) throws IOException {
|
private static Map<String, ResourceData> loadAllClassesFromJars(Collection<File> jarFiles, int classNumberEstimate) throws IOException {
|
||||||
Map<String, byte[]> classes = new HashMap<String, byte[]>(classNumberEstimate);
|
Map<String, ResourceData> resources = new HashMap<String, ResourceData>(classNumberEstimate);
|
||||||
|
|
||||||
FileInputStream fileInputStream = new FileInputStream(jarFile);
|
for (File jarFile : jarFiles) {
|
||||||
try {
|
FileInputStream fileInputStream = new FileInputStream(jarFile);
|
||||||
byte[] buffer = new byte[10 * 1024];
|
|
||||||
ZipInputStream stream = new ZipInputStream(new BufferedInputStream(fileInputStream));
|
|
||||||
while (true) {
|
|
||||||
ZipEntry entry = stream.getNextEntry();
|
|
||||||
if (entry == null) break;
|
|
||||||
|
|
||||||
ByteArrayOutputStreamWithPublicArray bytes = new ByteArrayOutputStreamWithPublicArray((int) entry.getSize());
|
|
||||||
int count;
|
|
||||||
while ((count = stream.read(buffer)) > 0) {
|
|
||||||
bytes.write(buffer, 0, count);
|
|
||||||
}
|
|
||||||
if (!entry.isDirectory()) {
|
|
||||||
classes.put(entry.getName(), bytes.getBytes());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
try {
|
try {
|
||||||
fileInputStream.close();
|
byte[] buffer = new byte[10 * 1024];
|
||||||
|
ZipInputStream stream = new ZipInputStream(new BufferedInputStream(fileInputStream));
|
||||||
|
while (true) {
|
||||||
|
ZipEntry entry = stream.getNextEntry();
|
||||||
|
if (entry == null) break;
|
||||||
|
if (entry.isDirectory()) continue;
|
||||||
|
String name = entry.getName();
|
||||||
|
if (resources.containsKey(name)) continue; // Only the first resource is stored
|
||||||
|
|
||||||
|
ByteArrayOutputStreamWithPublicArray bytes = new ByteArrayOutputStreamWithPublicArray((int) entry.getSize());
|
||||||
|
int count;
|
||||||
|
while ((count = stream.read(buffer)) > 0) {
|
||||||
|
bytes.write(buffer, 0, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
resources.put(name, new ResourceData(jarFile, name, bytes.getBytes()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
finally {
|
||||||
// Ignore
|
try {
|
||||||
|
fileInputStream.close();
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return classes;
|
return resources;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ByteArrayOutputStreamWithPublicArray extends ByteArrayOutputStream {
|
private static class ByteArrayOutputStreamWithPublicArray extends ByteArrayOutputStream {
|
||||||
@@ -150,4 +135,41 @@ public class ClassPreloadingUtils {
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class ResourceData {
|
||||||
|
private final File jarFile;
|
||||||
|
private final String resourceName;
|
||||||
|
private final byte[] bytes;
|
||||||
|
|
||||||
|
public ResourceData(File jarFile, String resourceName, byte[] bytes) {
|
||||||
|
this.jarFile = jarFile;
|
||||||
|
this.resourceName = resourceName;
|
||||||
|
this.bytes = bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getURL() {
|
||||||
|
try {
|
||||||
|
String path = "file:" + jarFile + "!" + resourceName;
|
||||||
|
return new URL("jar", null, 0, path, new URLStreamHandler() {
|
||||||
|
@Override
|
||||||
|
protected URLConnection openConnection(URL u) throws IOException {
|
||||||
|
return new URLConnection(u) {
|
||||||
|
@Override
|
||||||
|
public void connect() throws IOException {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream() throws IOException {
|
||||||
|
return new ByteArrayInputStream(bytes);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (MalformedURLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ package org.jetbrains.jet.preloading;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class Preloader {
|
public class Preloader {
|
||||||
|
|
||||||
@@ -13,10 +15,16 @@ public class Preloader {
|
|||||||
printUsageAndExit();
|
printUsageAndExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
File file = new File(args[0]);
|
String classpath = args[0];
|
||||||
if (!file.exists()) {
|
String[] paths = classpath.split("\\" + File.pathSeparator);
|
||||||
System.out.println("File does not exist: " + file);
|
List<File> files = new ArrayList<File>(paths.length);
|
||||||
printUsageAndExit();
|
for (String path : paths) {
|
||||||
|
File file = new File(path);
|
||||||
|
if (!file.exists()) {
|
||||||
|
System.out.println("File does not exist: " + file);
|
||||||
|
printUsageAndExit();
|
||||||
|
}
|
||||||
|
files.add(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
String mainClassCanonicalName = args[1];
|
String mainClassCanonicalName = args[1];
|
||||||
@@ -36,7 +44,7 @@ public class Preloader {
|
|||||||
|
|
||||||
ClassLoader parent = Preloader.class.getClassLoader();
|
ClassLoader parent = Preloader.class.getClassLoader();
|
||||||
|
|
||||||
ClassLoader preloaded = ClassPreloadingUtils.preloadClasses(file, classNumber, parent);
|
ClassLoader preloaded = ClassPreloadingUtils.preloadClasses(files, classNumber, parent);
|
||||||
|
|
||||||
Class<?> mainClass = preloaded.loadClass(mainClassCanonicalName);
|
Class<?> mainClass = preloaded.loadClass(mainClassCanonicalName);
|
||||||
Method mainMethod = mainClass.getMethod("main", String[].class);
|
Method mainMethod = mainClass.getMethod("main", String[].class);
|
||||||
@@ -58,7 +66,7 @@ public class Preloader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void printUsageAndExit() {
|
private static void printUsageAndExit() {
|
||||||
System.out.println("Usage: Preloader <path to jar> <main class> <class number estimate> <parameters to pass to the main class> <time|notime>");
|
System.out.println("Usage: Preloader <paths to jars> <main class> <class number estimate> <parameters to pass to the main class> <time|notime>");
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user