実行時にサードパーティのJavaライブラリのバージョンを判別することは可能ですか?
サードパーティJavaライブラリはJarファイルを意味し、Jarファイルマニフェストにはライブラリのバージョンを指定するためのプロパティがあります。
注意:shouldであっても、すべてのJarファイルが実際にバージョンを指定しているわけではありません。
組み込みJavaその情報を読み取る方法はリフレクションを使用することですが、クエリを実行するにはライブラリ内のsomeクラスを知っている必要があります。どのクラスでもかまいません/インターフェース。
例
public class Test {
public static void main(String[] args) {
printVersion(org.Apache.http.client.HttpClient.class);
printVersion(com.fasterxml.jackson.databind.ObjectMapper.class);
printVersion(com.google.gson.Gson.class);
}
public static void printVersion(Class<?> clazz) {
Package p = clazz.getPackage();
System.out.printf("%s%n Title: %s%n Version: %s%n Vendor: %s%n",
clazz.getName(),
p.getImplementationTitle(),
p.getImplementationVersion(),
p.getImplementationVendor());
}
}
出力
org.Apache.http.client.HttpClient
Title: HttpComponents Apache HttpClient
Version: 4.3.6
Vendor: The Apache Software Foundation
com.fasterxml.jackson.databind.ObjectMapper
Title: jackson-databind
Version: 2.7.0
Vendor: FasterXML
com.google.gson.Gson
Title: null
Version: null
Vendor: null
普遍的な標準はありませんが、ほとんどのオープンソースライブラリ、またはMavenリリースプラグインまたは互換性のあるメカニズムを介してMavenリポジトリからリリースされるものすべてに機能するハックがあります。 JVM上の他のほとんどのビルドシステムはMavenと互換性があるため、これはGradleまたはIvy(および場合によっては他のライブラリ)を介して配布されるライブラリにも適用されます。
Mavenリリースプラグイン(および互換性のあるすべてのプロセス)は、リリースされたJarにMETA-INF/${groupId}.${artifactId}/pom.properties
というファイルを作成します。このファイルには、プロパティgroupId
、artifactId
、およびversion
が含まれています。
このファイルをチェックして解析することで、ライブラリバージョンの大部分のバージョンを検出できます。サンプルコード(Java 8以降):
/**
* Reads a library's version if the library contains a Maven pom.properties
* file. You probably want to cache the output or write it to a constant.
*
* @param referenceClass any class from the library to check
* @return an Optional containing the version String, if present
*/
public static Optional<String> extractVersion(
final Class<?> referenceClass) {
return Optional.ofNullable(referenceClass)
.map(cls -> unthrow(cls::getProtectionDomain))
.map(ProtectionDomain::getCodeSource)
.map(CodeSource::getLocation)
.map(url -> unthrow(url::openStream))
.map(is -> unthrow(() -> new JarInputStream(is)))
.map(jis -> readPomProperties(jis, referenceClass))
.map(props -> props.getProperty("version"));
}
/**
* Locate the pom.properties file in the Jar, if present, and return a
* Properties object representing the properties in that file.
*
* @param jarInputStream the jar stream to read from
* @param referenceClass the reference class, whose ClassLoader we'll be
* using
* @return the Properties object, if present, otherwise null
*/
private static Properties readPomProperties(
final JarInputStream jarInputStream,
final Class<?> referenceClass) {
try {
JarEntry jarEntry;
while ((jarEntry = jarInputStream.getNextJarEntry()) != null) {
String entryName = jarEntry.getName();
if (entryName.startsWith("META-INF")
&& entryName.endsWith("pom.properties")) {
Properties properties = new Properties();
ClassLoader classLoader = referenceClass.getClassLoader();
properties.load(classLoader.getResourceAsStream(entryName));
return properties;
}
}
} catch (IOException ignored) { }
return null;
}
/**
* Wrap a Callable with code that returns null when an exception occurs, so
* it can be used in an Optional.map() chain.
*/
private static <T> T unthrow(final Callable<T> code) {
try {
return code.call();
} catch (Exception ignored) { return null; }
}
このコードをテストするために、3つのクラスを試してみます。1つは [〜#〜] vavr [〜#〜] 、1つは Guava から、もう1つはJDKから。
public static void main(String[] args) {
Stream.of(io.vavr.collection.LinkedHashMultimap.class,
com.google.common.collect.LinkedHashMultimap.class,
Java.util.LinkedHashMap.class)
.map(VersionExtractor::extractVersion)
.forEach(System.out::println);
}
私のマシンでの出力:
Optional[0.9.2]
Optional[24.1-jre]
Optional.empty
私は多くの非常にレガシーなJavaプロジェクトでこれを行うことを任されていたので、答えは「それは可能ですが、それを行う方法は異なります」です。
まず、JARMANIFEST.MFファイルを確認します。時々あなたはとても幸運になります。
次に、JARファイルをスキャンしてバージョンフィールドを探します。運が良ければ、その価値がうそをつくこともあります。
第三に、含まれているプロパティファイルをスキャンします。バージョンをプロパティファイルに保持するための一般的なANTビルドパターンがありました(更新が簡単でした)。
第4に、そのプロジェクトで使用可能なJARファイルのダウンロードを開始します。時折、バージョン番号が本当に失われることがあり、それを検証する唯一の方法は、既知の古いバージョンを見つけて、JARとJARの比較を行うことです。
他のテクニックもありますが、これら4つはほぼすべてのシナリオをカバーしています。非常に貧弱な名前のニッチライブラリのいくつかにとって、それはかなりの挑戦になる可能性があります。