Skip to content

Commit 67cb318

Browse files
committed
add feature to check new github releases and show info on about page
1 parent f055a89 commit 67cb318

6 files changed

Lines changed: 184 additions & 0 deletions

File tree

pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,13 @@
645645
<scope>test</scope>
646646
</dependency>
647647

648+
<!-- https://github.com/zafarkhaja/jsemver -->
649+
<dependency>
650+
<groupId>com.github.zafarkhaja</groupId>
651+
<artifactId>java-semver</artifactId>
652+
<version>0.9.0</version>
653+
</dependency>
654+
648655
<!-- Codes according to ISO standards
649656
https://github.com/TakahikoKawasaki/nv-i18n -->
650657
<dependency>
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package de.rwth.idsg.steve.service;
2+
3+
import com.fasterxml.jackson.databind.DeserializationFeature;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
6+
import com.github.zafarkhaja.semver.Version;
7+
import de.rwth.idsg.steve.SteveConfiguration;
8+
import de.rwth.idsg.steve.web.dto.ReleaseReport;
9+
import lombok.Getter;
10+
import lombok.Setter;
11+
import lombok.ToString;
12+
import lombok.extern.slf4j.Slf4j;
13+
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
14+
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
15+
import org.springframework.stereotype.Service;
16+
import org.springframework.web.client.RestClientException;
17+
import org.springframework.web.client.RestTemplate;
18+
19+
import javax.annotation.PostConstruct;
20+
import java.util.Collections;
21+
22+
/**
23+
* @author Sevket Goekay <goekay@dbis.rwth-aachen.de>
24+
* @since 19.04.2016
25+
*/
26+
@Slf4j
27+
@Service
28+
public class GithubReleaseCheckService implements ReleaseCheckService {
29+
30+
/**
31+
* If the Github api is slow to respond, we don't want the client of this class to wait forever (until the default
32+
* timeout kicks in).
33+
*/
34+
private static final int API_TIMEOUT_IN_MILLIS = 4_000;
35+
36+
private static final String API_URL = "https://api.github.com/repos/RWTH-i5-IDSG/steve/releases/latest";
37+
38+
private static final String TAG_NAME_PREFIX = "steve-";
39+
40+
private static final String FILE_SEPARATOR = System.getProperty("file.separator");
41+
42+
private RestTemplate restTemplate;
43+
44+
@PostConstruct
45+
private void init() {
46+
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
47+
factory.setReadTimeout(API_TIMEOUT_IN_MILLIS);
48+
factory.setConnectTimeout(API_TIMEOUT_IN_MILLIS);
49+
50+
ObjectMapper mapper = new ObjectMapper();
51+
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
52+
mapper.setPropertyNamingStrategy(new PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy());
53+
54+
restTemplate = new RestTemplate(Collections.singletonList(new MappingJackson2HttpMessageConverter(mapper)));
55+
restTemplate.setRequestFactory(factory);
56+
}
57+
58+
@Override
59+
public ReleaseReport check() {
60+
try {
61+
LatestReleaseResponse response = restTemplate.getForObject(API_URL, LatestReleaseResponse.class);
62+
return getReport(response);
63+
64+
} catch (RestClientException e) {
65+
// Fallback to "there is no new version atm".
66+
// Probably because Github did not respond within the timeout.
67+
return new ReleaseReport(false);
68+
}
69+
}
70+
71+
// -------------------------------------------------------------------------
72+
// Private helpers
73+
// -------------------------------------------------------------------------
74+
75+
private static ReleaseReport getReport(LatestReleaseResponse response) {
76+
String githubVersion = extractVersion(response);
77+
78+
Version build = Version.valueOf(SteveConfiguration.CONFIG.getSteveVersion());
79+
Version github = Version.valueOf(githubVersion);
80+
81+
boolean isGithubMoreRecent = github.greaterThan(build);
82+
String downloadUrl = decideDownloadUrl(response);
83+
84+
ReleaseReport ur = new ReleaseReport(isGithubMoreRecent);
85+
ur.setGithubVersion(githubVersion);
86+
ur.setDownloadUrl(downloadUrl);
87+
ur.setHtmlUrl(response.getHtmlUrl());
88+
return ur;
89+
}
90+
91+
private static String decideDownloadUrl(LatestReleaseResponse response) {
92+
if (isWindows()) {
93+
return response.getZipballUrl();
94+
} else {
95+
return response.getTarballUrl();
96+
}
97+
}
98+
99+
private static String extractVersion(LatestReleaseResponse response) {
100+
return response.getTagName().replaceFirst(TAG_NAME_PREFIX, "");
101+
}
102+
103+
/**
104+
* A little bit hacky, but good-enough solution. We only need to find out the family of the os (whether unix
105+
* or win). Therefore, we don't need full blown os detection, such as
106+
*
107+
* - https://github.com/apache/commons-lang/blob/master/src/main/java/org/apache/commons/lang3/SystemUtils.java
108+
* - http://stackoverflow.com/a/24861219
109+
*
110+
* So, we base or decision on file.separator property. According to
111+
* https://docs.oracle.com/javase/tutorial/essential/environment/sysprop.html,
112+
* it is "/" on UNIX and "\" on Windows.
113+
*/
114+
private static boolean isWindows() {
115+
return FILE_SEPARATOR.equals("\\");
116+
}
117+
118+
/**
119+
* Does not contain all the fields in the actual response, but only the ones that we are interested in.
120+
*
121+
* API doc: https://developer.github.com/v3/repos/releases/#get-the-latest-release
122+
*/
123+
@Getter
124+
@Setter
125+
@ToString
126+
private static class LatestReleaseResponse {
127+
private String tagName;
128+
private String name;
129+
130+
private String htmlUrl;
131+
private String tarballUrl;
132+
private String zipballUrl;
133+
}
134+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package de.rwth.idsg.steve.service;
2+
3+
import de.rwth.idsg.steve.web.dto.ReleaseReport;
4+
5+
/**
6+
* @author Sevket Goekay <goekay@dbis.rwth-aachen.de>
7+
* @since 19.04.2016
8+
*/
9+
public interface ReleaseCheckService {
10+
ReleaseReport check();
11+
}

src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import de.rwth.idsg.steve.repository.GenericRepository;
55
import de.rwth.idsg.steve.repository.SettingsRepository;
66
import de.rwth.idsg.steve.service.MailService;
7+
import de.rwth.idsg.steve.service.ReleaseCheckService;
78
import de.rwth.idsg.steve.web.dto.SettingsForm;
89
import org.joda.time.DateTime;
910
import org.joda.time.DateTimeZone;
@@ -32,6 +33,7 @@ public class AboutSettingsController {
3233
@Autowired private LogController logController;
3334
@Autowired private SettingsRepository settingsRepository;
3435
@Autowired private MailService mailService;
36+
@Autowired private ReleaseCheckService releaseCheckService;
3537

3638
// -------------------------------------------------------------------------
3739
// Paths
@@ -51,6 +53,7 @@ public String getAbout(Model model) {
5153
model.addAttribute("logFile", logController.getLogFilePath());
5254
model.addAttribute("systemTime", DateTime.now());
5355
model.addAttribute("systemTimeZone", DateTimeZone.getDefault());
56+
model.addAttribute("releaseReport", releaseCheckService.check());
5457
return "about";
5558
}
5659

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package de.rwth.idsg.steve.web.dto;
2+
3+
import lombok.Getter;
4+
import lombok.RequiredArgsConstructor;
5+
import lombok.Setter;
6+
import lombok.ToString;
7+
8+
/**
9+
* @author Sevket Goekay <goekay@dbis.rwth-aachen.de>
10+
* @since 19.04.2016
11+
*/
12+
@Getter
13+
@Setter
14+
@ToString
15+
@RequiredArgsConstructor
16+
public class ReleaseReport {
17+
private final boolean moreRecent;
18+
19+
private String githubVersion;
20+
21+
private String htmlUrl;
22+
private String downloadUrl;
23+
}

src/main/resources/webapp/WEB-INF/views/about.jsp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
<%@ include file="00-header.jsp" %>
22
<div class="content">
33
<section><span>About SteVe</span></section>
4+
<c:if test="${releaseReport.moreRecent}">
5+
<div class="info">A new version (${releaseReport.githubVersion}) is available!
6+
<a target="_blank" href="${releaseReport.htmlUrl}">Release Info</a> -
7+
<a target="_blank" href="${releaseReport.downloadUrl}">Download</a>
8+
</div>
9+
</c:if>
410
<table class="userInputFullPage">
511
<tr><td>Version:</td><td>${version}</td></tr>
612
<tr><td>Database Version:</td><td>${db.version}</td></tr>

0 commit comments

Comments
 (0)