Display application data

what’s the best way to get project data like build date, runtime version?

@mpedrotti I am not sure if there is a Jmix standard way. We did something similar by writing such data into the manifest of the bootable jar by a gradle task.


plugins {
    ...
    id 'org.ajoberstar.grgit' version '5.0.0'
}

def gitCommitId = grgit.status().isClean() ? grgit.head().id : ''

...

tasks.withType(Jar) {
    manifest {
        attributes(
                (java.util.jar.Attributes.Name.IMPLEMENTATION_TITLE.toString()): project.getName(),
                (java.util.jar.Attributes.Name.IMPLEMENTATION_VENDOR.toString()): 'My Compancy Ltd.',
                (java.util.jar.Attributes.Name.IMPLEMENTATION_VERSION.toString()): project.getVersion(),
                (java.util.jar.Attributes.Name.SPECIFICATION_TITLE.toString()): project.getName(),
                (java.util.jar.Attributes.Name.SPECIFICATION_VENDOR.toString()): 'My Compancy Ltd.',
                (java.util.jar.Attributes.Name.SPECIFICATION_VERSION.toString()): project.getVersion(),
                'Created-By': System.getProperty('java.version') + ' (' + System.getProperty('java.vendor') + ' ' + System.getProperty('java.vm.version') + ' ' + System.getProperty('java.vm.name') + ' ' + System.getProperty('java.vm.info') + ')',
                'Built-With': 'gradle-' + project.getGradle().getGradleVersion() + ', groovy-' + GroovySystem.getVersion(),
                'Build-OS': System.getProperty('os.name') + ' ' + System.getProperty('os.arch') + ' ' + System.getProperty('os.version'),
                'Build-Timestamp': java.time.format.DateTimeFormatter.ISO_INSTANT.format(java.time.Instant.now()),
                'Built-By': System.getProperty('user.name'),
                'Built-On': InetAddress.getLocalHost().getHostName(),
                'Jmix-Version': jmix.bomVersion,
                'Git-Commit-Id': gitCommitId
        )
    }
}

To read this data we wrote some Java code, which reads the manifest using standard and custom attributes and returns some simple POJOs (here JarInfo, ManifestInfo, BuildInfo …):

    static JarInfo readManifest(final InputStream someInput, final Set<String> someAdditionalPropNames)
                    throws IOException
{
        Objects.requireNonNull(someInput, "Input stream must not be null");
        ...
        final Manifest manifest = new Manifest(someInput);
        final Attributes mainAttributes = manifest.getMainAttributes();

        final JarInfo result = new JarInfo();

        final ManifestInfo manifestInfo = new ManifestInfo();
        manifestInfo.setVersion(mainAttributes.getValue(Attributes.Name.MANIFEST_VERSION));
        manifestInfo.setClassPath(mainAttributes.getValue(Attributes.Name.CLASS_PATH));
        manifestInfo.setMainClass(mainAttributes.getValue(Attributes.Name.MAIN_CLASS));
        // TODO consider resolving and setting some more predefined ones
        result.setManifestInfo(manifestInfo);

        final String titleImpl = mainAttributes.getValue(Attributes.Name.IMPLEMENTATION_TITLE);
        final String vendorImpl = mainAttributes.getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
        final String versionImpl = mainAttributes.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
        if (titleImpl != null || vendorImpl != null || versionImpl != null)
        {
            final ModuleInfo info = new ModuleInfo();
            info.setTitle(titleImpl);
            info.setVendor(vendorImpl);
            info.setVersion(versionImpl);
            result.setImplementationInfo(info);
        }

        ...

        final String biCreatedBy = mainAttributes.getValue("Created-By");
        final String biBuiltWith = mainAttributes.getValue("Built-With");
        final String biBuildOS = mainAttributes.getValue("Build-OS");
        final String biBuildTimestamp = mainAttributes.getValue("Build-Timestamp");
        final String biBuiltBy = mainAttributes.getValue("Built-By");
        final String biBuiltOn = mainAttributes.getValue("Built-On");
        if (biCreatedBy != null || biBuiltWith != null || biBuildOS != null || biBuildTimestamp != null
                        || biBuiltBy != null || biBuiltOn != null)
        {
            final BuildInfo info = new BuildInfo();
            info.setCreatedBy(biCreatedBy);
            info.setBuiltWith(biBuiltWith);
            info.setBuildOS(biBuildOS);
            info.setBuiltAt(biBuildTimestamp);
            info.setBuiltBy(biBuiltBy);
            info.setBuiltOn(biBuiltOn);
      }
      ....

On the Jmix application this plain Java code is used by a spring-boot component:

static JarInfo readManifest(final Class<?> aClass, final Set<String> someAdditionalPropNames)
    {
        Objects.requireNonNull(aClass, "Class must not be null");

        JarInfo result = null;
        try
        {
            final String manifestPath = "META-INF/MANIFEST.MF";
            final ClassLoader classLoader = aClass.getClassLoader();
            try (final InputStream input = classLoader.getResourceAsStream(manifestPath))
            {
                if (input != null)
                {
                    result = readManifest(input, someAdditionalPropNames);
                }
            }
            catch (final IOException ex)
            {
                ...
           }
        }
        catch (final SecurityException ex)
        {
            ...
        }

        return result;
    }

The class argument is the SpringBootApplication class.

In our case the POJO is mapped into a DTO entity being viewed by a screen fragment on the bottom of the login screen.

1 Like

I understood the logic, I found it interesting, but I didn’t understand how to assemble the first part.

could you guide me in this part?

You can add org.springframework.boot:spring-boot-starter-actuator dependency and use org.springframework.boot.actuate.info.InfoEndpoint to receive Artifact, App name, Build date, Version and Artifact group etc.

The version information is built into any Jmix application using the Spring Boot build plugin. It is stored in the build/resources/main/META-INF/build-info.properties file and packaged in your JAR or WAR artefact.

You can get the build information using the BuildProperties bean of Spring Boot. For example:

@Autowired
private Label<String> buildInfoLabel;
@Autowired
private BuildProperties buildProperties;

@Subscribe
public void onBeforeShow(BeforeShowEvent event) {
    String info = "Group: " + buildProperties.getGroup() + "\n" +
            "Artifact: " + buildProperties.getArtifact() + "\n" +
            "Name: " + buildProperties.getName() + "\n" +
            "Version: " + buildProperties.getVersion() + "\n" +
            "Time: " + buildProperties.getTime();
    buildInfoLabel.setValue(info);
}
2 Likes

thank you very much, it worked.