รับรุ่น Maven ของสิ่งประดิษฐ์ขณะใช้งานจริง


177

ฉันสังเกตเห็นว่าใน JAR ของ Mavenifact คุณลักษณะ project.version จะรวมอยู่ในสองไฟล์:

META-INF/maven/${groupId}/${artifactId}/pom.properties
META-INF/maven/${groupId}/${artifactId}/pom.xml

มีวิธีแนะนำให้อ่านเวอร์ชั่นนี้ตอนรันไทม์หรือไม่?


คำตอบ:


265

คุณไม่จำเป็นต้องเข้าถึงไฟล์เฉพาะ Maven เพื่อรับข้อมูลรุ่นของไลบรารี / คลาสที่กำหนด

คุณก็สามารถใช้เพื่อให้ได้ข้อมูลรุ่นที่ถูกเก็บไว้ในขวดไฟล์getClass().getPackage().getImplementationVersion() โชคดีที่ Maven ฉลาดพอน่าเสียดายที่ Maven ไม่ได้เขียนข้อมูลที่ถูกต้องลงในรายการด้วยเช่นกัน!MANIFEST.MF

แต่จะต้องแก้ไข<archive>องค์ประกอบการกำหนดค่าของการmaven-jar-pluginตั้งค่าaddDefaultImplementationEntriesและaddDefaultSpecificationEntriesไปtrueเป็นดังนี้

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
        <archive>                   
            <manifest>
                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
            </manifest>
        </archive>
    </configuration>
</plugin>

เป็นการดีที่การกำหนดค่านี้ควรใส่ใน บริษัทpomหรือฐานปอมอื่น

เอกสารรายละเอียดของ<archive>องค์ประกอบที่สามารถพบได้ในเอกสาร Maven เอกสารเก่า


6
น่าเสียดายที่ตัวโหลดคลาสทุกตัวดูเหมือนจะโหลดคุณสมบัติเหล่านี้จากไฟล์รายการ (ฉันจำได้ว่ามีปัญหากับ Tomcat ในกรณีนี้อย่างแน่นอน)
dwegener

@avithan: จริงเหรอ? ฉันไม่เคยมีปัญหากับ Tomcat ด้วยวิธีนี้ นอกจากนี้ฉันคิดว่าตัวโหลดคลาสที่ละเว้นรายการอาจไม่สอดคล้องกัน
โจอาคิมซาวเออร์

@JoachimSauer ตกลงฉันผิด ปัจจุบันดูเหมือนว่าจะใช้งานได้ดีกับ HotSpot แต่ไม่สามารถใช้งานได้กับ OpenJDK ฉันจะรายงานกลับเมื่อฉันได้รับข้อมูลโดยละเอียด
dwegener

@avithan สิ่งนี้เกี่ยวข้องกับฉัน (และฉันไม่เห็นสิ่งที่คุณรายงาน) - คุณได้รับข้อมูลรายละเอียดแล้วหรือยัง
Thorbjørn Ravn Andersen

4
น่าเสียดายที่นี่ไม่สามารถใช้งานได้หากโครงการรันจาก Eclipse หรือใช้ "mvn exec: java"
Jaan

77

ในการติดตามคำตอบข้างต้นสำหรับ.warสิ่งประดิษฐ์ฉันพบว่าฉันต้องใช้การกำหนดค่าที่เทียบเท่ากับmaven-war-pluginแทนmaven-jar-plugin:

<plugin>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.1</version>
    <configuration>
        <archive>                   
            <manifest>
                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
            </manifest>
        </archive>
    </configuration>
</plugin>

นี้เพิ่มข้อมูลรุ่นไปMANIFEST.MFในโครงการ.jar(รวมอยู่ในWEB-INF/libของ.war)


3
<archiveClasses> จริง </archiveClasses> ทำให้เกิดข้อผิดพลาดในกรณีของฉัน แต่ปัญหาได้รับการแก้ไขแล้วstackoverflow.com/questions/14934299/ …
Paul Verest

10
เมื่อฉันลองทำสิ่งนี้ผลลัพธ์ของฉันจะเป็นเสมอnullแม้ว่า MANIFEST.MF ในไฟล์ war จะมีข้อมูลที่ถูกต้อง
thomas.mc.work

ฉันยังต้องการที่จะเพิ่มลงใน maven-assembly-plugin
acheron55

2
<archiveClasses> จริง </archiveClasses> ดูเหมือนไม่เกี่ยวข้องกัน
Karl Kildén

1
@RafaelSimonelli ฉันลบไปแล้ว<archiveClasses>true</archiveClasses>- และมันก็ทำงานได้อย่างน่าเชื่อถือตั้งแต่นั้นมา
thomas.mc.work

28

ต่อไปนี้เป็นวิธีการรับรุ่นจาก pom.properties ย้อนกลับไปรับจากรายการ

public synchronized String getVersion() {
    String version = null;

    // try to load from maven properties first
    try {
        Properties p = new Properties();
        InputStream is = getClass().getResourceAsStream("/META-INF/maven/com.my.group/my-artefact/pom.properties");
        if (is != null) {
            p.load(is);
            version = p.getProperty("version", "");
        }
    } catch (Exception e) {
        // ignore
    }

    // fallback to using Java API
    if (version == null) {
        Package aPackage = getClass().getPackage();
        if (aPackage != null) {
            version = aPackage.getImplementationVersion();
            if (version == null) {
                version = aPackage.getSpecificationVersion();
            }
        }
    }

    if (version == null) {
        // we could not compute the version so use a blank
        version = "";
    }

    return version;
} 

2
ใส่สิ่งนี้ในบล็อก initializer คงที่
opyate

1
คำปรึกษาที่ดี. แม้ว่าถ้าคุณใช้สิ่งนี้ใน servlet (หรือ. jsp) ให้ใช้ getServletContext (). getResourceAsStream แทน getClass (). getResourceAsStream
Sandman

3
ใช้ได้เฉพาะเมื่อเรียกใช้แอปพลิเคชันจาก jar หากเรียกใช้จาก exec-maven-plugin (เช่น Netbeans) ทรัพยากรจะเป็นโมฆะ
Leif Gruenwoldt

รหัสนี้จะเป็นส่วนหนึ่งของค่าเริ่มต้นชั้นเรียนของฉัน! ขอบคุณ !!
เวนเดล

ฉันใช้สิ่งนี้กับคำตอบของ Will สำหรับตัวเลือกที่ตรงไปตรงมาและรักษาง่าย
javydreamercsw

3

ฉันใช้เวลากับสองวิธีหลักที่นี่และพวกเขาไม่ได้ผลสำหรับฉัน ฉันใช้ Netbeans สำหรับงานสร้างอาจเป็นไปได้มากขึ้น ฉันมีข้อผิดพลาดและคำเตือนจาก Maven 3 ที่มีโครงสร้างบางอย่าง แต่ฉันคิดว่าสิ่งเหล่านั้นง่ายต่อการแก้ไข ไม่มีปัญหา

ฉันหาคำตอบที่ดูบำรุงรักษาง่ายในการใช้ในบทความนี้ใน DZone:

ฉันมีโฟลเดอร์ย่อย resources / config แล้วและฉันตั้งชื่อไฟล์ของฉัน: app.properties เพื่อให้สะท้อนถึงประเภทของสิ่งที่เราอาจเก็บไว้ที่นั่น (เช่น URL สนับสนุนและอื่น ๆ )

ข้อแม้เดียวคือ Netbeans ให้คำเตือนว่า IDE ต้องการกรองออก ไม่แน่ใจว่าอยู่ที่ไหน / อย่างไร ไม่มีผล ณ จุดนี้ อาจมีวิธีแก้ไขหากฉันต้องข้ามสะพานนั่น ขอให้โชคดี


3

ฉันกำลังใช้maven-assembly-pluginสำหรับบรรจุภัณฑ์ Maven ของฉัน การใช้Apache Maven Archiverในคำตอบของ Joachim Sauerสามารถใช้งานได้:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <configuration>
        <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
        <archive>
            <manifest>
                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
            </manifest>
        </archive>
    </configuration>
    <executions>
        <execution .../>
    </executions>
</plugin>

เนื่องจาก archiever เป็นหนึ่งในองค์ประกอบที่ใช้ร่วมกันของmavenมันอาจถูกใช้โดยปลั๊กอินการสร้าง maven จำนวนมากซึ่งอาจมีความขัดแย้งได้หากมีปลั๊กอินที่แนะนำตั้งแต่สองรายการขึ้นไปรวมถึงarchiveการกำหนดค่าภายใน


2

ในการทำให้สิ่งนี้รันใน Eclipse เช่นเดียวกับใน Maven build คุณควรเพิ่มรายการaddDefaultImplementationEntriesและaddDefaultSpecificationEntriespom ตามที่อธิบายไว้ในคำตอบอื่น ๆ จากนั้นใช้รหัสต่อไปนี้:

public synchronized static final String getVersion() {
    // Try to get version number from pom.xml (available in Eclipse)
    try {
        String className = getClass().getName();
        String classfileName = "/" + className.replace('.', '/') + ".class";
        URL classfileResource = getClass().getResource(classfileName);
        if (classfileResource != null) {
            Path absolutePackagePath = Paths.get(classfileResource.toURI())
                    .getParent();
            int packagePathSegments = className.length()
                    - className.replace(".", "").length();
            // Remove package segments from path, plus two more levels
            // for "target/classes", which is the standard location for
            // classes in Eclipse.
            Path path = absolutePackagePath;
            for (int i = 0, segmentsToRemove = packagePathSegments + 2;
                    i < segmentsToRemove; i++) {
                path = path.getParent();
            }
            Path pom = path.resolve("pom.xml");
            try (InputStream is = Files.newInputStream(pom)) {
                Document doc = DocumentBuilderFactory.newInstance()
                        .newDocumentBuilder().parse(is);
                doc.getDocumentElement().normalize();
                String version = (String) XPathFactory.newInstance()
                        .newXPath().compile("/project/version")
                        .evaluate(doc, XPathConstants.STRING);
                if (version != null) {
                    version = version.trim();
                    if (!version.isEmpty()) {
                        return version;
                    }
                }
            }
        }
    } catch (Exception e) {
        // Ignore
    }

    // Try to get version number from maven properties in jar's META-INF
    try (InputStream is = getClass()
        .getResourceAsStream("/META-INF/maven/" + MAVEN_PACKAGE + "/"
                + MAVEN_ARTIFACT + "/pom.properties")) {
        if (is != null) {
            Properties p = new Properties();
            p.load(is);
            String version = p.getProperty("version", "").trim();
            if (!version.isEmpty()) {
                return version;
            }
        }
    } catch (Exception e) {
        // Ignore
    }

    // Fallback to using Java API to get version from MANIFEST.MF
    String version = null;
    Package pkg = getClass().getPackage();
    if (pkg != null) {
        version = pkg.getImplementationVersion();
        if (version == null) {
            version = pkg.getSpecificationVersion();
        }
    }
    version = version == null ? "" : version.trim();
    return version.isEmpty() ? "unknown" : version;
}

หาก Java build ของคุณวางคลาสเป้าหมายไว้ที่อื่นที่ไม่ใช่ "target / classes" คุณอาจต้องปรับค่าของ segmentToRemove


System.getProperty("user.dir")/pom.xmlคุณจะรู้ว่าถ้าเป็นสำหรับการทดสอบหน่วยที่คุณสามารถเพียงแค่ ฉันค่อนข้างแน่ใจว่ามันจะเป็นอย่างอื่นเช่นกันยกเว้นอาจจะไม่ใช่ WTP
Adam Gent

สิ่งนี้จะใช้ได้ก็ต่อเมื่อโปรเจ็กต์ของคุณอยู่ในไดเรกทอรี - ถ้าคุณกำลังรันโปรเจ็กต์ด้วย jarfiles โซลูชันของคุณจะไม่ทำงาน คุณจำเป็นต้องใช้หรือ.getResource() .getResourceAsStream()
ลุคฮัทชิสัน

ใช่ฉันคิดว่าคุณได้ตรวจสอบ jar (ala getResource) แล้ว นั่นเป็นครั้งแรกที่คุณตรวจสอบกับ getResource หากล้มเหลวโครงการนั้นยังไม่ได้สร้างขึ้นในขวดซึ่งหมายความว่าคุณกำลังเรียกใช้จาก Eclipse หรือ Maven ซึ่งหมายถึง `System.getProperty (" user.dir ") / pom.xml . ปัญหาเดียวคือไฟล์ pom นี้ไม่ใช่ pom ที่มีประสิทธิภาพจริง (นั่นคือคุณสมบัติบางอย่างจะไม่ถูกขยาย) แต่ก็ไม่มีทั้งที่คุณได้รับด้วยวิธี Eclipse
Adam Gent

1

ในแอพพลิเคชั่นบู๊ทสปริงของฉันการแก้ปัญหาจากคำตอบที่ยอมรับนั้นใช้ได้จนกระทั่งฉันเพิ่งอัปเดต jdk ของฉันไปเป็นเวอร์ชั่น 12 ลองคำตอบอื่น ๆ ทั้งหมดด้วยและไม่สามารถใช้งานได้

ณ จุดนั้นฉันเพิ่มบรรทัดด้านล่างลงในคลาสแรกของแอปพลิเคชันการบูทสปริงของฉันหลังจากการเพิ่มความคิดเห็น @SpringBootApplication

@PropertySources({ 
        @PropertySource("/META-INF/maven/com.my.group/my-artefact/pom.properties")
})

หลังจากนั้นฉันใช้ด้านล่างเพื่อรับค่าจากไฟล์คุณสมบัติในคลาสใดก็ตามที่ฉันต้องการใช้ค่าของมันและappVersionรับเวอร์ชันโปรเจ็กต์มาให้ฉัน:

@Value("${version}")
private String appVersion;

หวังว่าจะช่วยใครซักคน


จะทำเช่นเดียวกันกับไฟล์ pom หลายไฟล์ได้อย่างไร? ฉันต้องการโหลดเวอร์ชันจากไฟล์ pom หลายไฟล์
THM

0

ทางออกที่ง่ายซึ่งรองรับ Maven และใช้ได้กับคลาส (เช่นบุคคลที่สาม):

    private static Optional<String> getVersionFromManifest(Class<?> clazz) {
        try {
            File file = new File(clazz.getProtectionDomain().getCodeSource().getLocation().toURI());
            if (file.isFile()) {
                JarFile jarFile = new JarFile(file);
                Manifest manifest = jarFile.getManifest();
                Attributes attributes = manifest.getMainAttributes();
                final String version = attributes.getValue("Bundle-Version");
                return Optional.of(version);
            }
        } catch (Exception e) {
            // ignore
        }
        return Optional.empty();
    }

-1

ตัวแปร Java 8 สำหรับ EJB ในไฟล์ war ที่มีโปรเจ็กต์ maven ทดสอบกับ EAP 7.0

@Log4j // lombok annotation
@Startup
@Singleton
public class ApplicationLogic {

    public static final String DEVELOPMENT_APPLICATION_NAME = "application";

    public static final String DEVELOPMENT_GROUP_NAME = "com.group";

    private static final String POM_PROPERTIES_LOCATION = "/META-INF/maven/" + DEVELOPMENT_GROUP_NAME + "/" + DEVELOPMENT_APPLICATION_NAME + "/pom.properties";

    // In case no pom.properties file was generated or wrong location is configured, no pom.properties loading is done; otherwise VERSION will be assigned later
    public static String VERSION = "No pom.properties file present in folder " + POM_PROPERTIES_LOCATION;

    private static final String VERSION_ERROR = "Version could not be determinated";

    {    
        Optional.ofNullable(getClass().getResourceAsStream(POM_PROPERTIES_LOCATION)).ifPresent(p -> {

            Properties properties = new Properties();

            try {

                properties.load(p);

                VERSION = properties.getProperty("version", VERSION_ERROR);

            } catch (Exception e) {

                VERSION = VERSION_ERROR;

                log.fatal("Unexpected error occured during loading process of pom.properties file in META-INF folder!");
            }
        });
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.