如何创建在生产中测试和运行 jar 时可用的 MANIFEST.MF?
我花了太多时间试图解决这个问题。 这应该是最简单的事情,每个在 jar 中分发 Java 应用程序的人都必须处理它。
我只是想知道向 Java 应用程序添加版本控制的正确方法,以便在测试时可以访问版本信息,例如在 Eclipse 中调试和从 jar 运行。
这是我的 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>
这将创建 /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();
有任何想法吗?
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.
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 and running from a jar.
Here's what I have in my 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:
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).
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
这是我发现有效的:
packageVersion.java:
这是匹配的 build.xml:
Here's what I've found that works:
packageVersion.java:
Here's the matching build.xml:
您可以获取任意 jar 中任意类的清单,而无需解析类 url(这可能很脆弱)。 只需找到您知道的资源位于所需的 jar 中,然后将连接强制转换为 JarURLConnection。
如果您希望代码在类未捆绑在 jar 中时正常工作,请添加对返回的 URL 连接类型的 instanceof 检查。 解压的类层次结构中的类将返回内部 Sun FileURLConnection,而不是 JarUrlConnection。 然后,您可以使用其他答案中描述的 InputStream 方法之一加载清单。
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.
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.
ClassLoader.getResource (String) 将加载在类路径中找到的第一个清单,该清单可能是其他 JAR 文件的清单。 因此,您可以 枚举所有清单以查找您想要的清单或使用某种其他机制,例如具有唯一名称的属性文件。
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 manifests to find the one you want or use some other mechanism, such as a properties file with a unique name.
您想使用它:
您可以解析 URL 以找出清单来自哪个 jar,然后通过 getInputStream() 读取 URL 来解析清单。
You want to use this:
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.
我发现 McDowell 的评论是正确的 - 选择哪个 MANIFEST.MF 文件取决于类路径,并且可能不是想要的文件。 我使用这个
改编自 链接文本
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
which I adapted from link text
如果您使用与加载类相同的类加载器,则可以访问 jar 中的清单(或任何其他)文件。
如果您是多线程的,请使用以下命令:
这也是一种非常有用的技术,可以在 jar 中包含默认配置文件。
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.
If you are multi-threaded use the following:
This is also a realy useful technique for including a default configuration file within the jar.
您可以使用实用程序类
Manifests
来自 jcabi-manifests,它可以自动查找和解析所有MANIFEST。 MF
文件在类路径中可用。 然后,您可以用一行读取任何属性:另外,请检查一下:http://www.yegor256.com/2014/07/03/how-to-read-manifest-mf.html
You can use a utility class
Manifests
from jcabi-manifests that automates finding and parsing of allMANIFEST.MF
files available in classpath. Then, you read any attribute with a one liner:Also, check this out: http://www.yegor256.com/2014/07/03/how-to-read-manifest-mf.html
只是不要使用清单。 创建一个 foo.properties.original 文件,内容如下
version=@VERSION@
在您正在执行的同一任务中,您可以复制到 copu foo.properties.original,然后
Just don't use the manifest. Create a foo.properties.original file, with a content such as
version=@VERSION@
And in ther same task you are jaring you can do a copy to copu foo.properties.original and then
我通常也会使用版本文件。 我将为每个 jar 创建一个文件,因为每个 jar 都可以有自己的版本。
I will also usually use a version file. I will create one file per jar since each jar could have its own version.