How do you create a MANIFEST.MF that's available when you're testing and running from a jar in production?

前端 未结 9 1000
失恋的感觉
失恋的感觉 2020-12-28 16:28

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.

相关标签:
9条回答
  • 2020-12-28 17:21

    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.

    0 讨论(0)
  • 2020-12-28 17:22

    Here's what I've found that works:

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

    Here's the matching 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>
    
    0 讨论(0)
  • 2020-12-28 17:22

    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.

    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.

    0 讨论(0)
提交回复
热议问题