All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.embulk.deps.SelfContainedJarAwareURLClassLoader Maven / Gradle / Ivy

package org.embulk.deps;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.CodeSource;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Optional;
import java.util.Vector;
import java.util.jar.Attributes;
import java.util.jar.Manifest;

/**
 * Loads classes and resources from self-contained JAR file resources, and a search path with {@link java.net.URLClassLoader}.
 *
 * 

JARs files inside the Embulk JAR file are accessed only when the {@link java.net.URLClassLoader} implementation does * not find the requested resource. In other words, the delegation parent {@code ClassLoader} and a search path processed by * {@link java.net.URLClassLoader} are always prioritized over self-contained JAR files. * * @see Simplify your application delivery with One-JAR */ @SuppressWarnings("checkstyle:AbbreviationAsWordInName") public class SelfContainedJarAwareURLClassLoader extends URLClassLoader { public SelfContainedJarAwareURLClassLoader(final URL[] urls, final ClassLoader parent, final String selfContainedJarCategory) { // The delegation parent ClassLoader is processed by the super class URLClassLoader. super(urls, parent); this.accessControlContext = AccessController.getContext(); this.selfContainedJarCategory = selfContainedJarCategory; } /** * Finds the class with the specified binary name. * *

It should not be called when the class has already been loaded. The default {@code loadClass} checks * if the class has already been loaded before calling {@code findClass}. */ @Override protected Class findClass(final String className) throws ClassNotFoundException { try { // super.findClass(className) finds both from the delegation parent ClassLoader and non-self-contained JARs. return super.findClass(className); } catch (final ClassNotFoundException ignored) { // Pass through intentionally. Try finding from self-contained JARs. } // Try finding from self-contained JARs only when not found from the parent ClassLoader nor filesystem JARs. try { return AccessController.doPrivileged( new PrivilegedExceptionAction>() { @Override public Class run() throws ClassNotFoundException { try { return defineClassFromEmbulkSelfContainedJarFiles(className); } catch (final ClassNotFoundException | LinkageError | ClassCastException ex) { throw ex; } catch (final Throwable ex) { // Found a resource in the container JAR, but failed to load it as a class. throw new ClassNotFoundException(className, ex); } } }, this.accessControlContext); } catch (final PrivilegedActionException ex) { final Throwable internalException = ex.getException(); if (internalException instanceof ClassNotFoundException) { throw (ClassNotFoundException) internalException; } if (internalException instanceof LinkageError) { throw (LinkageError) internalException; } if (internalException instanceof ClassCastException) { throw (ClassCastException) internalException; } throw new ClassNotFoundException(className, ex); } } // "protected String findLibrary(String name)" is not overridden because Embulk does not assume native libraries. // TODO: (maybe) Override "loadClass" so that it can deal with the context class loader. // Refer to {@link com.simontuffs.onejar.JarClassLoader#loadClass}. // TODO: (maybe) Override "getResource" so that it can delegate to external class loader explicitly. // Refer to {@link com.simontuffs.onejar.JarClassLoader#getResource}. /** * Finds a resource recognized as the given name. * *

Resources found by the delegation parent {@link java.net.URLClassLoader} are always prioritized. Resources in * self-contained JAR file resources are looked into only when not found by the delegation parent {@code URLClassLoader}. * *

Note that {@link java.net.URLClassLoader#findResource} is public while {@link java.lang.ClassLoader#findResource} * is protected. * * @param resourceName name of target resource * @return URL of the resource */ @Override public URL findResource(final String resourceName) { // super.findResource(resourceName) finds both from the delegation parent ClassLoader and non-self-contained JARs. final URL resourceUrlFromSuper = super.findResource(resourceName); if (resourceUrlFromSuper != null) { return resourceUrlFromSuper; } if (this.selfContainedJarCategory != null) { // TODO: Consider duplicated resources. final Resource resource = EmbulkSelfContainedJarFiles.getSingleResource(resourceName, this.selfContainedJarCategory); if (resource == null) { return null; } try { return AccessController.doPrivileged(new PrivilegedExceptionAction() { @Override public URL run() throws MalformedURLException { return resource.buildJarEmbeddedUrl(); } }, this.accessControlContext); } catch (final PrivilegedActionException ignored) { // Pass through intentionally. } } return null; } /** * Finds resources recognized as the given name. * *

Note that {@link java.net.URLClassLoader#findResources} is public while {@link java.lang.ClassLoader#findResources} * is protected. */ @Override public Enumeration findResources(final String resourceName) throws IOException { // super.findResources(resourceName) finds both from the delegation parent ClassLoader and non-self-contained JARs. final Enumeration resourceUrlsFromSuper = super.findResources(resourceName); final Vector resourceUrls = new Vector(); while (resourceUrlsFromSuper.hasMoreElements()) { resourceUrls.add(resourceUrlsFromSuper.nextElement()); } if (this.selfContainedJarCategory != null) { // Even if some resources are found from the delegation parent class loader, it looks into self-contained JAR files. final Collection resources = EmbulkSelfContainedJarFiles.getMultipleResources(resourceName, this.selfContainedJarCategory); final Collection resourceUrlsFromSelfContainedJarFiles; try { resourceUrlsFromSelfContainedJarFiles = AccessController.doPrivileged(new PrivilegedExceptionAction>() { @Override public Collection run() throws IOException { final ArrayList urls = new ArrayList<>(); for (final Resource resource : resources) { urls.add(resource.buildJarEmbeddedUrl()); } return urls; } }, accessControlContext); } catch (final PrivilegedActionException ignored) { // Passing through intentionally. return resourceUrls.elements(); } resourceUrls.addAll(resourceUrlsFromSelfContainedJarFiles); } return resourceUrls.elements(); } private Class defineClassFromEmbulkSelfContainedJarFiles(final String className) throws ClassNotFoundException { if (this.selfContainedJarCategory == null) { throw new ClassNotFoundException(className); } final String resourceName = className.replace('.', '/').concat(".class"); // Class must be singular. final Resource resource = EmbulkSelfContainedJarFiles.getSingleResource(resourceName, this.selfContainedJarCategory); if (resource == null) { throw new ClassNotFoundException(className); } final URL codeSourceUrl = resource.getCodeSourceUrl(); final int indexLastPeriod = className.lastIndexOf('.'); if (indexLastPeriod != -1) { // If |className| has a package part. final String packageName = className.substring(0, indexLastPeriod); final Manifest manifest = resource.getManifest(); // Class must be singular. if (!this.checkPackageSealing(packageName, manifest, codeSourceUrl)) { try { if (manifest != null) { this.definePackageFromManifest(packageName, manifest, codeSourceUrl); } else { this.definePackage(packageName, null, null, null, null, null, null, null); } } catch (final IllegalArgumentException ex) { if (!this.checkPackageSealing(packageName, manifest, codeSourceUrl)) { throw new ClassNotFoundException( "FATAL: Unexpected double failures to define package: " + packageName, ex); } } } } final CodeSource codeSource = new CodeSource(codeSourceUrl, resource.getCodeSigners()); return this.defineClass(className, resource.getAdjustedByteBuffer(), codeSource); } private boolean checkPackageSealing(final String packageName, final Manifest manifest, final URL url) { final Package packageInstance = this.getPackage(packageName); if (packageInstance == null) { return false; } if (packageInstance.isSealed()) { if (!packageInstance.isSealed(url)) { throw new SecurityException(String.format( "Package \"%s\" is already loaded, and sealed with a different code source URL.", packageName)); } } else { if ((manifest != null) && isManifestToSeal(packageName, manifest)) { throw new SecurityException(String.format( "Package \"%s\" is already loaded, and unsealed.", packageName)); } } return true; } private static boolean isManifestToSeal(final String packageName, final Manifest manifest) { final Optional perEntryAttributes = Optional.ofNullable(manifest.getAttributes(packageName.replace('.', '/').concat("/"))); final Attributes mainAttributes = manifest.getMainAttributes(); return "true".equalsIgnoreCase( getEffectiveAttribute(mainAttributes, perEntryAttributes, Attributes.Name.SEALED)); } private Package definePackageFromManifest(final String packageName, final Manifest manifest, final URL codeSourceUrl) throws IllegalArgumentException { // https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Per-Entry_Attributes final Optional perEntryAttributes = Optional.ofNullable(manifest.getAttributes(packageName.replace('.', '/').concat("/"))); final Attributes mainAttributes = manifest.getMainAttributes(); return this.definePackage( packageName, getEffectiveAttribute(mainAttributes, perEntryAttributes, Attributes.Name.SPECIFICATION_TITLE), getEffectiveAttribute(mainAttributes, perEntryAttributes, Attributes.Name.SPECIFICATION_VERSION), getEffectiveAttribute(mainAttributes, perEntryAttributes, Attributes.Name.SPECIFICATION_VENDOR), getEffectiveAttribute(mainAttributes, perEntryAttributes, Attributes.Name.IMPLEMENTATION_TITLE), getEffectiveAttribute(mainAttributes, perEntryAttributes, Attributes.Name.IMPLEMENTATION_VERSION), getEffectiveAttribute(mainAttributes, perEntryAttributes, Attributes.Name.IMPLEMENTATION_VENDOR), "true".equalsIgnoreCase( getEffectiveAttribute(mainAttributes, perEntryAttributes, Attributes.Name.SEALED)) ? codeSourceUrl : null); } private static String getEffectiveAttribute( final Attributes mainAttributes, final Optional perEntryAttributes, final Attributes.Name attributeName) { final String mainAttribute = mainAttributes.getValue(attributeName); return (String) perEntryAttributes.orElse(mainAttributes).getOrDefault(attributeName, mainAttribute); } private final AccessControlContext accessControlContext; private final String selfContainedJarCategory; }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy