Как вы создаете MANIFEST.MF, который доступен, когда вы тестируете и запускаете из банки в производстве?
Я потратил слишком много времени, пытаясь понять это. Это должно быть самым простым, и все, кто распространяет Java-приложения в банках, должны иметь дело с ним.
Я просто хочу знать, как правильно добавить управление версиями в мое приложение Java, чтобы я мог получить доступ к информации о версии, когда я тестирую, например. отладка в Eclipse и с флага.
Вот что у меня в файле 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, он загружает манифест другой банки (предположительно, первую банку, загруженную приложением - в моем случае, activation.jar).
Кроме того, следующий код не работает, хотя все необходимые значения находятся в файле манифеста.
Package thisPackage = getClass().getPackage();
String implementationVersion = thisPackage.getImplementationVersion();
Любые идеи?
Ответы
Ответ 1
Вы можете получить манифест для произвольного класса в произвольном баночке без разбора URL-адреса класса (который может быть хрупким). Просто найдите ресурс, который, как вам известно, находится в банке, которую вы хотите, а затем откройте соединение с JarURLConnection.
Если вы хотите, чтобы код работал, когда класс не был связан в банке, добавьте проверку экземпляра типа возвращаемого URL-адреса. Классы в иерархии без распаковки возвращают внутреннее соединение 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);
}
}
Ответ 2
Вы хотите использовать это:
Enumeration<URL> resources = Thread.currentThread().getContextClassLoader().getResources("META-INF/MANIFEST.MF");
Вы можете проанализировать URL-адрес, чтобы выяснить, WHICH jar манифест, если из и затем прочитать URL-адрес через getInputStream(), чтобы проанализировать манифест.
Ответ 3
Вы можете получить доступ к манифесту (или любому другому) файлу в банке, если вы используете тот же загрузчик классов, что и для загрузки классов.
this.getClass().getClassLoader().getResourceAsStream( ... ) ;
Если вы используете многопоточность, используйте следующее:
Thread.currentThread().getContextClassLoader().getResourceAsStream( ... ) ;
Это также очень полезный метод для включения файла конфигурации по умолчанию в банке.
Ответ 4
Вот что я нашел, что работает:
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:
<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>
Ответ 5
ClassLoader.getResource(String) будет загружать первый манифест, который он находит в пути к классам, который может быть манифестом для какого-либо другого JAR файла, Таким образом, вы можете перечислить все манифесты, чтобы найти тот, который вы хотите, или использовать какой-либо другой механизм, такой как файл свойств с уникальным именем.
Ответ 6
Я нашел комментарий 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());
который я адаптировал из текст ссылки
Ответ 7
Просто не используйте манифест. Создайте файл foo.properties.original с таким содержимым, как
версия = @VERSION @
И в той же задаче, которую вы ставите, вы можете сделать копию copu foo.properties.original, а затем
Ответ 8
Я также обычно использую файл версии. Я создам один файл на банку, так как каждая банка может иметь свою собственную версию.
Ответ 9
Вы можете использовать служебный класс Manifests
из jcabi-manifestests, который автоматизирует поиск и анализ всех 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