Browse Source

Add a checker for nightly updates.

pull/674/head
Greg Holmes 8 years ago
parent
commit
fa3f83ed90

+ 1
- 0
build.gradle View File

@@ -43,6 +43,7 @@ dependencies {
43 43
     bundle group: 'net.kencochrane.raven', name: 'raven', version: '6.0.1-SNAPSHOT'
44 44
     bundle group: 'com.google.guava', name:'guava', version: '18.0'
45 45
     bundle group: 'net.engio', name: 'mbassador', version: '1.2.4'
46
+    bundle group: 'com.google.code.gson', name: 'gson', 'version': '2.5'
46 47
     bundle group: 'com.dmdirc', name: 'util', version: '+', changing: true
47 48
     bundle group: 'com.dmdirc.parser', name: 'common', version: '+', changing: true
48 49
 

+ 203
- 0
src/com/dmdirc/updater/checking/NightlyChecker.java View File

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

+ 6
- 0
test-res/com/dmdirc/updater/checking/nightlies.json View File

@@ -0,0 +1,6 @@
1
+[
2
+  { "name":"audio-0.8-1262-gc6376f4-SNAPSHOT.jar", "type":"file", "mtime":"Fri, 26 Feb 2016 00:13:49 GMT", "size":17861 },
3
+  { "name":"client-0.8.4-2046-g998b950-SNAPSHOT.jar", "type":"file", "mtime":"Fri, 26 Feb 2016 00:14:04 GMT", "size":7016852 },
4
+  { "name":"time-0.8-1296-ge579fda-SNAPSHOT.jar", "type":"file", "mtime":"Fri, 26 Feb 2016 00:13:45 GMT", "size":18769 },
5
+  { "name":"ui_swing-0.8-1314-g8583be9-SNAPSHOT.jar", "type":"file", "mtime":"Fri, 26 Feb 2016 00:13:58 GMT", "size":1038006 }
6
+]

+ 128
- 0
test/com/dmdirc/updater/checking/NightlyCheckerTest.java View File

@@ -0,0 +1,128 @@
1
+/*
2
+ * Copyright (c) 2006-2015 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.checking;
24
+
25
+import com.dmdirc.config.ConfigBinder;
26
+import com.dmdirc.interfaces.config.AggregateConfigProvider;
27
+import com.dmdirc.updater.UpdateComponent;
28
+import com.dmdirc.updater.Version;
29
+import com.dmdirc.util.io.Downloader;
30
+import com.dmdirc.util.io.FileUtils;
31
+
32
+import com.google.common.collect.Lists;
33
+
34
+import java.io.IOException;
35
+import java.nio.file.Files;
36
+import java.util.Map;
37
+
38
+import org.junit.Before;
39
+import org.junit.Test;
40
+import org.junit.runner.RunWith;
41
+import org.mockito.Mock;
42
+import org.mockito.runners.MockitoJUnitRunner;
43
+
44
+import static org.junit.Assert.assertEquals;
45
+import static org.mockito.Matchers.anyString;
46
+import static org.mockito.Mockito.when;
47
+
48
+@RunWith(MockitoJUnitRunner.class)
49
+public class NightlyCheckerTest {
50
+
51
+    private NightlyChecker instance;
52
+    @Mock private AggregateConfigProvider config;
53
+    @Mock private Downloader downloader;
54
+    @Mock private ConfigBinder configBinder;
55
+    @Mock private UpdateComponent componentOne;
56
+    @Mock private UpdateComponent componentTwo;
57
+    @Mock private UpdateComponent componentThree;
58
+    @Mock private UpdateComponent componentFour;
59
+
60
+    @Before
61
+    public void setUp() throws Exception {
62
+        when(config.getBinder()).thenReturn(configBinder);
63
+        when(downloader.getPage(anyString())).thenReturn(
64
+                Files.readAllLines(
65
+                        FileUtils.getPathForResource(getClass().getResource("nightlies.json"))));
66
+        when(componentOne.getName()).thenReturn("ui_swing");
67
+        when(componentTwo.getName()).thenReturn("client");
68
+        when(componentThree.getName()).thenReturn("time");
69
+        when(componentFour.getName()).thenReturn("audio");
70
+        when(componentOne.getVersion()).thenReturn(new Version("1.2"));
71
+        when(componentTwo.getVersion()).thenReturn(new Version("4.5"));
72
+        when(componentThree.getVersion()).thenReturn(new Version("0.1"));
73
+        when(componentFour.getVersion()).thenReturn(new Version("10"));
74
+        instance = new NightlyChecker(config, downloader);
75
+        instance.setChannel("NIGHTLY");
76
+    }
77
+
78
+    @Test
79
+    public void testUnknownChannel() throws Exception {
80
+        instance.setChannel("RANDOM");
81
+        final Map<UpdateComponent, UpdateCheckResult> updates =
82
+                instance.checkForUpdates(Lists.newArrayList(componentThree));
83
+        assertEquals(0, updates.size());
84
+    }
85
+
86
+    @Test
87
+    public void testNotNightly() throws Exception {
88
+        instance.setChannel("STABLE");
89
+        final Map<UpdateComponent, UpdateCheckResult> updates =
90
+                instance.checkForUpdates(Lists.newArrayList(componentThree));
91
+    }
92
+
93
+    @Test
94
+    public void testNightly() throws Exception {
95
+        final Map<UpdateComponent, UpdateCheckResult> updates =
96
+                instance.checkForUpdates(Lists.newArrayList(componentThree));
97
+        assertEquals(1, updates.size());
98
+    }
99
+
100
+    @Test
101
+    public void testNoUpdates() throws Exception {
102
+        final Map<UpdateComponent, UpdateCheckResult> updates = instance.checkForUpdates(
103
+                Lists.newArrayList(componentOne, componentTwo));
104
+        assertEquals(0, updates.size());
105
+    }
106
+
107
+    @Test
108
+    public void testOneUpdate() throws Exception {
109
+        final Map<UpdateComponent, UpdateCheckResult> updates = instance.checkForUpdates(
110
+                Lists.newArrayList(componentOne, componentThree));
111
+        assertEquals(1, updates.size());
112
+    }
113
+
114
+    @Test
115
+    public void testMultipleUpdate() throws Exception {
116
+        final Map<UpdateComponent, UpdateCheckResult> updates = instance.checkForUpdates(
117
+                Lists.newArrayList(componentOne, componentTwo, componentThree, componentFour));
118
+        assertEquals(2, updates.size());
119
+    }
120
+
121
+    @Test
122
+    public void testMalformedPage() throws Exception {
123
+        when(downloader.getPage(anyString())).thenThrow(new IOException("Failure."));
124
+        final Map<UpdateComponent, UpdateCheckResult> updates =
125
+                instance.checkForUpdates(Lists.newArrayList(componentThree));
126
+        assertEquals(0, updates.size());
127
+    }
128
+}

Loading…
Cancel
Save