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

com.nordstrom.common.jar.JarUtils Maven / Gradle / Ivy

package com.nordstrom.common.jar;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;

import com.nordstrom.common.base.UncheckedThrow;

/**
 * This utility class provides methods related to Java JAR files:
 * 
 * 
    *
  • {@link #getClasspath} assemble a classpath string from the specified array of dependencies.
  • *
  • {@link #findJarPathFor} find the path to the JAR file from which the named class was loaded.
  • *
  • {@link #getJarPremainClass} gets the 'Premain-Class' attribute from the indicated JAR file.
  • *
* * NOTE: The core implementation for the {@link #findJarPathFor(String)} method was shared on * Stack Overflow by * notnoop in their answer to a question regarding * how to locate the JAR file from which a specified class was loaded. This code was attributed to the * lombok.patcher project, published by * Reinier Zwitserloot. An updated version of the original * code can be found * here. */ public class JarUtils { private JarUtils() { throw new AssertionError("JarUtils is a static utility class that cannot be instantiated."); } /** * Assemble a classpath string from the specified array of dependencies. *

* NOTE: If any of the specified dependency contexts names the {@code premain} class of a Java agent, the * string returned by this method will contain two records delimited by a {@code newline} character: * *

    *
  • 0 - assembled classpath string
  • *
  • 1 - tab-delimited list of Java agent paths
  • *
* * @param dependencyContexts array of dependency contexts * @return assembled classpath string (see NOTE) */ public static String getClasspath(final String[] dependencyContexts) { // get dependency context paths List contextPaths = getContextPaths(false, dependencyContexts); // pop classpath from collection String classPath = contextPaths.remove(0); // if no agents were found if (contextPaths.isEmpty()) { // classpath only return classPath; } else { // classpath plus tab-delimited list of agent paths return classPath + "\n" + String.join("\t", contextPaths); } } /** * Assemble a list of context paths from the specified array of dependencies. *

* NOTE: The first item of the returned list contains a path-delimited string of JAR file paths suitable * for use with the Java {@code -cp} command line option. If any of the specified dependency contexts names the * {@code premain} class of a Java agent, subsequent items contain fully-formed {@code -javaagent} specifications. * * @param dependencyContexts array of dependency contexts * @return list of classpath/javaagent specifications (see NOTE) */ public static List getContextPaths(final String[] dependencyContexts) { return getContextPaths(true, dependencyContexts); } /** * Assemble a list of context paths from the specified array of dependencies. *

* NOTE: The first item of the returned list contains a path-delimited string of JAR file paths suitable * for use with the Java {@code -cp} command line option. If any of the specified dependency contexts names the * {@code premain} class of a Java agent, subsequent items contain Java agent JAR paths with optional prefix. * * @param prefixAgents {@code true} to request prefixing Java agent paths with {@code -javaagent:} * @param dependencyContexts array of dependency contexts * @return list of classpath/javaagent paths (see NOTE) */ private static List getContextPaths(final boolean prefixAgents, final String[] dependencyContexts) { Set pathList = new HashSet<>(); Set agentList = new HashSet<>(); List contextPaths = new ArrayList<>(); final String prefix = prefixAgents ? "-javaagent:" : ""; for (String contextClassName : dependencyContexts) { // get JAR path for this dependency context String jarPath = findJarPathFor(contextClassName); // if this context names the premain class of a Java agent if (contextClassName.equals(getJarPremainClass(jarPath))) { // collect agent path agentList.add(prefix + jarPath); // otherwise } else { // collect class path pathList.add(jarPath); } } // add assembled classpath string contextPaths.add(String.join(File.pathSeparator, pathList)); // add Java agent paths contextPaths.addAll(agentList); return contextPaths; } /** * If the provided class has been loaded from a JAR file that is on the * local file system, will find the absolute path to that JAR file. *

* NOTE: The core implementation of this method was lifted from * the {@code lombok.patcher} project. See the comments at the head of * {@link JarUtils this class} for details. * * @param contextClassName * The JAR file that contained the class file that represents * this class will be found. * @return absolute path to the JAR file from which the specified class was * loaded * @throws IllegalStateException * If the specified class was loaded from a directory or in some * other way (such as via HTTP, from a database, or some other * custom class-loading device). */ public static String findJarPathFor(final String contextClassName) { Class contextClass; try { contextClass = Class.forName(contextClassName); } catch (ClassNotFoundException e) { throw UncheckedThrow.throwUnchecked(e); } String shortName = contextClassName; int idx = shortName.lastIndexOf('.'); String protocol; if (idx > -1) { shortName = shortName.substring(idx + 1); } String uri = contextClass.getResource(shortName + ".class").toString(); if (uri.startsWith("file:")) { protocol = "file:"; String relPath = '/' + contextClassName.replace('.', '/') + ".class"; if (uri.endsWith(relPath)) { idx = uri.length() - relPath.length(); } else { throw new IllegalStateException("Unrecognized structure file-protocol path: " + uri); } } else if (uri.startsWith("jar:file:")) { protocol = "jar:file:"; idx = uri.indexOf('!'); if (idx == -1) { throw new IllegalStateException("No separator found in jar-protocol path: " + uri); } } else { idx = uri.indexOf(':'); protocol = (idx > -1) ? uri.substring(0, idx) : "(unknown)"; throw new IllegalStateException("This class has been loaded remotely via the " + protocol + " protocol. Only loading from a jar on the local file system is supported."); } try { String fileName = URLDecoder.decode(uri.substring(protocol.length(), idx), Charset.defaultCharset().name()); return new File(fileName).getAbsolutePath(); } catch (UnsupportedEncodingException e) { throw (InternalError) new InternalError( "Default charset doesn't exist. Your VM is borked.").initCause(e); } } /** * Extract the 'Premain-Class' attribute from the manifest of the indicated JAR file. * * @param jarPath absolute path to the JAR file * @return value of 'Premain-Class' attribute; {@code null} if unspecified */ public static String getJarPremainClass(String jarPath) { try(InputStream inputStream = new FileInputStream(jarPath); JarInputStream jarStream = new JarInputStream(inputStream)) { Manifest manifest = jarStream.getManifest(); if (manifest != null) { return manifest.getMainAttributes().getValue("Premain-Class"); } } catch (IOException e) { // nothing to do here } return null; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy