From 3536dded1bb7714056cd78e152882e33d5baa049 Mon Sep 17 00:00:00 2001 From: Rick Herrick <jrherrick@wustl.edu> Date: Wed, 11 May 2016 23:49:19 -0500 Subject: [PATCH] Removed old version REST API, added new buildInfo call in siteConfig, added build plugin to retrieve git repo info and build number. --- build.gradle | 28 ++++++++++- .../nrg/xapi/rest/settings/SiteConfigApi.java | 35 ++++++++++++++ .../nrg/xnat/initialization/RootConfig.java | 11 +++++ .../org/nrg/xnat/restlet/XNATApplication.java | 2 - .../resources/VersionRepresentation.java | 46 ------------------- .../webapp/WEB-INF/conf/xnat-security.xml | 2 - src/main/webapp/scripts/footer.js | 10 ++-- src/main/webapp/scripts/timeLeft.js | 2 +- 8 files changed, 80 insertions(+), 56 deletions(-) delete mode 100644 src/main/java/org/nrg/xnat/restlet/resources/VersionRepresentation.java diff --git a/build.gradle b/build.gradle index c4358a0c..7822995b 100644 --- a/build.gradle +++ b/build.gradle @@ -18,11 +18,14 @@ def vGroovy = '2.4.6' def vJython = '2.7.0' group 'org.nrg.xnat' -version vXnat buildscript { repositories { mavenLocal() + jcenter() + maven { + url "https://plugins.gradle.org/m2/" + } maven { url 'https://nrgxnat.artifactoryonline.com/nrgxnat/libs-release' name 'XNAT Release Repository' @@ -31,11 +34,11 @@ buildscript { url 'https://nrgxnat.artifactoryonline.com/nrgxnat/libs-snapshot' name 'XNAT Snapshot Repository' } - jcenter() } dependencies { classpath "com.bmuschko:gradle-cargo-plugin:2.2.2" classpath "com.bmuschko:gradle-tomcat-plugin:2.2.4" + classpath "gradle.plugin.com.zoltu.gradle.plugin:git-versioning:2.0.19" } } @@ -46,6 +49,7 @@ apply plugin: 'maven' apply plugin: 'maven-publish' apply plugin: 'com.bmuschko.tomcat' apply plugin: 'com.bmuschko.cargo' +apply plugin: "com.zoltu.git-versioning" apply plugin: 'idea' apply plugin: 'eclipse' @@ -135,6 +139,26 @@ if (JavaVersion.current().isJava8Compatible()) { } } +// Pulls in the Jenkins BUILD_NUMBER environment variable if available. +def buildNumber = hasProperty("BUILD_NUMBER") ? getProperty("BUILD_NUMBER") : "Manual" +def buildDate = new Date() + +jar { + manifest { + attributes 'Build-Number': buildNumber, + 'Build-Date': buildDate, + 'Application-Name': 'XNAT' + } +} + +war { + manifest { + attributes 'Build-Number': buildNumber, + 'Build-Date': buildDate, + 'Application-Name': 'XNAT' + } +} + task sourceJar(type: Jar, dependsOn: classes) { from sourceSets.main.allSource } diff --git a/src/main/java/org/nrg/xapi/rest/settings/SiteConfigApi.java b/src/main/java/org/nrg/xapi/rest/settings/SiteConfigApi.java index 6c3ca05a..fcdad8eb 100644 --- a/src/main/java/org/nrg/xapi/rest/settings/SiteConfigApi.java +++ b/src/main/java/org/nrg/xapi/rest/settings/SiteConfigApi.java @@ -21,11 +21,40 @@ import org.springframework.web.bind.annotation.RequestMethod; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Properties; +import java.util.jar.Attributes; +import java.util.jar.Manifest; @Api(description = "Site Configuration Management API") @XapiRestController @RequestMapping(value = "/siteConfig") public class SiteConfigApi extends AbstractXnatRestApi { + @ApiOperation(value = "Returns a map of application build properties.", notes = "This includes the implementation version, Git commit hash, and build number and number.", response = Properties.class) + @ApiResponses({@ApiResponse(code = 200, message = "Application build properties successfully retrieved."), @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."), @ApiResponse(code = 500, message = "Unexpected error")}) + @RequestMapping(value = "buildInfo", produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.GET}) + public ResponseEntity<Properties> getBuildInfo() { + if (_log.isDebugEnabled()) { + _log.debug("User " + getSessionUser().getUsername() + " requested the application build information."); + } + + if (_properties.size() == 0) { + final Attributes attributes = _manifest.getMainAttributes(); + _properties.setProperty("buildNumber", attributes.getValue("Build-Number")); + _properties.setProperty("buildDate", attributes.getValue("Build-Date")); + _properties.setProperty("version", attributes.getValue("Implementation-Version")); + _properties.setProperty("commit", attributes.getValue("Implementation-Sha")); + if (_log.isDebugEnabled()) { + _log.debug("Initialized application build information:\n * Version: {}\n * Build number: {}\n * Build Date: {}\n * Commit: {}", + _properties.getProperty("version"), + _properties.getProperty("buildNumber"), + _properties.getProperty("buildDate"), + _properties.getProperty("commit")); + } + } + + return new ResponseEntity<>(_properties, HttpStatus.OK); + } + @ApiOperation(value = "Returns the full map of site configuration properties.", notes = "Complex objects may be returned as encapsulated JSON strings.", response = SiteConfigPreferences.class) @ApiResponses({@ApiResponse(code = 200, message = "Site configuration properties successfully retrieved."), @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."), @ApiResponse(code = 403, message = "Not authorized to set site configuration properties."), @ApiResponse(code = 500, message = "Unexpected error")}) @RequestMapping(produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.GET}) @@ -134,4 +163,10 @@ public class SiteConfigApi extends AbstractXnatRestApi { @Autowired @Lazy private SiteConfigPreferences _preferences; + + @Autowired + @Lazy + private Manifest _manifest; + + private Properties _properties = new Properties(); } diff --git a/src/main/java/org/nrg/xnat/initialization/RootConfig.java b/src/main/java/org/nrg/xnat/initialization/RootConfig.java index 3a539caf..cb878fda 100644 --- a/src/main/java/org/nrg/xnat/initialization/RootConfig.java +++ b/src/main/java/org/nrg/xnat/initialization/RootConfig.java @@ -24,9 +24,13 @@ import org.springframework.http.converter.json.MappingJackson2HttpMessageConvert import org.springframework.http.converter.xml.MarshallingHttpMessageConverter; import org.springframework.oxm.jaxb.Jaxb2Marshaller; +import javax.servlet.ServletContext; import javax.xml.bind.Marshaller; +import java.io.IOException; +import java.io.InputStream; import java.util.HashMap; import java.util.Map; +import java.util.jar.Manifest; /** * Configuration for the XNAT root application context. This contains all of the basic infrastructure for initializing @@ -40,6 +44,13 @@ import java.util.Map; @Import({PropertiesConfig.class, DatabaseConfig.class}) @ImportResource("WEB-INF/conf/xnat-security.xml") public class RootConfig { + @Bean + public Manifest applicationManifest(final ServletContext context) throws IOException { + try (final InputStream input = context.getResourceAsStream("/META-INF/MANIFEST.MF")) { + return new Manifest(input); + } + } + @Bean public InitializerSiteConfiguration initializerSiteConfiguration() { return new InitializerSiteConfiguration(); diff --git a/src/main/java/org/nrg/xnat/restlet/XNATApplication.java b/src/main/java/org/nrg/xnat/restlet/XNATApplication.java index 1bcbc4b2..d7567871 100755 --- a/src/main/java/org/nrg/xnat/restlet/XNATApplication.java +++ b/src/main/java/org/nrg/xnat/restlet/XNATApplication.java @@ -477,8 +477,6 @@ public class XNATApplication extends Application { } private void addPublicRoutes(final Router router, List<Class<? extends Resource>> publicRoutes) { - attachURI(router, "/version", VersionRepresentation.class); - if (publicRoutes == null) { return; } diff --git a/src/main/java/org/nrg/xnat/restlet/resources/VersionRepresentation.java b/src/main/java/org/nrg/xnat/restlet/resources/VersionRepresentation.java deleted file mode 100644 index 25355881..00000000 --- a/src/main/java/org/nrg/xnat/restlet/resources/VersionRepresentation.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * org.nrg.xnat.restlet.resources.VersionRepresentation - * XNAT http://www.xnat.org - * Copyright (c) 2014, Washington University School of Medicine - * All Rights Reserved - * - * Released under the Simplified BSD. - * - * Last modified 7/10/13 9:04 PM - */ -package org.nrg.xnat.restlet.resources; - -import org.nrg.xnat.utils.FileUtils; -import org.restlet.Context; -import org.restlet.data.MediaType; -import org.restlet.data.Request; -import org.restlet.data.Response; -import org.restlet.resource.Representation; -import org.restlet.resource.Resource; -import org.restlet.resource.StringRepresentation; -import org.restlet.resource.Variant; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; - -public class VersionRepresentation extends Resource { - private final Logger logger = LoggerFactory.getLogger(VersionRepresentation.class); - - public VersionRepresentation(Context context, Request request, Response response) { - super(context, request, response); - this.getVariants().add(new Variant(MediaType.ALL)); - } - - @Override - public Representation represent(Variant variant) { - if (logger.isDebugEnabled()) { - logger.debug("Getting XNAT version from the default configuration folder"); - } - try { - return new StringRepresentation(FileUtils.getXNATVersion()); - } catch (IOException exception) { - return new StringRepresentation("[Error: check system logs]"); - } - } -} diff --git a/src/main/webapp/WEB-INF/conf/xnat-security.xml b/src/main/webapp/WEB-INF/conf/xnat-security.xml index bd9d4683..1e8e4fee 100644 --- a/src/main/webapp/WEB-INF/conf/xnat-security.xml +++ b/src/main/webapp/WEB-INF/conf/xnat-security.xml @@ -111,8 +111,6 @@ <value>/app/template/VerificationSent.vm*</value> <value>/app/template/VerifyEmail.vm*</value> <value>/favicon.ico</value> - <value>/data/version</value> - <value>/REST/version</value> <value>/data/JSESSION</value> <value>/REST/JSESSION</value> <value>/data/services/auth*</value> diff --git a/src/main/webapp/scripts/footer.js b/src/main/webapp/scripts/footer.js index 98590d8a..46c53722 100644 --- a/src/main/webapp/scripts/footer.js +++ b/src/main/webapp/scripts/footer.js @@ -534,9 +534,13 @@ $(function(){ // add version to title attribute of XNAT logos if (typeof logged_in != 'undefined' && logged_in == true){ - $.get(serverRoot+'/data/version',function(data){ - XNAT_version = data.split(" ")[0]; - $('#xnat_power').find('a').attr('title','XNAT version ' + XNAT_version).after('<small>version ' + XNAT_version + '</small>'); + $.get(serverRoot+'/xapi/siteConfig/buildInfo',function(data){ + XNAT_version = data.version + " build: " + data.buildNumber; + var isNonRelease = /.*(SNAPSHOT|BETA|RC).*/.test(data.version); + if (isNonRelease) { + XNAT_version += " (" + data.commit + ")"; + } + $('#xnat_power').find('a').attr('title','XNAT version ' + XNAT_version).after('<small>version ' + XNAT_version + (isNonRelease ? "<br>" + data.buildDate : "") + '</small>'); $('#header_logo').attr('title','XNAT version ' + XNAT_version); XNAT.app.version = XNAT_version ; }); diff --git a/src/main/webapp/scripts/timeLeft.js b/src/main/webapp/scripts/timeLeft.js index 4239784a..0572060e 100644 --- a/src/main/webapp/scripts/timeLeft.js +++ b/src/main/webapp/scripts/timeLeft.js @@ -180,7 +180,7 @@ if (typeof XNAT.app.timeout == 'undefined'){ XNAT.app.timeout={} } timeout.handleOk = function () { timeout.hideWarningDialog(timeout.warningDialog); timeout.touchCallback.startTime = new Date().getTime(); - XNAT.xhr.get(XNAT.url.restUrl('/data/version'), timeout.touchCallback); + XNAT.xhr.get(XNAT.url.restUrl('/xapi/siteConfig/buildInfo'), timeout.touchCallback); $('applet').css('visibility', 'visible'); }; -- GitLab