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

prompto.utils.JarLoader Maven / Gradle / Ivy

The newest version!
package prompto.utils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.jar.JarFile;

/**
 * Adds JAR files to the class path dynamically. Uses an officially supported
 * API where possible. To ensure use of the official method and compatibility
 * with Java 9+, your app must be started with
 * {@code -javaagent:path/to/jar-loader.jar}.
 *
 * @author Chris Jennings <https://cgjennings.ca/contact.html>
 */

public class JarLoader {


    private static boolean loadedViaPreMain = false;
    private static Instrumentation instrumentation;
    private static ClassLoader addUrlThis;
    private static Method addUrlMethod;
   
    public static void addURLs(Collection urls) throws Exception {
		for(URL url : urls)
			addURL(url);
	}

	public static void addURL(URL url) throws Exception {
		if("file".equals(url.getProtocol())) {
			Path path = Paths.get(url.toURI());
			addToClassPath(path.toFile());
		} else
			throw new UnsupportedOperationException("Unsupported protocol: " + url.getProtocol());
	}

	/**
     * Adds a JAR file to the list of JAR files searched by the system class
     * loader. This effectively adds a new JAR to the class path.
     *
     * @param jarFile the JAR file to add
     * @throws IOException if there is an error accessing the JAR file
     */
    public static synchronized void addToClassPath(File jarFile) throws IOException {
        if (jarFile == null) {
            throw new NullPointerException();
        }
        // do our best to ensure consistent behaviour across methods
        if (!jarFile.exists()) {
            throw new FileNotFoundException(jarFile.getAbsolutePath());
        }
        if (!jarFile.canRead()) {
            throw new IOException("can't read jar: " + jarFile.getAbsolutePath());
        }
        if (jarFile.isDirectory()) {
            throw new IOException("not a jar: " + jarFile.getAbsolutePath());
        }

        // add the jar using instrumentation, or fall back to reflection
        if (instrumentation != null) {
        	try(JarFile jar = new JarFile(jarFile)) {
        		instrumentation.appendToSystemClassLoaderSearch(jar);
        		return;
        	}
        }
        try {
            getAddUrlMethod().invoke(addUrlThis, jarFile.toURI().toURL());
        } catch (SecurityException iae) {
            throw new RuntimeException("security model prevents access to method", iae);
        } catch (Throwable t) {
            // IllegalAccessException
            // IllegalArgumentException
            // InvocationTargetException
            // MalformedURLException
            // (or a runtime error)
            throw new AssertionError("internal error", t);
        }
    }

    /**
     * Returns whether the extending the class path is supported on the host
     * JRE. If this returns false, the most likely causes are:
     * 
    *
  • the manifest is not configured to load the agent or the * {@code -javaagent:jarpath} argument was not specified (Java 9+); *
  • security restrictions are preventing reflective access to the class * loader (Java ≤ 8); *
  • the underlying VM neither supports agents nor uses URLClassLoader as * its system class loader (extremely unlikely from Java 1.6+). *
* * @return true if the Jar loader is supported on the Java runtime */ public static synchronized boolean isSupported() { try { return instrumentation != null || getAddUrlMethod() != null; } catch (Throwable t) { } return false; } /** * Returns a string that describes the strategy being used to add JAR files * to the class path. This is meant mainly to assist with debugging and * diagnosing client issues. * * @return returns {@code "none"} if no strategy was found, otherwise a * short describing the method used; the value {@code "reflection"} * indicates that a fallback not compatible with Java 9+ is being used */ public static synchronized String getStrategy() { String strat = "none"; if (instrumentation != null) { strat = loadedViaPreMain ? "agent" : "agent (main)"; } else { try { if (isSupported()) { strat = "reflection"; } } catch (Throwable t) { } } return strat; } /** * Called by the JRE. Do not call this method from user code. * *

* This method is automatically invoked when the JRE loads this class as an * agent using the option {@code -javaagent:jarPathOfThisClass}. * *

* For this to work the {@code MANIFEST.MF} file must * include the line {@code Premain-Class: prompto.utils.JarLoader}. * * @param agentArgs agent arguments; currently ignored * @param instrumentation provided by the JRE */ public static void premain(String agentArgs, Instrumentation instrumentation) { loadedViaPreMain = true; agentmain(agentArgs, instrumentation); } /** * Called by the JRE. Do not call this method from user code. * *

* This method is called when the agent is attached to a running process. In * practice, this is not how JarLoader is used, but it is implemented should * you need it. * *

* For this to work the {@code MANIFEST.MF} file must * include the line {@code Agent-Class: prompto.utils.JarLoader}. * * @param agentArgs agent arguments; currently ignored * @param instrumentation provided by the JRE */ public static void agentmain(String agentArgs, Instrumentation instrumentation) { if (instrumentation == null) { throw new NullPointerException("instrumentation"); } if (JarLoader.instrumentation == null) { JarLoader.instrumentation = instrumentation; } } private static Method getAddUrlMethod() { if (addUrlMethod == null) { addUrlThis = ClassLoader.getSystemClassLoader(); if (addUrlThis instanceof URLClassLoader) { try { final Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); method.setAccessible(true); addUrlMethod = method; } catch (NoSuchMethodException nsm) { throw new AssertionError(); // violates URLClassLoader API! } } else { throw new UnsupportedOperationException( "did you forget -javaagent:?" ); } } return addUrlMethod; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy