You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

NightlyChecker.java 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. /*
  2. * Copyright (c) 2006-2017 DMDirc Developers
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
  5. * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
  6. * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
  7. * permit persons to whom the Software is furnished to do so, subject to the following conditions:
  8. *
  9. * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
  10. * Software.
  11. *
  12. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  13. * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
  14. * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  15. * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  16. */
  17. package com.dmdirc.updater.checking;
  18. import com.dmdirc.config.binding.ConfigBinding;
  19. import com.dmdirc.config.GlobalConfig;
  20. import com.dmdirc.config.provider.AggregateConfigProvider;
  21. import com.dmdirc.updater.UpdateChannel;
  22. import com.dmdirc.updater.UpdateComponent;
  23. import com.dmdirc.updater.Version;
  24. import com.dmdirc.util.io.Downloader;
  25. import com.google.common.base.MoreObjects;
  26. import com.google.gson.Gson;
  27. import com.google.gson.reflect.TypeToken;
  28. import java.io.IOException;
  29. import java.net.MalformedURLException;
  30. import java.net.URL;
  31. import java.util.Collection;
  32. import java.util.Collections;
  33. import java.util.HashMap;
  34. import java.util.List;
  35. import java.util.Map;
  36. import java.util.Objects;
  37. import java.util.function.Function;
  38. import java.util.regex.Matcher;
  39. import java.util.regex.Pattern;
  40. import java.util.stream.Collectors;
  41. import javax.inject.Inject;
  42. import org.slf4j.Logger;
  43. import org.slf4j.LoggerFactory;
  44. /**
  45. * Nightly update checker.
  46. */
  47. public class NightlyChecker implements UpdateCheckStrategy {
  48. private static final Logger LOG = LoggerFactory.getLogger(NightlyChecker.class);
  49. /** Name matching regex. */
  50. private final Pattern pattern = Pattern.compile(
  51. "^(.*?)-([^-]+(-[0-9]+-g[0-9a-f]+)?)(-SNAPSHOT)?\\.jar$");
  52. /** The URL to request to check for updates. */
  53. private static final String UPDATE_URL = "https://nightlies.dmdirc.com/json/latest";
  54. /** The update channel to check for updates on. */
  55. private UpdateChannel channel;
  56. /** Downloader to download files. */
  57. private final Downloader downloader;
  58. /**
  59. * Creates a new instance of {@link NightlyChecker}.
  60. *
  61. * @param configProvider The provider to use to retrieve update channel information.
  62. * @param downloader Used to download files
  63. */
  64. @Inject
  65. public NightlyChecker(@GlobalConfig final AggregateConfigProvider configProvider,
  66. final Downloader downloader) {
  67. configProvider.getBinder().bind(this, NightlyChecker.class);
  68. this.downloader = downloader;
  69. }
  70. /**
  71. * Sets the channel which will be used by the {@link NightlyChecker}.
  72. *
  73. * @param channel The new channel to use
  74. */
  75. @ConfigBinding(domain = "updater", key = "channel")
  76. public void setChannel(final String channel) {
  77. LOG.info("Changing channel to {}", channel);
  78. try {
  79. this.channel = UpdateChannel.valueOf(channel.toUpperCase());
  80. } catch (IllegalArgumentException ex) {
  81. this.channel = null;
  82. LOG.warn("Unknown channel {}", channel, ex);
  83. }
  84. }
  85. @Override
  86. public Map<UpdateComponent, UpdateCheckResult> checkForUpdates(
  87. final Collection<UpdateComponent> components) {
  88. if (channel != UpdateChannel.NIGHTLY) {
  89. LOG.info("Channel {} is not nightly, aborting", channel);
  90. return Collections.emptyMap();
  91. }
  92. LOG.info("Retrieving latest versions.");
  93. final List<NightlyResult> resultsList = new Gson().fromJson(getJson(),
  94. new TypeToken<List<NightlyResult>>(){}.getType());
  95. if (resultsList == null) {
  96. return Collections.emptyMap();
  97. }
  98. resultsList.stream()
  99. .filter(Objects::nonNull) //This is incase the JSON is broken
  100. .forEach(e -> {
  101. final Matcher matcher = pattern.matcher(e.getName());
  102. if (matcher.matches()) {
  103. e.setOtherName(matcher.group(1));
  104. e.setVersion(new Version(matcher.group(2)));
  105. e.setUrl(UPDATE_URL + '/' + e.getName());
  106. }
  107. });
  108. final Map<String, NightlyResult> resultsMap = resultsList.stream()
  109. .collect(Collectors.toMap(NightlyResult::getOtherName, Function.identity()));
  110. final Map<UpdateComponent, UpdateCheckResult> returns = new HashMap<>();
  111. components.forEach(e -> {
  112. if (resultsMap.containsKey(e.getName())) {
  113. if (resultsMap.get(e.getName()).getVersion().compareTo(e.getVersion()) > 0) {
  114. final String name = e.getName();
  115. final NightlyResult result = resultsMap.get(e.getName());
  116. try {
  117. returns.put(e, new BaseDownloadableResult(e, getURL(result),
  118. result.getOtherName(), result.getVersion()));
  119. } catch (MalformedURLException e1) {
  120. LOG.error("Unable to create a URL for {}", name);
  121. }
  122. LOG.info("Updating {} from {} to {}", e.getName(), e.getVersion(),
  123. resultsMap.get(e.getName()).getVersion());
  124. } else {
  125. LOG.info("Not updating {} from {} to {}", e.getName(), e.getVersion(),
  126. resultsMap.get(e.getName()).getVersion());
  127. }
  128. }
  129. });
  130. return returns;
  131. }
  132. private URL getURL(final NightlyResult result) throws MalformedURLException {
  133. return new URL(result.getUrl());
  134. }
  135. private String getJson() {
  136. try {
  137. return downloader.getPage(UPDATE_URL).stream().map(String::toString)
  138. .collect(Collectors.joining("\r\n"));
  139. } catch (IOException e) {
  140. LOG.warn("Error when getting update page: {}", e.getMessage());
  141. return "";
  142. }
  143. }
  144. /**
  145. * Wrapper class for GSON to deserialise the JSON.
  146. */
  147. private static class NightlyResult {
  148. private final String name;
  149. private final String type;
  150. private final String mtime;
  151. private final int size;
  152. private String otherName;
  153. private Version version;
  154. private String url;
  155. NightlyResult(final String name, final String type, final String mtime,
  156. final int size) {
  157. this.name = name;
  158. this.type = type;
  159. this.mtime = mtime;
  160. this.size = size;
  161. }
  162. String getName() {
  163. return name;
  164. }
  165. String getUrl() {
  166. return url;
  167. }
  168. String getOtherName() {
  169. return otherName;
  170. }
  171. void setOtherName(final String otherName) {
  172. this.otherName = otherName;
  173. }
  174. Version getVersion() {
  175. return version;
  176. }
  177. void setVersion(final Version version) {
  178. this.version = version;
  179. }
  180. void setUrl(final String url) {
  181. this.url = url;
  182. }
  183. public String toString() {
  184. return MoreObjects.toStringHelper(NightlyChecker.class)
  185. .add("name", otherName)
  186. .add("version", version)
  187. .toString();
  188. }
  189. }
  190. }