Расположение javaagent jar в bootclasspath

У меня есть javaagent jar, который я помещаю в bootclasspath, используя

Boot-Class-Path: myagent.jar

внутри файла MANIFEST.MF.

Мне нужно найти каталог в файловой системе, в котором находится банка.

Однако метод, описанный для этого здесь, не похоже, работает для меня:

 new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation().toURI().g­etPath());

В этом случае ProtectionDomain.getCodeSource () возвращает null. Я предполагаю, что это происходит из-за того, что jar был помещен в путь к классам загрузки. Из-за этого я также не могу использовать MyClass.getClassLoader (), чтобы получить местоположение ресурса.

Я использую Java 6.

Может кто подскажет, как узнать местонахождение баночки?


person pdeva    schedule 20.04.2011    source источник
comment
Я знаю, что здесь неодобрительно относятся к комментариям с простым выражением благодарности, но только потому, что я увидел, что вы упомянули Boot-Class-Path, мне стало любопытно, и собранная информация помогла мне элегантно решить проблему с загрузкой агента. Я проделал последнее вручную с большим количеством шаблонного кода, теперь он просто работает до тех пор, пока пользователь не изменяет имя артефакта, прежде чем использовать его в качестве JAR агента. Так что спасибо!   -  person kriegaex    schedule 30.06.2020


Ответы (2)


Вы можете использовать загрузчик системных классов, чтобы найти классы в пути к загрузочному классу. Например, это

System.out.println(
  ClassLoader.getSystemClassLoader().getResource("java/lang/String.class")
);

Распечатает что-то вроде,

jar:file:/C:/Program%20Files/Java/jdk1.6.0_22/jre/lib/rt.jar!/java/lang/String.class

Чтобы найти MyClass.class на диске, выполните

String urlString = ClassLoader
 .getSystemClassLoader()
 .getResource("com/my/package/MyClass.class")
 .toString();

urlString = urlString.substring(urlString.indexOf("file:"), urlString.indexOf('!'));
URL url = new URL(urlString);
File file = new File(url.toURI());
System.out.println(file);
System.out.println(file.exists());

Обновление 2020-06-29, автор: kriegaex: вместо того, чтобы писать новый ответ на этот старый вопрос, я хочу улучшить или обновить этот очень хороший ответ, также принимая во внимание пути с пробелы и модули Java 9+.

Вот метод, возвращающий путь к файлу класса как экземпляр File. Если файл класса является частью модуля времени выполнения JAR или Java (URL-адрес начинается с протокола jrt:), он просто возвращает пути к JAR или файлу JMOD, что может вам не понадобиться, но это просто демонстрация того, что вы можете редактировать, сколько душе угодно. Конечно, это все еще хакерская игра, например не учитывая классы, загруженные из веб-URL, а не из файла, но вы поняли.

public static File getFileForClass(String className) {
  className = className.replace('.', '/') + ".class";
  URL classURL = ClassLoader.getSystemClassLoader().getResource(className);
  if (classURL == null)
    return null;
  System.out.println("Class file URL: " + classURL);

  // Adapt this if you also have '.war' or other archive types
  if (classURL.toString().contains(".jar!")) {
    String jarFileName = classURL.getPath().replaceFirst("!.*", "");
    System.out.println("Containing JAR file: " + jarFileName);
    try {
      return new File(new URL(jarFileName).toURI());
    }
    catch (URISyntaxException | MalformedURLException e) {
      throw new RuntimeException(e);
    }
  }

  if (classURL.getProtocol().equals("jrt")) {
    String jrtModule = classURL.getFile().replaceFirst("/([^/]+).*", "$1");
    System.out.println("Target class is part of Java runtime module " + jrtModule);
    String jmodName = System.getProperty("java.home") + "/jmods/" + jrtModule + ".jmod";
    System.out.println("Containing Java module file: " + jmodName);
    return new File(jmodName);
  }
  try {
    return new File(classURL.toURI());
  }
  catch (URISyntaxException e) {
    throw new RuntimeException(e);
  }
}

Когда я вызываю это из одного из моих проектов IntelliJ IDEA с JDK 14, установленным по пути, содержащему пробелы, и намеренно добавляю JAR, также содержащий путь с пробелами для тестирования, этот код ...

public static void main(String[] args) throws IOException {
  Instrumentation instrumentation = ByteBuddyAgent.install();
  instrumentation.appendToSystemClassLoaderSearch(
    new JarFile("C:/Program Files/JetBrains/IntelliJ IDEA 2018.3/lib/idea_rt.jar")
  );
  Stream
    .of(
      Weaver.class.getName(),
      String.class.getName(),
      ByteBuddy.class.getName(),
      "com.intellij.execution.TestDiscoveryListener"
    )
    .forEach(className -> System.out.printf("Found file: %s%n%n", getFileForClass(className)));
}

... дает этот вывод консоли:

Class file URL: file:/C:/Users/alexa/Documents/java-src/Sarek/sarek-aspect/target/classes/dev/sarek/agent/aspect/Weaver.class
Found file: C:\Users\alexa\Documents\java-src\Sarek\sarek-aspect\target\classes\dev\sarek\agent\aspect\Weaver.class

Class file URL: jrt:/java.base/java/lang/String.class
Target class is part of Java runtime module java.base
Containing Java module file: C:\Program Files\Java\jdk-14.0.1/jmods/java.base.jmod
Found file: C:\Program Files\Java\jdk-14.0.1\jmods\java.base.jmod

Class file URL: jar:file:/C:/Users/alexa/.m2/repository/net/bytebuddy/byte-buddy/1.10.13/byte-buddy-1.10.13.jar!/net/bytebuddy/ByteBuddy.class
Containing JAR file: file:/C:/Users/alexa/.m2/repository/net/bytebuddy/byte-buddy/1.10.13/byte-buddy-1.10.13.jar
Found file: C:\Users\alexa\.m2\repository\net\bytebuddy\byte-buddy\1.10.13\byte-buddy-1.10.13.jar

Class file URL: jar:file:/C:/Program%20Files/JetBrains/IntelliJ%20IDEA%202018.3/lib/idea_rt.jar!/com/intellij/execution/TestDiscoveryListener.class
Containing JAR file: file:/C:/Program%20Files/JetBrains/IntelliJ%20IDEA%202018.3/lib/idea_rt.jar
Found file: C:\Program Files\JetBrains\IntelliJ IDEA 2018.3\lib\idea_rt.jar
person sbridges    schedule 23.04.2011
comment
не работает, если в пути есть пробел, например jar: file: / C: /test%20recorder/recorder2/jar/recorder-1.0.0-beta-build86.jar! / com / chronon / sb / recorder / RecorderMain .класс. В этом случае создание нового файла (C: /test%20recorder/recorder2/jar/recorder-1.0.0-beta-build86.jar) .exists () возвращает false - person pdeva; 24.04.2011
comment
Вам просто нужно отменить экранирование URL-адреса, stackoverflow.com/questions/623861/ - person sbridges; 24.04.2011
comment
в url.substring () вы сделали file: .length () + 1, не хотите ли +1, удалить начало '/' в файловой системе unix? - person pdeva; 24.04.2011
comment
возможно, это должно быть легко исправить - person sbridges; 24.04.2011
comment
Примечание. URLDecoder НЕ ДОЛЖЕН использоваться для отмены экранирования URL-адреса (он преобразует + в пробелы, что неверно). Правильный способ работы с URL-адресами - url.toURI().getSchemeSpecificPart() (он вернет декодированный URL-адрес) - person Vladimir Sitnikov; 09.09.2018
comment
Я взял на себя смелость добавить больше примеров кода к этому очень хорошему ответу, учитывая пути с пробелами и модули Java 9+. Я не хотел писать новый ответ и красть у этого репутацию. Я надеюсь, что с редактированием все в порядке - без обид. В противном случае @sbridges может просто отменить его. - person kriegaex; 29.06.2020

Вы можете сделать что-то вроде этого:

final String   bootClassPath;
final String[] entries;

// different VM may use a different string here...
bootClassPath = System.getProperty("sun.boot.class.path");
entries       = bootClassPath.split(File.pathSeparator);

for(final String entry : entries)
{
    System.out.println(entry);
}

Которая проходит через каждую запись в пути к классам загрузки. Затем для каждой записи вы можете получить файл JAR, просмотреть файл манифеста и посмотреть, принадлежит ли он вам. Вы можете добавить еще одну запись в манифест для поиска, если еще нет чего-то уникального.

person TofuBeer    schedule 23.04.2011
comment
RuntimeMXBean.getBootClassPath, вероятно, лучше. - person Brett Kail; 27.04.2011
comment
Наверное ... давно меня не волновали пути к загрузочным классам ... Я не в курсе. Другой ответ в любом случае лучше моего (просто держите его под рукой на случай, если он кому-то поможет в какой-то момент :-) - person TofuBeer; 27.04.2011