20

私はこれを理解しようと非常に多くの時間を費やしてきました。これは最も単純なことであり、Javaアプリケーションをjarで配布するすべての人がそれに対処する必要があります。

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();

何か案は?

4

9 に答える 9

11

クラスの URL を解析せずに、任意の jar 内の任意のクラスのマニフェストを取得できます (これは脆弱である可能性があります)。必要な jar 内にあることがわかっているリソースを見つけて、接続を JarURLConnection にキャストするだけです。

クラスが jar にバンドルされていないときにコードが機能するようにするには、返される URL 接続のタイプに instanceof チェックを追加します。アンパックされたクラス階層のクラスは、JarUrlConnection ではなく、内部の Sun FileURLConnection を返します。次に、他の回答で説明されている 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);
    }
}
于 2010-06-18T16:55:19.920 に答える
2

これを使用したい:

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

URL を解析してマニフェストの場合はどの jar かを特定し、getInputStream() を介して URL を読み取ってマニフェストを解析できます。

于 2008-09-18T16:44:23.460 に答える
2

ClassLoader.getResource(String)は、クラスパスで最初に見つかったマニフェストをロードします。これは、他の JAR ファイルのマニフェストである可能性があります。したがって、すべてのマニフェストを列挙して必要なものを見つけるか、一意の名前を持つプロパティ ファイルなどの他のメカニズムを使用することができます。

于 2009-03-05T16:06:04.760 に答える
1

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());

リンクテキストから適応させたもの

于 2010-06-18T11:30:49.177 に答える
1

クラスのロードに使用したものと同じクラスローダーを使用すると、jar 内のマニフェスト (またはその他の) ファイルにアクセスできます。

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

マルチスレッドの場合は、次を使用します。

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

これは、jar 内にデフォルトの構成ファイルを含めるための非常に便利な手法でもあります。

于 2008-09-17T16:17:40.897 に答える
1

これが私が見つけたものです:

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>
于 2008-09-29T16:28:17.507 に答える
0

私も通常バージョンファイルを使用します。各jarには独自のバージョンがある可能性があるため、jarごとに1つのファイルを作成します。

于 2008-09-17T16:11:22.767 に答える
0

マニフェストを使用しないでください。version=@VERSION@ のような内容で foo.properties.original ファイルを作成します。

そして、あなたがやっている同じタスクで、copu foo.properties.original にコピーしてから

于 2008-09-17T15:38:23.690 に答える
0

クラスパスで利用可能なすべてのファイルの検索と解析を自動化する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

于 2012-12-30T09:02:05.250 に答える