Чтение моего собственного манифеста Джара

Мне нужно прочитать файл Manifest , который доставил мой class, но когда я использую:

 getClass().getClassLoader().getResources(...) 

Я получаю MANIFEST с первого .jar загруженного в Java Runtime.
Мое приложение будет работать из апплета или веб-сайта,
поэтому я не буду иметь доступ к своему .jar файлу, я думаю.

Я действительно хочу прочитать атрибут Export-package из .jar который запустил Felix OSGi, поэтому я могу предоставить эти пакеты Felix. Есть идеи?

Вы можете сделать одну из двух вещей:

  1. Вызовите getResources() и выполните итерацию по возвращенной коллекции URL-адресов, прочитав их как манифесты, пока не найдете их:

     Enumeration resources = getClass().getClassLoader() .getResources("META-INF/MANIFEST.MF"); while (resources.hasMoreElements()) { try { Manifest manifest = new Manifest(resources.nextElement().openStream()); // check that this is your manifest and do what you need or get the next one ... } catch (IOException E) { // handle } } 
  2. Вы можете попробовать проверить getClass().getClassLoader() – это экземпляр java.net.URLClassLoader . Большинство загрузчиков classов Sun, включая AppletClassLoader . Затем вы можете бросить его и вызвать findResource() который был известен – для апплетов – по крайней мере – для возврата необходимого манифеста непосредственно:

     URLClassLoader cl = (URLClassLoader) getClass().getClassLoader(); try { URL url = cl.findResource("META-INF/MANIFEST.MF"); Manifest manifest = new Manifest(url.openStream()); // do stuff with it ... } catch (IOException E) { // handle } 

Сначала вы можете найти URL для своего classа. Если это JAR, тогда вы загружаете манифест оттуда. Например,

 Class clazz = MyClass.class; String className = clazz.getSimpleName() + ".class"; String classPath = clazz.getResource(className).toString(); if (!classPath.startsWith("jar")) { // Class not from JAR return; } String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) + "/META-INF/MANIFEST.MF"; Manifest manifest = new Manifest(new URL(manifestPath).openStream()); Attributes attr = manifest.getMainAttributes(); String value = attr.getValue("Manifest-Version"); 

Вы можете использовать Manifests из jcabi-manifestests и читать любой атрибут из любого из доступных файлов MANIFEST.MF только с одной строкой:

 String value = Manifests.read("My-Attribute"); 

Единственная зависимость, в которой вы нуждаетесь:

  com.jcabi jcabi-manifests 0.7.5  

Кроме того, см. Это сообщение в блоге для более подробной информации: http://www.yegor256.com/2014/07/03/how-to-read-manifest-mf.html

Я считаю, что наиболее подходящим способом получить манифест для любого пакета (включая пакет, который загружал данный class), является использование объекта Bundle или BundleContext.

 // If you have a BundleContext Dictionary headers = bundleContext.getBundle().getHeaders(); // If you don't have a context, and are running in 4.2 Bundle bundle = FrameworkUtil.getBundle(this.getClass()); bundle.getHeaders(); 

Обратите внимание, что объект Bundle также предоставляет getEntry(String path) для поиска ресурсов, содержащихся в определенном пакете, а не для поиска всей getEntry(String path) .

В общем случае, если вы хотите получить информацию, связанную с пакетом, не полагайтесь на предположения о загрузчиках classов, просто используйте API OSGi напрямую.

Следующий код работает с несколькими типами архивов (jar, war) и несколькими типами загрузчиков classов (jar, url, vfs, …)

  public static Manifest getManifest(Class clz) { String resource = "/" + clz.getName().replace(".", "/") + ".class"; String fullPath = clz.getResource(resource).toString(); String archivePath = fullPath.substring(0, fullPath.length() - resource.length()); if (archivePath.endsWith("\\WEB-INF\\classes") || archivePath.endsWith("/WEB-INF/classes")) { archivePath = archivePath.substring(0, archivePath.length() - "/WEB-INF/classes".length()); // Required for wars } try (InputStream input = new URL(archivePath + "/META-INF/MANIFEST.MF").openStream()) { return new Manifest(input); } catch (Exception e) { throw new RuntimeException("Loading MANIFEST for class " + clz + " failed!", e); } } 

Вы можете использовать getProtectionDomain (). GetCodeSource () следующим образом:

 URL url = Menu.class.getProtectionDomain().getCodeSource().getLocation(); File file = DataUtilities.urlToFile(url); JarFile jar = null; try { jar = new JarFile(file); Manifest manifest = jar.getManifest(); Attributes attributes = manifest.getMainAttributes(); return attributes.getValue("Built-By"); } finally { jar.close(); } 

Самый простой способ – использовать class JarURLConnection:

 String className = getClass().getSimpleName() + ".class"; String classPath = getClass().getResource(className).toString(); if (!classPath.startsWith("jar")) { return DEFAULT_PROPERTY_VALUE; } URL url = new URL(classPath); JarURLConnection jarConnection = (JarURLConnection) url.openConnection(); Manifest manifest = jarConnection.getManifest(); Attributes attributes = manifest.getMainAttributes(); return attributes.getValue(PROPERTY_NAME); 

Потому что в некоторых случаях ...class.getProtectionDomain().getCodeSource().getLocation(); дает путь с vfs:/ , поэтому это нужно обрабатывать дополнительно.

Почему вы включаете шаг getClassLoader? Если вы скажете «this.getClass (). GetResource ()», вы должны получать ресурсы по отношению к вызывающему classу. Я никогда не использовал ClassLoader.getResource (), хотя из быстрого просмотра Java Docs это похоже на то, что вы получите первый ресурс этого имени, найденный в любом текущем пути к classам.

  public static Manifest getManifest( Class cl ) { InputStream inputStream = null; try { URLClassLoader classLoader = (URLClassLoader)cl.getClassLoader(); String classFilePath = cl.getName().replace('.','/')+".class"; URL classUrl = classLoader.getResource(classFilePath); if ( classUrl==null ) return null; String classUri = classUrl.toString(); if ( !classUri.startsWith("jar:") ) return null; int separatorIndex = classUri.lastIndexOf('!'); if ( separatorIndex<=0 ) return null; String manifestUri = classUri.substring(0,separatorIndex+2)+"META-INF/MANIFEST.MF"; URL url = new URL(manifestUri); inputStream = url.openStream(); return new Manifest( inputStream ); } catch ( Throwable e ) { // handle errors ... return null; } finally { if ( inputStream!=null ) { try { inputStream.close(); } catch ( Throwable e ) { // ignore } } } } 

Я использовал решение от Anthony Juckel, но в MANIFEST.MF ключ должен начинаться с прописного.

Поэтому мой файл MANIFEST.MF содержит ключ типа:

Mykey: значение

Затем в активаторе или другом classе вы можете использовать код от Anthony, чтобы прочитать файл MANIFEST.MF и необходимое вам значение.

 // If you have a BundleContext Dictionary headers = bundleContext.getBundle().getHeaders(); // If you don't have a context, and are running in 4.2 Bundle bundle = `FrameworkUtil.getBundle(this.getClass()); bundle.getHeaders(); 

Я соглашусь с тем, что этот ответ не отвечает на первоначальный вопрос, который обычно имеет доступ к Манифесту. Однако, если действительно требуется прочитать один из нескольких «стандартных» атрибутов Manifest, следующее решение намного проще, чем те, которые были выше. Поэтому я надеюсь, что модератор это позволит. Обратите внимание, что это решение находится в Kotlin, а не Java, но я бы ожидал, что порт для Java будет тривиальным. (Хотя я признаю, что не знаю эквивалент Java «.package».

В моем случае я хотел прочитать атрибут «Версия реализации», поэтому я начал с приведенных выше решений, чтобы получить stream, а затем прочитать его для получения значения. Хотя это решение сработало, коллега, просматривая мой код, показал мне более простой способ сделать то, что я хотел. Обратите внимание, что это решение находится в Kotlin, а не в Java.

 val myPackage = MyApplication::class.java.`package` val implementationVersion = myPackage.implementationVersion 

Еще раз обратите внимание, что это не отвечает на исходный вопрос, в частности «Export-package» не является одним из поддерживаемых атрибутов. Тем не менее, есть myPackage.name, которое возвращает значение. Возможно, кто-то, кто понимает это больше, чем я могу прокомментировать, возвращает ли это значение, которое запрашивает оригинальный плакат.

У меня есть это странное решение, которое запускает военные приложения на встроенном сервере Jetty, но эти приложения также должны запускаться на стандартных серверах Tomcat, и у нас есть некоторые специальные свойства в manfest.

Проблема заключалась в том, что когда в Tomcat манифест можно было прочитать, но когда в причале был выбран случайный манифест (который пропустил специальные свойства)

Основываясь на ответе Алекса Коншина, я придумал следующее решение (входной stream затем используется в classе Manifest):

 private static InputStream getWarManifestInputStreamFromClassJar(Class cl ) { InputStream inputStream = null; try { URLClassLoader classLoader = (URLClassLoader)cl.getClassLoader(); String classFilePath = cl.getName().replace('.','/')+".class"; URL classUrl = classLoader.getResource(classFilePath); if ( classUrl==null ) return null; String classUri = classUrl.toString(); if ( !classUri.startsWith("jar:") ) return null; int separatorIndex = classUri.lastIndexOf('!'); if ( separatorIndex<=0 ) return null; String jarManifestUri = classUri.substring(0,separatorIndex+2); String containingWarManifestUri = jarManifestUri.substring(0,jarManifestUri.indexOf("WEB-INF")).replace("jar:file:/","file:///") + MANIFEST_FILE_PATH; URL url = new URL(containingWarManifestUri); inputStream = url.openStream(); return inputStream; } catch ( Throwable e ) { // handle errors LOGGER.warn("No manifest file found in war file",e); return null; } } 
Interesting Posts

Как создать экземпляр типа, представленного параметром type в Scala

java: получить все имена переменных в classе

Переход на вкладки вправо или влево от текущей вкладки в Notepad ++

Тесты JUnit проходят в Eclipse, но сбой в Maven Surefire

Обновления Windows и поиск драйверов потребляют огромное количество оперативной памяти.

В чем разница между «методом classа» и «статическим методом»?

Добавить индекс к смежным прогонам равных значений

Где в памяти хранятся типы с нулевым значением?

Скопировать имя файла в буфер обмена

как десериализовать JSON в IEnumerable с помощью Newtonsoft JSON.NET

Как решить атаки DDOS в Windows Server 2008 R2 / Windows Server 2012?

Отправка клавиши F1 – F12 с внешней клавиатурой с отсутствующими клавишами на iPad Pro с использованием программного обеспечения эмуляции SSH

Можно ли постоянно использовать 100% -ное использование ЦП?

Заголовок изменяется при прокрутке вниз (jQuery)

Как я могу прочитать файл даже при получении исключения «в использовании другим процессом»?

Давайте будем гением компьютера.