package jj; import java.io.IOException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.security.AllPermission; import java.security.CodeSource; import java.security.Permissions; import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.jar.Attributes; import java.util.jar.Manifest; import java.util.jar.Attributes.Name; /** * Responsible for loading the kernel and its direct dependencies * The kernel will make its own set of classloaders to decide what * means what to applications. * * Most of this should actually go into a different classloader inside the kernel * project. this should only really know how to find the startup class inside there * * @author jason * */ public final class BootstrapClassLoader extends ClassLoader { private static final String CLASS_FILE_FORMAT = "/%s.class"; private static final String RESOURCE_FORMAT = "/%s"; private static final Permissions ALL_PERMISSIONS = new Permissions(); static { registerAsParallelCapable(); ALL_PERMISSIONS.add(new AllPermission()); ALL_PERMISSIONS.setReadOnly(); } private final Jars systemJars; BootstrapClassLoader(Jars systemJars) throws IOException { // whatever loaded this class is the root of all classloaders in the system super(BootstrapClassLoader.class.getClassLoader()); // the kernel uses assertions all over the place to ensure the state is // what we expect it to be setDefaultAssertionStatus(true); this.systemJars = systemJars; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { try { String classFile = String.format(CLASS_FILE_FORMAT, name.replace('.', '/')); Path attempt = systemJars.pathForFile(classFile); if (attempt != null) { byte[] classBytes = Files.readAllBytes(attempt); definePackageIfNeeded(name, classFile); CodeSource codeSource = systemJars.codeSourceForFile(classFile); ProtectionDomain protectionDomain = new ProtectionDomain(codeSource, ALL_PERMISSIONS); return defineClass(name, classBytes, 0, classBytes.length, protectionDomain); } } catch (Exception e) { System.err.printf("Something went wrong reading a class [%s]\n", name); e.printStackTrace(); throw new ClassNotFoundException(name, e); } throw new ClassNotFoundException(name); } private void definePackageIfNeeded(String name, String classFile) throws Exception { String packageName = name.substring(0, name.lastIndexOf('.')); if (getPackage(packageName) == null) { Manifest manifest = systemJars.jarManifestForFile(classFile); doDefinePackage(packageName, manifest); } } private Package doDefinePackage(String packageName, Manifest manifest) { String specTitle = null, specVersion = null, specVendor = null, implTitle = null, implVersion = null, implVendor = null; if (manifest != null) { Attributes attributes = manifest.getAttributes(packageName.replace('.', '/').concat("/")); if (attributes != null) { specTitle = attributes.getValue(Name.SPECIFICATION_TITLE); specVersion = attributes.getValue(Name.SPECIFICATION_VERSION); specVendor = attributes.getValue(Name.SPECIFICATION_VENDOR); implTitle = attributes.getValue(Name.IMPLEMENTATION_TITLE); implVersion = attributes.getValue(Name.IMPLEMENTATION_VERSION); implVendor = attributes.getValue(Name.IMPLEMENTATION_VENDOR); } attributes = manifest.getMainAttributes(); if (attributes != null) { specTitle = specTitle == null ? attributes.getValue(Name.SPECIFICATION_TITLE) : specTitle; specVersion = specVersion == null ? attributes.getValue(Name.SPECIFICATION_VERSION) : specVersion; specVendor = specVendor == null ? attributes.getValue(Name.SPECIFICATION_VENDOR) : specVendor; implTitle = implTitle == null ? attributes.getValue(Name.IMPLEMENTATION_TITLE) : implTitle; implVersion = implVersion == null ? attributes.getValue(Name.IMPLEMENTATION_VERSION) : implVersion; implVendor = implVendor == null ? attributes.getValue(Name.IMPLEMENTATION_VENDOR) : implVendor; } } return definePackage(packageName, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, null); } @Override protected URL findResource(String name) { URL result = null; try { String resourceFile = String.format(RESOURCE_FORMAT, name); Path attempt = systemJars.pathForFile(resourceFile); if (attempt != null) { result = attempt.toUri().toURL(); } } catch (Exception e) { System.err.printf("Something went wrong reading a resource [%s]\n", name); e.printStackTrace(); } return result; } @Override protected Enumeration<URL> findResources(String name) throws IOException { List<URL> result = new ArrayList<>(); try { String resourceFile = String.format(RESOURCE_FORMAT, name); for (Path path : systemJars.pathsForFile(resourceFile)) { result.add(path.toUri().toURL()); } } catch (Exception e) { System.err.printf("Something went wrong looking for resources by name [%s]\n", name); e.printStackTrace(); } return Collections.enumeration(result); } }