package org.apereo.cas.util;

import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.core.io.VfsResource;

import java.io.File;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;


/**
 * Class that exposes the CAS version. Fetches the "Implementation-Version"
 * manifest attribute from the jar file.
 *
 * @author Dmitriy Kopylenko
 * @since 3.0.0
 */
@Slf4j
@UtilityClass
public class CasVersion {
    private static final int JAR_PROTOCOL_STARTING_INDEX = 5;

    /**
     * To string.
     *
     * @return the string
     */
    public static String asString() {
        return getVersion() + " - " + getSpecificationVersion() + " - " + getDateTime().toString();
    }

    /**
     * @return Return the full CAS version string.
     * @see java.lang.Package#getImplementationVersion
     */
    public static String getVersion() {
        return CasVersion.class.getPackage().getImplementationVersion();
    }

    /**
     * Gets specification version from the manifest package.
     *
     * @return the specification version
     */
    public static String getSpecificationVersion() {
        return CasVersion.class.getPackage().getSpecificationVersion();
    }

    /**
     * Gets last modified date/time for the module.
     *
     * @return the date/time
     */
    public static ZonedDateTime getDateTime() {
        val clazz = CasVersion.class;
        val resource = clazz.getResource(clazz.getSimpleName() + ".class");
        try {
            if ("file".equals(resource.getProtocol())) {
                return DateTimeUtils.zonedDateTimeOf(new File(resource.toURI()).lastModified());
            }
            if ("jar".equals(resource.getProtocol())) {
                val path = resource.getPath();
                val file = new File(path.substring(JAR_PROTOCOL_STARTING_INDEX, path.indexOf('!')));
                return DateTimeUtils.zonedDateTimeOf(file.lastModified());
            }
            if ("vfs".equals(resource.getProtocol())) {
                val content = resource.openConnection().getContent();
                val virtualFile = Thread.currentThread().getContextClassLoader().loadClass("org.jboss.vfs.VirtualFile");
                if (virtualFile.isAssignableFrom(content.getClass())) {
                    val file = new VfsResource(resource.openConnection().getContent(new Class[] {virtualFile})).getFile();
                    return DateTimeUtils.zonedDateTimeOf(file.lastModified());
                }
                return ZonedDateTime.now(ZoneOffset.UTC);
            }
        } catch (final Exception e) {
            LOGGER.warn(e.getMessage(), e);
        }
        LOGGER.warn("Unhandled url protocol: [{}] resource: [{}]", resource.getProtocol(), resource);
        return ZonedDateTime.now(ZoneOffset.UTC);
    }
}

package org.apereo.cas.util;

import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import lombok.val;

import java.io.File;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;


/**
 * Class that exposes the CAS version. Fetches the "Implementation-Version"
 * manifest attribute from the jar file.
 *
 * @author Dmitriy Kopylenko
 * @since 3.0.0
 */
@Slf4j
@UtilityClass
public class CasVersion {
    private static final int JAR_PROTOCOL_STARTING_INDEX = 5;

    /**
     * To string.
     *
     * @return the string
     */
    public static String asString() {
        return getVersion() + " - " + getSpecificationVersion() + " - " + getDateTime().toString();
    }

    /**
     * @return Return the full CAS version string.
     * @see java.lang.Package#getImplementationVersion
     */
    public static String getVersion() {
        return CasVersion.class.getPackage().getImplementationVersion();
    }

    /**
     * Gets specification version from the manifest package.
     *
     * @return the specification version
     */
    public static String getSpecificationVersion() {
        return CasVersion.class.getPackage().getSpecificationVersion();
    }

    /**
     * Gets last modified date/time for the module.
     *
     * @return the date/time
     */
    public static ZonedDateTime getDateTime() {
        val clazz = CasVersion.class;
        val resource = clazz.getResource(clazz.getSimpleName() + ".class");
        try {
            return DateTimeUtils.zonedDateTimeOf(resource.openConnection().getLastModified());
        } catch (final Exception e) {
            LOGGER.warn(e.getMessage(), e);
        }
        LOGGER.warn("Unhandled url protocol: [{}] resource: [{}]", resource.getProtocol(), resource);
        return ZonedDateTime.now(ZoneOffset.UTC);
    }
}

Determine last-modified-date for CASVersion correctly (#4168)