Получить версию артефакта Maven во время выполнения

177

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

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

Есть ли рекомендуемый способ чтения этой версии во время выполнения?

Armand
источник
См stackoverflow.com/a/26589696/52176
Лейф Gruenwoldt

Ответы:

265

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

Вы можете просто использовать, getClass().getPackage().getImplementationVersion()чтобы получить информацию о версии, которая хранится в .jar-файлах MANIFEST.MF. К счастью, Maven достаточно умен. К сожалению, по умолчанию 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 это относится ко мне (и я не видел, что вы сообщаете) - вы уже получили подробную информацию?
Турбьёрн Равн Андерсен
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 в файлах war содержит правильную информацию.
thomas.mc.work
Мне также нужно было добавить его в maven-assembly-plugin
acheron55
2
<archiveClasses> true </ archiveClasses> кажется не связанным
Карл Килден
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;
} 
mysomic
источник
2
Поместите это в статический блок инициализатора.
Опять
1
Хороший совет. Хотя, если вы используете это в сервлете (или .jsp), обязательно используйте getServletContext (). GetResourceAsStream вместо getClass (). GetResourceAsStream
Sandman
3
Это работает только тогда, когда приложение запускается из банки. При запуске из exec-maven-plugin (например, Netbeans) ресурс будет нулевым.
Лейф Грюнволдт,
Этот код будет частью моего основного класса по умолчанию! Спасибо!!
Вендел
Я использовал это с ответом Уилла для прямого и простого в обслуживании варианта.
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>

Поскольку архиватор является одним из общих компонентов maven , он может использоваться несколькими подключаемыми модулями сборки maven, что также может привести к конфликту, если появятся два или более подключаемых модуля, включая archiveконфигурацию внутри.

千 木 郷
источник
2

Чтобы запустить это в Eclipse, а также в сборке Maven, вы должны добавить записи 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 / classes», то вам, возможно, потребуется скорректировать значение columnsToRemove.

Люк Хатчисон
источник
Вы знаете, если это для модульных тестов, вы можете просто System.getProperty("user.dir")/pom.xml. Я вполне уверен, что это будет и для других вещей, за исключением, может быть, не для WTP.
Адам Гент
Это будет работать, только если ваш проект находится в каталоге - если вы запускаете проект, основанный на jarfiles, ваше решение не будет работать. Вам нужно использовать .getResource()или .getResourceAsStream().
Люк Хатчисон
Да, я предполагал, что вы уже проверили банку (аля getResource). То есть сначала вы проверяете с помощью getResource, если это не удается, тогда проект еще не был встроен в jar, что означает, что вы запускаете его из Eclipse или Maven, что означает `System.getProperty (" user.dir ") / pom.xml , Единственная проблема заключается в том, что этот pom-файл не является действительно эффективным pom (то есть некоторые свойства не будут расширены), но также не тот, который вы получаете с помощью Eclipse.
Адам Гент
1

В моем весеннем загрузочном приложении решение из принятого ответа работало до тех пор, пока я недавно не обновил свой jdk до версии 12. Также пробовал все остальные ответы и не мог заставить это работать.

В этот момент я добавил строку ниже к первому классу моего весеннего загрузочного приложения, сразу после аннотации @SpringBootApplication

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

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

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

Надеюсь, это поможет кому-то.

Reema
источник
Как сделать то же самое с несколькими файлами 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();
    }
rdehuyss
источник
-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!");
            }
        });
    }
}
onderbewustzijn
источник