Java 您如何创建在生产中从 jar 进行测试和运行时可用的 MANIFEST.MF?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/84486/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do you create a MANIFEST.MF that's available when you're testing and running from a jar in production?
提问by user16216
I've spent far too much time trying to figure this out. This should be the simplest thing and everyone who distributes Java applications in jars must have to deal with it.
我花了太多时间试图弄清楚这一点。这应该是最简单的事情,每个在 jars 中分发 Java 应用程序的人都必须处理它。
I just want to know the proper way to add versioning to my Java app so that I can access the version information when I'm testing, e.g. debugging in Eclipse andrunning from a jar.
我只想知道将版本控制添加到我的 Java 应用程序的正确方法,以便我可以在测试时访问版本信息,例如在 Eclipse 中调试和从 jar 运行。
Here's what I have in my build.xml:
这是我在 build.xml 中的内容:
<target name="jar" depends = "compile">
<property name="version.num" value="1.0.0"/>
<buildnumber file="build.num"/>
<tstamp>
<format property="TODAY" pattern="yyyy-MM-dd HH:mm:ss" />
</tstamp>
<manifest file="${build}/META-INF/MANIFEST.MF">
<attribute name="Built-By" value="${user.name}" />
<attribute name="Built-Date" value="${TODAY}" />
<attribute name="Implementation-Title" value="MyApp" />
<attribute name="Implementation-Vendor" value="MyCompany" />
<attribute name="Implementation-Version" value="${version.num}-b${build.number}"/>
</manifest>
<jar destfile="${build}/myapp.jar" basedir="${build}" excludes="*.jar" />
</target>
This creates /META-INF/MANIFEST.MF and I can read the values when I'm debugging in Eclipse thusly:
这将创建 /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.");
}
}
}
But, when I create the jar file, it loads the manifest of another jar (presumably the first jar loaded by the application - in my case, activation.jar).
但是,当我创建 jar 文件时,它会加载另一个 jar 的清单(大概是应用程序加载的第一个 jar - 在我的例子中是 activation.jar)。
Also, the following code doesn't work either although all the proper values are in the manifest file.
此外,尽管所有正确的值都在清单文件中,但以下代码也不起作用。
Package thisPackage = getClass().getPackage();
String implementationVersion = thisPackage.getImplementationVersion();
Any ideas?
有任何想法吗?
回答by Juan Pablo Morales
Just don't use the manifest. Create a foo.properties.original file, with a content such as version=@VERSION@
只是不要使用清单。创建一个 foo.properties.original 文件,内容如 version=@VERSION@
And in ther same task you are jaring you can do a copy to copu foo.properties.original and then
在相同的任务中,您可以将副本复制到 copu foo.properties.original 然后
回答by Javamann
I will also usually use a version file. I will create one file per jar since each jar could have its own version.
我通常也会使用版本文件。我将为每个 jar 创建一个文件,因为每个 jar 都可以有自己的版本。
回答by Martin Spamer
You can access the manifest (or any other) file within a jar if you use the same class loader to as was used to load the classes.
如果您使用与加载类相同的类加载器,则可以访问 jar 中的清单(或任何其他)文件。
this.getClass().getClassLoader().getResourceAsStream( ... ) ;
If you are multi-threaded use the following:
如果您是多线程,请使用以下内容:
Thread.currentThread().getContextClassLoader().getResourceAsStream( ... ) ;
This is also a realy useful technique for including a default configuration file within the jar.
这也是在 jar 中包含默认配置文件的非常有用的技术。
回答by basszero
You want to use this:
你想使用这个:
Enumeration<URL> resources = Thread.currentThread().getContextClassLoader().getResources("META-INF/MANIFEST.MF");
You can parse the URL to figure out WHICH jar the manifest if from and then read the URL via getInputStream() to parse the manifest.
您可以解析 URL 以找出清单中的哪个 jar,然后通过 getInputStream() 读取 URL 以解析清单。
回答by user16216
Here's what I've found that works:
这是我发现有效的方法:
packageVersion.java:
包版本.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();
}
}
Here's the matching build.xml:
这是匹配的 build.xml:
<project name="packageVersion" default="run" basedir=".">
<property name="src" location="src"/>
<property name="build" location="bin"/>
<property name="dist" location="dist"/>
<target name="init">
<tstamp>
<format property="TIMESTAMP" pattern="yyyy-MM-dd HH:mm:ss" />
</tstamp>
<mkdir dir="${build}"/>
<mkdir dir="${build}/META-INF"/>
</target>
<target name="compile" depends="init">
<javac debug="on" srcdir="${src}" destdir="${build}"/>
</target>
<target name="dist" depends = "compile">
<mkdir dir="${dist}"/>
<property name="version.num" value="1.0.0"/>
<buildnumber file="build.num"/>
<manifest file="${build}/META-INF/MANIFEST.MF">
<attribute name="Built-By" value="${user.name}" />
<attribute name="Built-Date" value="${TIMESTAMP}" />
<attribute name="Implementation-Vendor" value="Company" />
<attribute name="Implementation-Title" value="PackageVersion" />
<attribute name="Implementation-Version" value="${version.num} (b${build.number})"/>
<section name="com/company/division/project/packageversion">
<attribute name="Sealed" value="false"/>
</section>
</manifest>
<jar destfile="${dist}/packageversion-${version.num}.jar" basedir="${build}" manifest="${build}/META-INF/MANIFEST.MF"/>
</target>
<target name="clean">
<delete dir="${build}"/>
<delete dir="${dist}"/>
</target>
<target name="run" depends="dist">
<java classname="com.company.division.project.packageversion.packageVersion">
<arg value="-h"/>
<classpath>
<pathelement location="${dist}/packageversion-${version.num}.jar"/>
<pathelement path="${java.class.path}"/>
</classpath>
</java>
</target>
</project>
回答by McDowell
ClassLoader.getResource(String)will load the first manifest it finds on the classpath, which may be the manifest for some other JAR file. Thus, you can either enumerate all the manifeststo find the one you want or use some other mechanism, such as a properties file with a unique name.
ClassLoader.getResource(String)将加载它在类路径上找到的第一个清单,它可能是其他一些 JAR 文件的清单。因此,您可以枚举所有清单以找到您想要的清单,或者使用某种其他机制,例如具有唯一名称的属性文件。
回答by Glenn Burkhardt
I've found the comment by McDowell to be true - which MANIFEST.MF file gets picked up depends on the classpath and might not be the one wanted. I use this
我发现 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());
which I adapted from link text
我改编自链接文本
回答by gibbss
You can get the manifest for an arbitrary class in an arbitrary jar without parsing the class url (which could be brittle). Just locate a resource that you know is in the jar you want, and then cast the connection to JarURLConnection.
您可以获取任意 jar 中任意类的清单,而无需解析类 url(这可能很脆弱)。只需在所需的 jar 中找到您知道的资源,然后将连接转换为 JarURLConnection。
If you want the code to work when the class is not bundled in a jar, add an instanceof check on the type of URL connection returned. Classes in an unpacked class hierarchy will return a internal Sun FileURLConnection instead of the JarUrlConnection. Then you can load the Manifest using one of the InputStream methods described in other answers.
如果您希望代码在类未捆绑在 jar 中时工作,请添加对返回的 URL 连接类型的 instanceof 检查。解压缩的类层次结构中的类将返回内部 Sun FileURLConnection 而不是 JarUrlConnection。然后,您可以使用其他答案中描述的 InputStream 方法之一加载清单。
@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);
}
}
回答by yegor256
You can use a utility class Manifests
from jcabi-manifeststhat automates finding and parsing of all MANIFEST.MF
files available in classpath. Then, you read any attribute with a one liner:
您可以使用jcabi-manifests中的实用程序类Manifests
,它可以自动查找和解析MANIFEST.MF
类路径中可用的所有文件。然后,您使用单行读取任何属性:
final String name = Manifests.read("Build-By");
final String date = Manifests.read("Build-Date");
Also, check this out: http://www.yegor256.com/2014/07/03/how-to-read-manifest-mf.html
另外,看看这个:http: //www.yegor256.com/2014/07/03/how-to-read-manifest-mf.html