如何在生产中测试和运行jar时创建一个可用的MANIFEST.MF?

我花了太多时间试图解决这个问题。 这应该是最简单的事情,每个在Java中分发Java应用程序的人都必须处理它。

我只是想知道向我的Java应用程序添加版本控制的正确方法,以便我可以在测试时访问版本信息,例如在Eclipse中调试从jar运行。

这是我在build.xml中的内容:

               

这创建了/META-INF/MANIFEST.MF,我可以在Eclipse中调试时读取值:

 public MyClass() { try { InputStream stream = getClass().getResourceAsStream("/META-INF/MANIFEST.MF"); Manifest manifest = new Manifest(stream); Attributes attributes = manifest.getMainAttributes(); String implementationTitle = attributes.getValue("Implementation-Title"); String implementationVersion = attributes.getValue("Implementation-Version"); String builtDate = attributes.getValue("Built-Date"); String builtBy = attributes.getValue("Built-By"); } catch (IOException e) { logger.error("Couldn't read manifest."); } 

}

但是,当我创建jar文件时,它会加载另一个jar的清单(可能是应用程序加载的第一个jar – 在我的例子中,是activation.jar)。

此外,尽管所有正确的值都在清单文件中,但以下代码不起作用。

  Package thisPackage = getClass().getPackage(); String implementationVersion = thisPackage.getImplementationVersion(); 

有任何想法吗?

您可以在任意jar中获取任意类的清单,而无需解析类URL(可能很脆弱)。 只需找到您知道的jar所需的资源,然后将连接转换为JarURLConnection。

如果您希望代码在类未捆绑在jar中时工作,请添加一个instanceof检查返回的URL连接类型。 解压缩的类层次结构中的类将返回内部Sun FileURLConnection而不是JarUrlConnection。 然后,您可以使用其他答案中描述的InputStream方法之一加载Manifest。

 @Test public void testManifest() throws IOException { URL res = org.junit.Assert.class.getResource(org.junit.Assert.class.getSimpleName() + ".class"); JarURLConnection conn = (JarURLConnection) res.openConnection(); Manifest mf = conn.getManifest(); Attributes atts = mf.getMainAttributes(); for (Object v : atts.values()) { System.out.println(v); } } 

你想用这个:

 Enumeration resources = Thread.currentThread().getContextClassLoader().getResources("META-INF/MANIFEST.MF"); 

您可以解析URL以找出清单中的清单,然后通过getInputStream()读取清单以解析清单。

以下是我发现的有效方法:

packageVersion.java:

 package com.company.division.project.packageversion; import java.io.IOException; import java.io.InputStream; import java.util.jar.Attributes; import java.util.jar.Manifest; public class packageVersion { void printVersion() { try { InputStream stream = getClass().getResourceAsStream("/META-INF/MANIFEST.MF"); if (stream == null) { System.out.println("Couldn't find manifest."); System.exit(0); } Manifest manifest = new Manifest(stream); Attributes attributes = manifest.getMainAttributes(); String impTitle = attributes.getValue("Implementation-Title"); String impVersion = attributes.getValue("Implementation-Version"); String impBuildDate = attributes.getValue("Built-Date"); String impBuiltBy = attributes.getValue("Built-By"); if (impTitle != null) { System.out.println("Implementation-Title: " + impTitle); } if (impVersion != null) { System.out.println("Implementation-Version: " + impVersion); } if (impBuildDate != null) { System.out.println("Built-Date: " + impBuildDate); } if (impBuiltBy != null) { System.out.println("Built-By: " + impBuiltBy); } System.exit(0); } catch (IOException e) { System.out.println("Couldn't read manifest."); } } /** * @param args */ public static void main(String[] args) { packageVersion version = new packageVersion(); version.printVersion(); } } 

这是匹配的build.xml:

                         

如果使用与加载类相同的类加载器,则可以访问jar中的清单(或任何其他)文件。

 this.getClass().getClassLoader().getResourceAsStream( ... ) ; 

如果您是multithreading,请使用以下命令:

 Thread.currentThread().getContextClassLoader().getResourceAsStream( ... ) ; 

这也是在jar中包含默认配置文件的一种非常有用的技术。

ClassLoader.getResource(String)将加载它在类路径上找到的第一个清单,该清单可能是某些其他JAR文件的清单。 因此,您可以枚举所有清单以查找所需清单或使用其他机制,例如具有唯一名称的属性文件。

我发现McDowell的评论是真的 – 哪个MANIFEST.MF文件被拾取取决于类路径,可能不是想要的那个。 我用这个

 String cp = PCAS.class.getResource(PCAS.class.getSimpleName() + ".class").toString(); cp = cp.substring(0, cp.indexOf(PCAS.class.getPackage().getName())) + "META-INF/MANIFEST.MF"; Manifest mf = new Manifest((new URL(cp)).openStream()); 

我改编自链接文本

只是不要使用清单。 创建一个foo.properties.original文件,其内容如version = @ VERSION @

在同样的任务中,你正在盯着你可以复制到copu foo.properties.original然后

我通常也会使用版本文件。 我将为每个jar创建一个文件,因为每个jar都可以有自己的版本。

您可以使用jcabi-manifests中的实用程序类Manifests自动查找和解析类路径中可用的所有MANIFEST.MF文件。 然后,您使用单行读取任何属性:

 final String name = Manifests.read("Build-By"); final String date = Manifests.read("Build-Date"); 

另外,请查看: http : //www.yegor256.com/2014/07/03/how-to-read-manifest-mf.html