Отримайте версію артефакту Maven під час виконання


177

Я помітив, що в JAR артефакту Maven атрибут project.version включений у два файли:

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

Чи є рекомендований спосіб прочитати цю версію під час виконання?


Відповіді:


265

Вам не потрібно мати доступ до файлів, характерних для Maven, щоб отримати інформацію про версії будь-якої бібліотеки / класу.

Ви можете просто скористатися getClass().getPackage().getImplementationVersion()для отримання інформації про версію, яка зберігається у .jar-файлах MANIFEST.MF. На щастя, Мейвен досить розумний На жаль, Maven не пише правильної інформації в маніфест також за замовчуванням!

Натомість потрібно змінити <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 були проблеми саме в цьому випадку).
мешканець

@avithan: справді? Я ніколи не мав проблем із Tomcat з таким підходом. Крім того, я думаю, що завантажувач класів, який ігнорує маніфест, ймовірно, не відповідає.
Йоахім Зауер

@JoachimSauer добре, я помилявся. В даний час здається, що він чудово працює на HotSpot, але не працює надійно на OpenJDK. Я
звітну,

@avithan це стосується мене (і я не бачив, про що ви повідомляєте) - ви ще отримали детальну інформацію?
Thorbjørn Ravn Andersen

4
На жаль, це не працює, якщо проект запускається з Eclipse або використовується "mvn exec: java".
Яан

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> true </archiveClasses> викликав помилку в моєму випадку. Але проблему вирішили stackoverflow.com/questions/14934299/…
Пол Верест

10
Коли я намагаюсь це, то мій результат завжди є, nullхоча MANIFEST.MF у файлах війни містить правильну інформацію.
thomas.mc.робота

Мені також потрібно було додати його до maven-Assembly-plugin
acheron55

2
<archiveClasses> true </archiveClasses> здається не пов’язаним
Karl Kildén

1
@RafaelSimonelli я видалив <archiveClasses>true</archiveClasses>- і він працює надійно з тих пір.
thomas.mc.робота

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), обов'язково використовуйте getServletContext (). GetResourceAsStream замість getClass (). GetResourceAsStream
Sandman

3
Це працює лише тоді, коли програма запущена з банки. Якщо запустити з exec-maven-plugin (наприклад, Netbeans), ресурс є нульовим.
Leif Gruenwoldt

Цей код буде частиною мого основного за замовчуванням класу! Дякую!!
Вендел

Я використав це з відповіддю Вілла для прямого і простого в обслуговуванні варіанту.
javydreamercsw

3

Я витратив деякий час на два основні підходи тут, і вони не спрацювали для мене. Я використовую Netbeans для складання, можливо, там більше відбувається. У мене були деякі помилки та застереження від Maven 3 з деякими конструкціями, але я думаю, що їх було легко виправити. Немає великого.

У цій статті на DZone я знайшов відповідь, яка виглядає бездоганною та простою:

У мене вже є підпапка ресурсів / config, і я назвав свій файл: app.properties, щоб краще відобразити тип матеріалів, які ми можемо зберігати там (наприклад, URL-адреса підтримки тощо).

Єдине застереження полягає в тому, що Netbeans попереджає, що IDE потребує фільтрації. Не впевнений, де / як. На цей момент це не має ефекту. Можливо, для цього є робота, якщо мені потрібно перейти цей міст. Удачі.


3

Я використовую maven-assembly-pluginдля упаковки Maven. Використання архіватора Apache Maven у відповіді Йоахіма Зауера також може спрацювати:

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

Оскільки архівник є одним із спільних компонентів Maven , він може бути використаний декількома плагінами maven building, які також можуть мати конфлікт, якщо введено два чи більше плагінів, включаючи 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 ставить цільові класи десь окрім "target / класів", то вам може знадобитися коригувати значення segmentsToRemove.


Ви знаєте, якщо це для одиничних тестів, ви можете просто System.getProperty("user.dir")/pom.xml. Я впевнений, що це буде і для інших речей, за винятком того, що можливо не для WTP.
Адам Гент

Це буде працювати лише в тому випадку, якщо ваш проект знаходиться в каталозі - якщо ви працюєте з проектом на базі jarfiles, ваше рішення не працюватиме. Вам потрібно використовувати .getResource()або .getResourceAsStream().
Люк Хатчісон

Так, я припускав, що ви вже перевірили банку (ala getResource). По-перше, ви перевірите за допомогою getResource, якщо це не вдалося, тоді проект ще не був вбудований в банку, а це означає, що ви або запускаєте його з Eclipse або Maven, що означає `System.getProperty (" user.dir ") / pom.xml . Єдине питання полягає в тому, що цей файл Pom не є справжньою ефективною пом (тобто деякі властивості не розширюватимуться), але жоден з них не отримує шляхом Eclipse.
Адам Гент

1

У моєму застосуванні до весняного завантаження рішення з прийнятої відповіді працювало, поки я нещодавно не оновив jdk до версії 12. Спробував і всі інші відповіді, і я не зміг це зробити.

У цей момент я додав нижченаведений рядок до першого класу моєї програми для весняного завантаження, відразу після анотації @SpringBootApplication

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

Пізніше я використовую наведене нижче, щоб отримати значення з файлу властивостей у будь-якому класі, я хочу використовувати його значення та appVersionотримує для мене версію проекту:

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

Сподіваюся, що хтось допомагає.


Як зробити те ж саме з кількома файлами 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 у файлі війни з проектом 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!");
            }
        });
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.