ServletContextSystemInfo.java

package com.reallifedeveloper.common.infrastructure.jmx;

import static com.reallifedeveloper.common.domain.LogUtil.removeCRLF;

import java.io.InputStream;
import java.util.jar.Attributes;
import java.util.jar.Manifest;

import org.checkerframework.checker.nullness.qual.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.web.context.ServletContextAware;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.servlet.ServletContext;

/**
 * An implementation of the JMX {@link SystemInfoMXBean} interface that reads the system information from a manifest file in a WAR file.
 * <p>
 * This class expects to be configured as a Spring bean in a web application so that the {@link #setServletContext(ServletContext)} method
 * is called automatically. If you want to use this class outside of Spring, you are responsible for calling this method.
 *
 * @author RealLifeDeveloper
 */
@ManagedResource(description = "General System Information")
public class ServletContextSystemInfo implements SystemInfoMXBean, ServletContextAware {

    private static final String VERSION_MANIFEST_ENTRY = "Implementation-Version";
    private static final String BUILD_TIME_MANIFEST_ENTRY = "Build-Time";
    private static final String SCM_REVISION_MANIFEST_ENTRY = "SCM-Revision";

    private static final Logger LOG = LoggerFactory.getLogger(ServletContextSystemInfo.class);

    private @Nullable String version;
    private @Nullable String buildTime;
    private @Nullable String scmRevision;

    @Override
    @ManagedAttribute(description = "System version")
    public @Nullable String getVersion() {
        return version;
    }

    @Override
    @ManagedAttribute(description = "Date and time that the system was built")
    public @Nullable String getBuildTime() {
        return buildTime;
    }

    @Override
    @ManagedAttribute(description = "System revision number in version control")
    public @Nullable String getScmRevision() {
        return scmRevision;
    }

    @Override
    @SuppressWarnings({ "PMD.AvoidCatchingGenericException", "PMD.LooseCoupling" })
    @SuppressFBWarnings("REC_CATCH_EXCEPTION")
    public void setServletContext(ServletContext servletContext) {
        try (InputStream in = servletContext.getResourceAsStream("/META-INF/MANIFEST.MF")) {
            Manifest manifest = new Manifest(in);
            Attributes attributes = manifest.getMainAttributes();
            version = attributes.getValue(VERSION_MANIFEST_ENTRY);
            LOG.info("version={}", removeCRLF(version));
            buildTime = attributes.getValue(BUILD_TIME_MANIFEST_ENTRY);
            LOG.info("buildTime={}", removeCRLF(buildTime));
            scmRevision = attributes.getValue(SCM_REVISION_MANIFEST_ENTRY);
            LOG.info("scmRevision={}", removeCRLF(scmRevision));
        } catch (Exception e) {
            LOG.error("Failed to read META-INF/MANIFEST.MF: ", e);
        }
    }

}