Nhận phiên bản tạo tác Maven khi chạy


176

Tôi đã nhận thấy rằng trong JAR của tạo tác Maven, thuộc tính project.version được bao gồm trong hai tệp:

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

Có cách nào để đọc phiên bản này trong thời gian chạy không?


Câu trả lời:


265

Bạn không cần phải truy cập các tệp dành riêng cho Maven để lấy thông tin phiên bản của bất kỳ thư viện / lớp cụ thể nào.

Bạn chỉ có thể sử dụng getClass().getPackage().getImplementationVersion()để lấy thông tin phiên bản được lưu trữ trong tệp .jar MANIFEST.MF. May mắn thay, Maven đủ thông minh Thật không may Maven không viết thông tin chính xác vào bảng kê khai theo mặc định!

Thay vào đó, người ta phải sửa đổi thành <archive>phần cấu hình của maven-jar-pluginthiết lập addDefaultImplementationEntriesaddDefaultSpecificationEntriesthành true, như thế này:

<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>

Lý tưởng nhất là cấu hình này nên được đưa vào công ty pomhoặc một cơ sở khác.

Tài liệu chi tiết về <archive>yếu tố này có thể được tìm thấy trong tài liệu Lưu trữ Maven .


6
thật đáng buồn, không phải mọi trình nạp lớp dường như tải các thuộc tính này từ tệp kê khai (tôi nhớ có vấn đề với Tomcat trong trường hợp này chính xác).
bộ

@avithan: thật sao? Tôi chưa bao giờ gặp vấn đề với Tomcat với cách tiếp cận này. Ngoài ra, tôi nghĩ rằng một trình nạp lớp mà bỏ qua bảng kê khai có lẽ không phù hợp.
Joachim Sauer

@JoachimSauer ok, tôi đã nhầm. Hiện tại có vẻ như nó hoạt động rất tốt trên HotSpot nhưng không hoạt động đáng tin cậy trên OpenJDK. Tôi sẽ báo cáo lại khi tôi nhận được thông tin chi tiết
16:30

@avithan điều này có liên quan đến tôi (và tôi chưa thấy những gì bạn báo cáo) - bạn đã nhận được thông tin chi tiết chưa?
Thorbjørn Ravn Andersen

4
Thật không may, điều này không hoạt động nếu dự án được chạy từ Eclipse hoặc sử dụng "mvn exec: java".
Jaan

77

Để theo dõi câu trả lời ở trên, đối với một .warvật phẩm, tôi thấy rằng tôi phải áp dụng cấu hình tương đương maven-war-plugin, thay vì 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>

Đây thêm thông tin phiên bản để MANIFEST.MFin của dự án .jar(bao gồm trong WEB-INF/libcác .war)


3
<archiveClass> true </ archiveClass> gây ra lỗi trong trường hợp của tôi. Nhưng vấn đề đã được giải quyết stackoverflow.com/questions/14934299/ Cách
Paul Verest

10
Khi tôi thử điều này thì kết quả của tôi luôn luôn nullmặc dù MANIFEST.MF trong các tệp tin chiến tranh chứa thông tin chính xác.
thomas.mc.work

Tôi cũng cần thêm nó vào maven-assembly-plugin
acheron55

2
<archiveClass> true </ archiveClass> dường như không liên quan
Karl Kildén

1
@RafaelSimonelli Tôi đã xóa <archiveClasses>true</archiveClasses>- và nó hoạt động đáng tin cậy kể từ đó.
thomas.mc.work

28

Đây là một phương pháp để có được phiên bản từ pom.properies, quay trở lại để lấy nó từ tệp kê khai

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
Đặt cái này trong một khối khởi tạo tĩnh.
opyate

1
Lời khuyên tốt. Mặc dù, nếu bạn đang sử dụng điều này trong một servlet (hoặc .jsp), hãy chắc chắn sử dụng getServletContext (). GetResourceAsStream thay vì getClass (). GetResourceAsStream
Sandman

3
Điều này chỉ hoạt động khi ứng dụng được chạy từ jar. Nếu chạy từ exec-maven-plugin (ví dụ Netbeans), tài nguyên là null.
Leif Gruenwoldt

Mã này sẽ là một phần của mặc định lớp chính của tôi! Cảm ơn!!
Wendel

Tôi đã sử dụng điều này với câu trả lời của Will cho một lựa chọn thẳng và dễ bảo trì.
javydreamercsw

3

Tôi đã dành một chút thời gian cho hai cách tiếp cận chính ở đây và chúng không phù hợp với tôi. Tôi đang sử dụng Netbeans cho các bản dựng, có thể sẽ còn nhiều hơn nữa. Tôi đã có một số lỗi và cảnh báo từ Maven 3 với một số cấu trúc, nhưng tôi nghĩ chúng rất dễ sửa. Không có vấn đề gì

Tôi đã tìm thấy một câu trả lời có vẻ dễ duy trì và đơn giản để thực hiện trong bài viết này trên DZone:

Tôi đã có một thư mục con resource / config và tôi đặt tên cho tệp của mình: app.properies, để phản ánh tốt hơn các loại nội dung chúng tôi có thể giữ ở đó (như URL hỗ trợ, v.v.).

Nhắc nhở duy nhất là Netbeans đưa ra cảnh báo rằng IDE cần lọc bỏ. Không chắc chắn ở đâu / như thế nào. Nó không có tác dụng vào thời điểm này. Có lẽ có một công việc xung quanh cho điều đó nếu tôi cần đi qua cây cầu đó. May mắn nhất.


3

Tôi đang sử dụng maven-assembly-plugincho bao bì maven của tôi. Việc sử dụng Apache Maven Archiver trong câu trả lời của Joachimerer cũng có thể hoạt động:

<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>

Bởi vì archiever là một trong những thành phần được chia sẻ của maven , nên nó có thể được sử dụng bởi nhiều plugin xây dựng maven, cũng có thể có xung đột nếu hai hoặc nhiều plugin được giới thiệu, bao gồm cả archivecấu hình bên trong.


2

Để chạy cái này trong Eclipse, cũng như trong bản dựng Maven, bạn nên thêm các mục addDefaultImplementationEntriesaddDefaultSpecificationEntriespom như được mô tả trong các câu trả lời khác, sau đó sử dụng mã sau:

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

Nếu bản dựng Java của bạn đặt các lớp mục tiêu ở đâu đó ngoài "mục tiêu / lớp", thì bạn có thể cần điều chỉnh giá trị của SegToRemove.


Bạn biết nếu điều này là cho các bài kiểm tra đơn vị bạn có thể chỉ System.getProperty("user.dir")/pom.xml. Tôi khá chắc chắn rằng nó cũng sẽ cho những thứ khác, ngoại trừ có thể không phải cho WTP.
Adam Gent

Điều đó sẽ chỉ hoạt động nếu dự án của bạn nằm trong một thư mục - nếu bạn đang chạy một dự án dựa trên jarfiles, giải pháp của bạn sẽ không hoạt động. Bạn cần sử dụng .getResource()hoặc .getResourceAsStream().
Luke Hutchison

Có, tôi đã giả sử rằng bạn đã kiểm tra jar (ala getResource). Đó là lần đầu tiên bạn kiểm tra với getResource nếu thất bại thì dự án chưa được tích hợp vào một jar, điều đó có nghĩa là bạn đang chạy nó từ Eclipse hoặc Maven, có nghĩa là `System.getProperty (" user.dir ") / pom.xml . Vấn đề duy nhất là tệp pom này không phải là pom hiệu quả thực sự (đó là một số thuộc tính sẽ không được mở rộng) nhưng cũng không phải là tệp bạn đang sử dụng theo cách của Eclipse.
Adam Gent

1

Trên ứng dụng khởi động mùa xuân của tôi, giải pháp từ câu trả lời được chấp nhận đã hoạt động cho đến khi gần đây tôi cập nhật jdk của mình lên phiên bản 12. Đã thử tất cả các câu trả lời khác và không thể làm cho nó hoạt động.

Vào thời điểm đó, tôi đã thêm dòng dưới đây vào lớp đầu tiên của ứng dụng khởi động mùa xuân của mình, ngay sau chú thích @SpringBootApplication

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

Sau này tôi sử dụng phần bên dưới để lấy giá trị từ tệp thuộc tính trong bất kỳ lớp nào tôi muốn sử dụng giá trị của nó và appVersionnhận phiên bản dự án cho tôi:

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

Hy vọng rằng sẽ giúp được ai đó.


Làm thế nào để làm tương tự với nhiều tập tin pom? Tôi muốn tải phiên bản từ nhiều tập tin pom.
THM

0

Một giải pháp đơn giản tương thích với Maven và hoạt động cho bất kỳ lớp nào (cũng như bên thứ ba):

    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

Biến thể Java 8 cho EJB trong tệp tin chiến tranh với dự án maven. Đã thử nghiệm trên 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!");
            }
        });
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.