런타임에 Maven 아티팩트 버전 가져 오기


177

Maven 아티팩트의 JAR에서 project.version 속성이 두 파일에 포함되어 있음을 알았습니다.

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

런타임에이 버전을 읽는 권장 방법이 있습니까?


답변:


265

특정 라이브러리 / 클래스의 버전 정보를 얻기 위해 Maven 특정 파일에 액세스 할 필요는 없습니다.

getClass().getPackage().getImplementationVersion().jar-files에 저장된 버전 정보를 얻는 데 사용할 수 있습니다 MANIFEST.MF. 운 좋게도 Maven은 충분히 영리합니다 불행하게도 Maven은 기본적으로 매니페스트에 올바른 정보를 쓰지 않습니다!

대신 하나는 수정이 <archive>의 구성 요소 maven-jar-plugin세트 addDefaultImplementationEntriesaddDefaultSpecificationEntriestrue이 같은 :

<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 Archive 설명서를 참조하십시오 .


6
슬프게도 모든 클래스 로더가 매니페스트 파일에서 이러한 속성을로드하는 것은 아닙니다 (정확히이 경우 Tomcat에 문제가 있음을 기억합니다).
dwegener

@avithan : 정말로 요? 이 방법으로 Tomcat에 문제가 없었습니다. 또한 매니페스트를 무시하는 클래스 로더가 적합하지 않은 것 같습니다.
Joachim Sauer

@JoachimSauer 좋아, 내가 틀렸다. 현재 HotSpot에서는 훌륭하게 작동하지만 OpenJDK에서는 안정적으로 작동하지 않습니다. 자세한 정보를 얻으면 다시 알려
드리겠습니다

@avithan 이것은 나와 관련이 있습니다 (보고 한 것을 보지 못했습니다). 아직 자세한 정보를 얻었습니까?
Thorbjørn Ravn Andersen

4
불행히도 프로젝트가 Eclipse에서 실행되거나 "mvn exec : java"를 사용하는 경우 작동하지 않습니다.
Jaan

77

A에 대한 위의 대답을 따르 .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> true </ archiveClasses>에서 오류가 발생했습니다. 그러나 문제는 해결되었다 stackoverflow.com/questions/14934299/...을
폴 Verest

10
내가 이것을 시도 null하면 war 파일의 MANIFEST.MF에 올바른 정보가 포함되어 있지만 결과는 항상 있습니다.
thomas.mc.work

또한 받는다는 - 조립 플러그인에 추가 할 필요
acheron55

2
<archiveClasses> true </ 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
이것을 정적 초기화 블록에 넣으십시오.
opyate

1
좋은 조언. 서블릿 (또는 .jsp)에서 이것을 사용하는 경우 getClass (). getResourceAsStream 대신 getServletContext (). getResourceAsStream을 사용해야합니다.
Sandman

3
jar에서 응용 프로그램을 실행할 때만 작동합니다. exec-maven-plugin (예 : Netbeans)에서 실행하면 리소스가 null입니다.
Leif Gruenwoldt

이 코드는 내 기본 클래스 기본값의 일부가 될 것입니다! 감사!!
Wendel

나는 이것을 똑바로 유지하기 쉬운 윌의 대답과 함께 사용했습니다.
javydreamercsw 2013 년

3

나는 여기에 두 가지 주요 접근 방식에 시간을 보냈지 만 그들은 나를 위해 운동하지 않았습니다. 빌드에 Netbeans를 사용하고 있으며 더 많은 작업이있을 수 있습니다. Maven 3의 일부 오류와 경고가 있지만 일부는 쉽게 수정할 수 있다고 생각합니다. 더 큰.

DZone 의이 기사에서 유지 관리 가능하고 구현하기 쉬운 답변을 찾았습니다.

이미 resources / config 하위 폴더를 가지고 있고 지원 URL과 같이 유지할 수있는 것들을 더 잘 반영하기 위해 app.properties라는 파일 이름을 지정했습니다.

유일한주의 사항은 Netbeans가 IDE에서 필터링을 해제해야한다는 경고를 표시한다는 것입니다. 어디서 / 어떻게 확실하지 않습니다. 이 시점에서는 효과가 없습니다. 다리를 건너야 할 경우 해결 방법이있을 수 있습니다. 행운을 빕니다.


3

maven-assembly-plugin내 메이븐 포장에 사용 하고 있습니다. Joachim Sauer의 답변 에서 Apache Maven Archiver 를 사용하면 다음과 같이 작동 할 수 있습니다.

<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 building plugin에서 사용할 수 있으며, archive내부 구성을 포함하여 둘 이상의 플러그인이 도입되면 충돌이 발생할 수 있습니다 .


2

Maven 빌드뿐만 아니라 Eclipse에서도 실행하려면 다른 응답에 설명 된대로 addDefaultImplementationEntriesaddDefaultSpecificationEntriespom 항목을 추가 한 후 다음 코드를 사용해야합니다.

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 빌드가 대상 클래스를 "대상 / 클래스"이외의 다른 곳에두면 segmentToRemove의 값을 조정해야합니다.


이것이 단위 테스트 용인지 알 수 있습니다 System.getProperty("user.dir")/pom.xml. WTP가 아닌 것을 제외하고는 다른 것들에 대해서도 확실히 확신합니다.
Adam Gent

프로젝트가 디렉토리에있는 경우에만 작동합니다-jarfile을 기반으로 프로젝트를 실행하면 솔루션이 작동하지 않습니다. .getResource()또는 을 사용해야 .getResourceAsStream()합니다.
Luke Hutchison

예, jar (ala getResource)를 이미 확인했다고 가정합니다. 즉, getResource를 검사하여 실패하면 프로젝트가 jar로 빌드되지 않았지만 아직 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

maven 프로젝트의 war 파일에서 EJB 용 Java 8 변형. 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.