Muszę przeczytać Manifest
plik, który dostarczył moje zajęcia, ale kiedy używam:
getClass().getClassLoader().getResources(...)
Otrzymuję MANIFEST
od pierwszego .jar
załadowanego do środowiska wykonawczego Java.
Moja aplikacja będzie działać z apletu lub startera internetowego,
więc myślę, że nie będę miał dostępu do własnego .jar
pliku.
W rzeczywistości chcę przeczytać Export-package
atrybut, z .jar
którego uruchomiono Felix OSGi, aby móc udostępnić te pakiety Felixowi. Jakieś pomysły?
java
osgi
manifest.mf
apache-felix
Houtman
źródło
źródło
Odpowiedzi:
Możesz zrobić jedną z dwóch rzeczy:
Wywołaj
getResources()
i iteruj po zwróconej kolekcji adresów URL, odczytując je jako manifesty, aż znajdziesz swój:Enumeration<URL> 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 } }
Możesz spróbować sprawdzić, czy
getClass().getClassLoader()
jest to instancjajava.net.URLClassLoader
. Większość programów ładujących klasy Sun to, w tymAppletClassLoader
. Następnie możesz go rzucić i wywołać,findResource()
który jest znany - przynajmniej w przypadku apletów - aby bezpośrednio zwrócić potrzebny manifest: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 }
źródło
Najpierw możesz znaleźć adres URL swojej klasy. Jeśli jest to plik JAR, ładujesz stamtąd manifest. Na przykład,
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");
źródło
classPath.replace("org/example/MyClass.class", "META-INF/MANIFEST.MF"
getSimpleName
usuwa nazwę klasy zewnętrznej. To będzie pracować dla klas wewnętrznych:clazz.getName().replace (".", "/") + ".class"
.Możesz użyć
Manifests
z manifestów jcabi i odczytać dowolny atrybut z dowolnego z dostępnych plików MANIFEST.MF za pomocą tylko jednej linii:String value = Manifests.read("My-Attribute");
Jedyna zależność, której potrzebujesz, to:
<dependency> <groupId>com.jcabi</groupId> <artifactId>jcabi-manifests</artifactId> <version>0.7.5</version> </dependency>
Więcej informacji można znaleźć w tym poście na blogu: http://www.yegor256.com/2014/07/03/how-to-read-manifest-mf.html
źródło
<logger name="com.jcabi.manifests" level="OFF"/>
Przyznam się z góry, że ta odpowiedź nie odpowiada na pierwotne pytanie, jakim jest ogólnie dostęp do Manifestu. Jeśli jednak naprawdę trzeba przeczytać jeden z wielu „standardowych” atrybutów Manifestu, poniższe rozwiązanie jest znacznie prostsze niż te zamieszczone powyżej. Mam więc nadzieję, że moderator na to pozwoli. Zauważ, że to rozwiązanie jest w Kotlinie, a nie w Javie, ale spodziewałbym się, że przeniesienie do Javy byłoby trywialne. (Chociaż przyznaję, że nie znam odpowiednika Java ".`package`".
W moim przypadku chciałem odczytać atrybut „Implementation-Version”, więc zacząłem od rozwiązań podanych powyżej, aby uzyskać strumień, a następnie przeczytałem go, aby uzyskać wartość. Chociaż to rozwiązanie zadziałało, współpracownik przeglądający mój kod pokazał mi łatwiejszy sposób zrobienia tego, co chciałem. Zauważ, że to rozwiązanie jest w Kotlinie, a nie w Javie.
val myPackage = MyApplication::class.java.`package` val implementationVersion = myPackage.implementationVersion
Jeszcze raz zauważ, że nie jest to odpowiedź na pierwotne pytanie, w szczególności „Pakiet eksportowy” nie wydaje się być jednym z obsługiwanych atrybutów. To powiedziawszy, istnieje myPackage.name, który zwraca wartość. Być może ktoś, kto rozumie to bardziej niż ja, mogę skomentować, czy zwraca to wartość, o którą prosi oryginalny plakat.
źródło
String implementationVersion = MyApplication.class.getPackage().getImplementationVersion();
Uważam, że najwłaściwszym sposobem uzyskania manifestu dla dowolnego pakietu (w tym pakietu, który załadował daną klasę) jest użycie obiektu Bundle lub 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();
Zauważ, że obiekt Bundle umożliwia również
getEntry(String path)
wyszukiwanie zasobów zawartych w konkretnym pakiecie, zamiast przeszukiwania całej ścieżki klas tego pakietu.Ogólnie rzecz biorąc, jeśli potrzebujesz informacji specyficznych dla pakietu, nie polegaj na założeniach dotyczących programów ładujących klasy, po prostu użyj bezpośrednio interfejsów API OSGi.
źródło
Najłatwiej jest użyć klasy 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);
Ponieważ w niektórych przypadkach
...class.getProtectionDomain().getCodeSource().getLocation();
podaje ścieżkę zvfs:/
, więc należy to dodatkowo załatwić.źródło
Poniższy kod działa z wieloma typami archiwów (jar, war) i wieloma typami klas ładujących (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); } }
źródło
clz.getResource(resource).toString()
ukośników odwrotnych?Możesz użyć getProtectionDomain (). GetCodeSource () w następujący sposób:
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(); }
źródło
getCodeSource
może wrócićnull
. Jakie są kryteria, aby to zadziałało? Dokumentacja nie wyjaśnić.DataUtilities
importowany? Wygląda na to, że nie ma go w JDK.Dlaczego dołączasz krok getClassLoader? Jeśli powiesz „this.getClass (). GetResource ()”, powinieneś otrzymywać zasoby w stosunku do klasy wywołującej. Nigdy nie korzystałem z ClassLoader.getResource (), chociaż z szybkiego spojrzenia na dokumentację Java wygląda na to, że dostaniesz pierwszy zasób o tej nazwie znaleziony w dowolnej bieżącej ścieżce klas.
źródło
class.getResource("myresource.txt")
spróbuje załadować ten zasób zcom/mypackage/myresource.txt
. Jak dokładnie zamierzasz użyć tego podejścia, aby uzyskać manifest?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 } } } }
źródło
cl.getResourceAsStream("META-INF/MANIFEST.MF")
.classLoader.getResource(..)
iurl.openStream()
jest całkowicie nieistotny i podatny na błędy, ponieważ próbuje zrobić to samo, coclassLoader.getResourceAsStream(..)
robi.ClassLoader classLoader = cl.getClassLoader(); return new Manifest(classLoader.getResourceAsStream("/META-INF/MANIFEST.MF"));
Użyłem rozwiązania Anthony'ego Juckela, ale w pliku MANIFEST.MF klucz musi zaczynać się od wielkich liter.
Więc mój plik MANIFEST.MF zawiera klucz taki jak:
Mykey: wartość
Następnie w aktywatorze lub innej klasie możesz użyć kodu Anthony'ego do odczytania pliku MANIFEST.MF i potrzebnej wartości.
// 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();
źródło
Mam to dziwne rozwiązanie, które uruchamia aplikacje wojenne na wbudowanym serwerze Jetty, ale te aplikacje muszą również działać na standardowych serwerach Tomcat, a my mamy kilka specjalnych właściwości w manfestie.
Problem polegał na tym, że w Tomcat manifest można było odczytać, ale gdy był na molo, został odebrany losowy manifest (który pomijał specjalne właściwości)
Opierając się na odpowiedzi Alexa Konshina, wymyśliłem następujące rozwiązanie (strumień wejściowy jest następnie używany w klasie 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; } }
źródło