package de.rwth.idsg.steve.service;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.github.zafarkhaja.semver.Version;
import de.rwth.idsg.steve.SteveConfiguration;
import de.rwth.idsg.steve.web.dto.ReleaseReport;
import de.rwth.idsg.steve.web.dto.ReleaseResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import javax.annotation.PostConstruct;
import java.io.File;
import java.util.Collections;
/**
* @author Sevket Goekay <goekay@dbis.rwth-aachen.de>
* @since 19.04.2016
*/
@Slf4j
public class GithubReleaseCheckService implements ReleaseCheckService {
/**
* If the Github api is slow to respond, we don't want the client of this class to wait forever (until the default
* timeout kicks in).
*/
private static final int API_TIMEOUT_IN_MILLIS = 4_000;
private static final String API_URL = "https://api.github.com/repos/RWTH-i5-IDSG/steve/releases/latest";
private static final String TAG_NAME_PREFIX = "steve-";
private static final String FILE_SEPARATOR = File.separator;
private RestTemplate restTemplate;
@PostConstruct
private void init() {
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setReadTimeout(API_TIMEOUT_IN_MILLIS);
factory.setConnectTimeout(API_TIMEOUT_IN_MILLIS);
ObjectMapper mapper = new ObjectMapper();
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper.setPropertyNamingStrategy(new PropertyNamingStrategy.SnakeCaseStrategy());
restTemplate = new RestTemplate(Collections.singletonList(new MappingJackson2HttpMessageConverter(mapper)));
restTemplate.setRequestFactory(factory);
}
@Override
public ReleaseReport check() {
try {
ReleaseResponse response = restTemplate.getForObject(API_URL, ReleaseResponse.class);
return getReport(response);
} catch (RestClientException e) {
// Fallback to "there is no new version atm".
// Probably because Github did not respond within the timeout.
return new ReleaseReport(false);
}
}
// -------------------------------------------------------------------------
// Private helpers
// -------------------------------------------------------------------------
private static ReleaseReport getReport(ReleaseResponse response) {
String githubVersion = extractVersion(response);
Version build = Version.valueOf(SteveConfiguration.CONFIG.getSteveVersion());
Version github = Version.valueOf(githubVersion);
boolean isGithubMoreRecent = github.greaterThan(build);
String downloadUrl = decideDownloadUrl(response);
ReleaseReport ur = new ReleaseReport(isGithubMoreRecent);
ur.setGithubVersion(githubVersion);
ur.setDownloadUrl(downloadUrl);
ur.setHtmlUrl(response.getHtmlUrl());
return ur;
}
private static String decideDownloadUrl(ReleaseResponse response) {
if (isWindows()) {
return response.getZipballUrl();
} else {
return response.getTarballUrl();
}
}
private static String extractVersion(ReleaseResponse response) {
return response.getTagName().replaceFirst(TAG_NAME_PREFIX, "");
}
/**
* A little bit hacky, but good-enough solution. We only need to find out the family of the os (whether unix
* or win). Therefore, we don't need full blown os detection, such as
*
* - https://github.com/apache/commons-lang/blob/master/src/main/java/org/apache/commons/lang3/SystemUtils.java
* - http://stackoverflow.com/a/24861219
*
* So, we base or decision on file.separator property. According to
* https://docs.oracle.com/javase/tutorial/essential/environment/sysprop.html,
* it is "/" on UNIX and "\" on Windows.
*/
private static boolean isWindows() {
return FILE_SEPARATOR.equals("\\");
}
}