MainJarInfo.java

package io.extact.rms.platform.env;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.function.Predicate;
import java.util.jar.Manifest;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.eclipse.microprofile.config.Config;

import io.extact.rms.platform.util.ResourceUtils;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

@Getter
@Slf4j
public class MainJarInfo {

    private static final String UNKNOWN_INFORMATION = "-";
    private static final String MAIN_JAR_PROP = "main.manifest.jar";

    static final MainJarInfo UNKNOWN_INFO;
    static {
        var temp = new MainJarInfo();
        temp.applicationName = UNKNOWN_INFORMATION;
        temp.jarName = UNKNOWN_INFORMATION;
        temp.mainClassName = UNKNOWN_INFORMATION;
        temp.version = UNKNOWN_INFORMATION;
        temp.buildtimeInfo = UNKNOWN_INFORMATION;
        UNKNOWN_INFO = temp;
    }

    private String applicationName;
    private String jarName;
    private String mainClassName;
    private String version;
    private String buildtimeInfo;


    // ----------------------------------------------------- static methods

    static MainJarInfoBuilder builder() {
        return new MainJarInfoBuilder();
    }


    // ----------------------------------------------------- getter methods

    public String startupModuleInfo() {
        if (applicationName.equals(UNKNOWN_INFORMATION)) {
            return UNKNOWN_INFORMATION;
        }
        return applicationName + "/" + jarName + "/" + mainClassName;
    }


    // ----------------------------------------------------- inner class def

    static class MainJarInfoBuilder {

        MainJarInfo build(Config config) {
            var manifestUrl = findTargetManifestUrl(config);
            return buidFromManifestUrl(manifestUrl);
        }

        private URL findTargetManifestUrl(Config config) {
            String jarFindName = config.getOptionalValue(MAIN_JAR_PROP, String.class).orElse(null);
            if (jarFindName == null) {
                return null;
            }

            // find jar metainf
            Enumeration<URL> metainfResources;
            try {
                metainfResources = Thread.currentThread().getContextClassLoader().getResources("META-INF/MANIFEST.MF");
            } catch (IOException e) {
                throw new IllegalStateException(e);
            }

            // filter by jar protocol and filename
            Predicate<Object> findJarMatcher = new FindJarMatcher(jarFindName);
            List<URL> metainfUrls = Collections.list(metainfResources).stream()
                                .filter(url -> url.getProtocol().equals("jar"))
                                .filter(url -> findJarMatcher.test(ResourceUtils.extractFilePathStringOfUrl(url)))
                                .toList();

            // resolve metainf resource url
            if (metainfUrls.isEmpty()) {
                log.warn("no matched jar or META-INF [{}]", jarFindName);
                return null;
            }
            if (metainfUrls.size() > 1) {
                String urlPaths = metainfUrls.stream().map(URL::toString).collect(Collectors.joining(System.lineSeparator()));
                log.warn("to many match jar or META-INF [{}]" + System.lineSeparator() + urlPaths, jarFindName);
                return null;
            }
            return metainfUrls.get(0);
        }

        private MainJarInfo buidFromManifestUrl(URL manifestUrl) {

            // check parameter
            if (manifestUrl == null) {
                return null;
            }

            // resolve jarName from jarfile path
            var jarFilePath = manifestUrl.toString().substring(0, manifestUrl.toString().indexOf('!'));
            var jarName = jarFilePath.substring(jarFilePath.lastIndexOf("/") + 1, jarFilePath.length());

            // resolve manifest
            Manifest manifest;
            try (InputStream is = manifestUrl.openStream()) {
                manifest = new Manifest(is);
            } catch (IOException e) {
                throw new IllegalStateException(e);
            }

            // resolve metainf prop
            String applicationName = getManifestValue(manifest, "Application-Name", UNKNOWN_INFORMATION);
            String version = getManifestValue(manifest, "Application-Version", UNKNOWN_INFORMATION);
            String mainClassName = getManifestValue(manifest, "Main-Class", UNKNOWN_INFORMATION);
            String buildtime = getManifestValue(manifest, "Build-Time", UNKNOWN_INFORMATION);

            // setup MainJarInfo
            var mainJarInfo = new MainJarInfo();
            mainJarInfo.applicationName = applicationName;
            mainJarInfo.jarName = jarName;
            mainJarInfo.mainClassName = mainClassName;
            mainJarInfo.version = version;
            mainJarInfo.buildtimeInfo = buildtime;

            return mainJarInfo;
        }

        private String getManifestValue(Manifest manifest, String key, String defaultValue) {
            String value = manifest.getMainAttributes().getValue(key);
            return  (value == null) ? defaultValue : value;
        }
    }


    // ----------------------------------------------------- inner class def

    static class FindJarMatcher implements Predicate<Object> {
        private Pattern pattern;
        private FindJarMatcher(String findJarName) {
            this.pattern = Pattern.compile(findJarName);
        }
        @Override
        public boolean test(Object input) {
            return pattern.matcher(input.toString()).find();
        }
    }
}