java/com.sap.sailing.landscape.test/src/com/sap/sailing/landscape/test/TestReleaseRepository.java
... ...
@@ -16,6 +16,6 @@ public class TestReleaseRepository {
16 16
17 17
@Test
18 18
public void testForAtLeastOneMasterRelease() {
19
- assertNotNull(SailingReleaseRepository.INSTANCE.getLatestMasterRelease());
19
+ assertNotNull(SailingReleaseRepository.INSTANCE.getLatestDefaultRelease());
20 20
}
21 21
}
java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/client/LinkBuilder.java
... ...
@@ -2,6 +2,7 @@ package com.sap.sailing.landscape.ui.client;
2 2
3 3
import com.google.gwt.safehtml.shared.SafeHtml;
4 4
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
5
+import com.sap.sailing.landscape.SailingReleaseRepository;
5 6
import com.sap.sailing.landscape.ui.shared.SailingAnalyticsProcessDTO;
6 7
import com.sap.sailing.landscape.ui.shared.SailingApplicationReplicaSetDTO;
7 8
import com.sap.sse.common.Builder;
... ...
@@ -128,8 +129,7 @@ public class LinkBuilder implements Builder<LinkBuilder, SafeHtml> {
128 129
}
129 130
130 131
private String getReleaseNotesLink(final String version) {
131
- // TODO bug6173: can we figure this out from the ReleaseRepository, asking a Release specified by "version"? See Release.getReleaseNotesURL()
132
- return "https://github.com/SAP/sailing-analytics/releases/download/" + version + "/release-notes.txt";
132
+ return SailingReleaseRepository.INSTANCE.getRelease(version).getReleaseNotesURL().toString();
133 133
}
134 134
135 135
/**
java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/SailingReleaseRepository.java
... ...
@@ -4,5 +4,8 @@ import com.sap.sse.landscape.ReleaseRepository;
4 4
import com.sap.sse.landscape.impl.GithubReleasesRepository;
5 5
6 6
public interface SailingReleaseRepository extends ReleaseRepository {
7
- ReleaseRepository INSTANCE = new GithubReleasesRepository("https://github.com/SAP/sailing-analytics/releases/download/", /* main release name prefix */ "main");
7
+ ReleaseRepository INSTANCE = new GithubReleasesRepository(
8
+ "SAP", // owner
9
+ "sailing-analytics", // repo name
10
+ "main"); // main release name prefix
8 11
}
java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/impl/LandscapeServiceImpl.java
... ...
@@ -802,7 +802,7 @@ public class LandscapeServiceImpl implements LandscapeService {
802 802
@Override
803 803
public Release getRelease(String releaseNameOrNullForLatestMaster) {
804 804
return releaseNameOrNullForLatestMaster==null
805
- ? SailingReleaseRepository.INSTANCE.getLatestMasterRelease()
805
+ ? SailingReleaseRepository.INSTANCE.getLatestDefaultRelease()
806 806
: SailingReleaseRepository.INSTANCE.getRelease(releaseNameOrNullForLatestMaster);
807 807
}
808 808
java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/impl/SailingAnalyticsProcessImpl.java
... ...
@@ -38,7 +38,6 @@ import com.sap.sse.landscape.aws.ApplicationProcessHost;
38 38
import com.sap.sse.landscape.aws.AwsLandscape;
39 39
import com.sap.sse.landscape.aws.MongoUriParser;
40 40
import com.sap.sse.landscape.aws.impl.AwsApplicationProcessImpl;
41
-import com.sap.sse.landscape.impl.ReleaseImpl;
42 41
import com.sap.sse.landscape.mongodb.Database;
43 42
import com.sap.sse.shared.util.Wait;
44 43
import com.sap.sse.util.HttpUrlConnectionHelper;
... ...
@@ -102,7 +101,7 @@ implements SailingAnalyticsProcess<ShardingKey> {
102 101
private boolean updateReleaseFromStatus(JSONObject status) {
103 102
final boolean success;
104 103
if (status.containsKey(STATUS_RELEASE_PROPERTY_NAME)) {
105
- release = new ReleaseImpl((String) status.get(STATUS_RELEASE_PROPERTY_NAME), SailingReleaseRepository.INSTANCE);
104
+ release = SailingReleaseRepository.INSTANCE.getRelease((String) status.get(STATUS_RELEASE_PROPERTY_NAME));
106 105
success = true;
107 106
} else {
108 107
success = false;
java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/procedures/SailingAnalyticsApplicationConfiguration.java
... ...
@@ -33,7 +33,7 @@ extends AwsApplicationConfiguration<ShardingKey, SailingAnalyticsMetrics, Sailin
33 33
* <li>If no {@link #setTelnetPort(int) telnet port} is provided, the {@link #DEFAULT_TELNET_PORT} is used (14888).</li>
34 34
* <li>If no {@link #setExpeditionPort(int) expedition UDP port} is provided, the {@link #DEFAULT_EXPEDITION_PORT} is used (2010).</li>
35 35
* <li>If no {@link #setServerDirectory(String) server directory} is specified, it defaults to {@link ApplicationProcessHost#DEFAULT_SERVER_PATH}.</li>
36
- * <li>If no {@link #setRelease(Release) release} is specified, it defaults to {@link SailingReleaseRepository#getLatestMasterRelease()}.</li>
36
+ * <li>If no {@link #setRelease(Release) release} is specified, it defaults to {@link SailingReleaseRepository#getLatestDefaultRelease()}.</li>
37 37
* <li>The {@link DefaultProcessConfigurationVariables#ADDITIONAL_JAVA_ARGS} variable is extended by system properties that configure
38 38
* security, landscape data, and basic sailing master data to be shared across the {@link SharedLandscapeConstants#DEFAULT_DOMAIN_NAME} domain.</li>
39 39
* </ul>
... ...
@@ -144,7 +144,7 @@ extends AwsApplicationConfiguration<ShardingKey, SailingAnalyticsMetrics, Sailin
144 144
145 145
@Override
146 146
protected Optional<Release> getRelease() {
147
- return Optional.of(super.getRelease().orElse(SailingReleaseRepository.INSTANCE.getLatestMasterRelease()));
147
+ return Optional.of(super.getRelease().orElse(SailingReleaseRepository.INSTANCE.getLatestDefaultRelease()));
148 148
}
149 149
150 150
/**
java/com.sap.sse.landscape.aws.test/src/com/sap/sse/landscape/aws/ConnectivityTest.java
... ...
@@ -267,7 +267,7 @@ public class ConnectivityTest<ProcessT extends AwsApplicationProcess<String, Sai
267 267
}),
268 268
Optional.of(Tags.with("Name", "MyHost").and("Hello", "World")),
269 269
"MONGODB_URI=\""+mongoEndpoint.getURI(Optional.of(new DatabaseImpl(mongoEndpoint, "winddbTest")))+"\"",
270
- "INSTALL_FROM_RELEASE="+SailingReleaseRepository.INSTANCE.getLatestMasterRelease().getName(),
270
+ "INSTALL_FROM_RELEASE="+SailingReleaseRepository.INSTANCE.getLatestDefaultRelease().getName(),
271 271
"SERVER_NAME=\""+serverName+"\"");
272 272
try {
273 273
assertNotNull(host);
java/com.sap.sse.landscape.test/src/com/sap/sse/landscape/impl/TestFolderBasedReleaseRepository.java
... ...
@@ -0,0 +1,33 @@
1
+package com.sap.sse.landscape.impl;
2
+
3
+import static org.junit.jupiter.api.Assertions.assertEquals;
4
+import static org.junit.jupiter.api.Assertions.assertFalse;
5
+import static org.junit.jupiter.api.Assertions.assertNotNull;
6
+
7
+import org.junit.jupiter.api.BeforeAll;
8
+import org.junit.jupiter.api.Test;
9
+
10
+import com.sap.sse.common.Util;
11
+import com.sap.sse.landscape.Release;
12
+
13
+public class TestFolderBasedReleaseRepository {
14
+ private static final String MAIN = "main";
15
+ private static FolderBasedReleaseRepositoryImpl repository;
16
+
17
+ @BeforeAll
18
+ public static void setUp() {
19
+ repository = new FolderBasedReleaseRepositoryImpl("https://releases.sapsailing.com", MAIN);
20
+ }
21
+
22
+ @Test
23
+ public void testAtLeastOneRelease() {
24
+ assertFalse(Util.isEmpty(repository));
25
+ }
26
+
27
+ @Test
28
+ public void testAtLeastOneMainRelease() {
29
+ final Release latestMasterRelease = repository.getLatestDefaultRelease();
30
+ assertNotNull(latestMasterRelease);
31
+ assertEquals(MAIN, latestMasterRelease.getBaseName());
32
+ }
33
+}
java/com.sap.sse.landscape.test/src/com/sap/sse/landscape/impl/TestGithubReleaseRepository.java
... ...
@@ -0,0 +1,60 @@
1
+package com.sap.sse.landscape.impl;
2
+
3
+import static org.junit.jupiter.api.Assertions.assertEquals;
4
+import static org.junit.jupiter.api.Assertions.assertFalse;
5
+import static org.junit.jupiter.api.Assertions.assertNotNull;
6
+import static org.junit.jupiter.api.Assertions.assertNull;
7
+
8
+import org.junit.jupiter.api.BeforeAll;
9
+import org.junit.jupiter.api.Test;
10
+
11
+import com.sap.sse.common.Util;
12
+import com.sap.sse.landscape.Release;
13
+
14
+public class TestGithubReleaseRepository {
15
+ private static final String DOCKER_25 = "docker-25";
16
+ private static final String MAIN = "main";
17
+ private static GithubReleasesRepository repository;
18
+
19
+ @BeforeAll
20
+ public static void setUp() {
21
+ repository = new GithubReleasesRepository("SAP", "sailing-analytics", MAIN);
22
+ }
23
+
24
+ @Test
25
+ public void testAtLeastOneRelease() {
26
+ assertFalse(Util.isEmpty(repository));
27
+ }
28
+
29
+ @Test
30
+ public void testAtLeastOneMainRelease() {
31
+ final Release latestMasterRelease = repository.getLatestDefaultRelease();
32
+ assertNotNull(latestMasterRelease);
33
+ assertEquals(MAIN, latestMasterRelease.getBaseName());
34
+ }
35
+
36
+ @Test
37
+ public void testAtLeastOneDocker25Release() {
38
+ final Release latestDocker25Release = repository.getLatestRelease(DOCKER_25);
39
+ assertNotNull(latestDocker25Release);
40
+ assertEquals(DOCKER_25, latestDocker25Release.getBaseName());
41
+ }
42
+
43
+ @Test
44
+ public void testLinkToNextPageFromFirst() {
45
+ assertEquals("https://api.github.com/repositories/790295432/releases?page=2",
46
+ repository.getNextPageURL("<https://api.github.com/repositories/790295432/releases?page=2>; rel=\"next\", <https://api.github.com/repositories/790295432/releases?page=27>; rel=\"last\""));
47
+ }
48
+
49
+ @Test
50
+ public void testLinkToNextPageFromMiddleWith100PerPage() {
51
+ assertEquals("https://api.github.com/repositories/790295432/releases?per_page=100&page=5",
52
+ repository.getNextPageURL("<https://api.github.com/repositories/790295432/releases?per_page=100&page=3>; rel=\"prev\", <https://api.github.com/repositories/790295432/releases?per_page=100&page=5>; rel=\"next\", <https://api.github.com/repositories/790295432/releases?per_page=100&page=8>; rel=\"last\", <https://api.github.com/repositories/790295432/releases?per_page=100&page=1>; rel=\"first\""));
53
+ }
54
+
55
+ @Test
56
+ public void testLinkToNextPageFromLaste() {
57
+ assertNull(
58
+ repository.getNextPageURL("<https://api.github.com/repositories/790295432/releases?per_page=100&page=7>; rel=\"prev\", <https://api.github.com/repositories/790295432/releases?per_page=100&page=1>; rel=\"first\""));
59
+ }
60
+}
java/com.sap.sse.landscape/src/com/sap/sse/landscape/Release.java
... ...
@@ -1,9 +1,13 @@
1 1
package com.sap.sse.landscape;
2 2
3
-import java.net.MalformedURLException;
4 3
import java.net.URL;
4
+import java.text.ParseException;
5
+import java.text.SimpleDateFormat;
5 6
import java.util.HashMap;
6 7
import java.util.Map;
8
+import java.util.TimeZone;
9
+import java.util.logging.Level;
10
+import java.util.logging.Logger;
7 11
8 12
import com.sap.sse.common.Named;
9 13
import com.sap.sse.common.TimePoint;
... ...
@@ -15,26 +19,30 @@ import com.sap.sse.common.TimePoint;
15 19
*
16 20
*/
17 21
public interface Release extends UserDataProvider, Named {
22
+ Logger logger = Logger.getLogger(Release.class.getName());
23
+
18 24
String RELEASE_NOTES_FILE_NAME = "release-notes.txt";
19 25
String ARCHIVE_EXTENSION = ".tar.gz";
20 26
21
- ReleaseRepository getRepository();
22
-
23
- String getBaseName();
24
-
25
- TimePoint getCreationDate();
26
-
27
- default String getFolderURL() {
28
- return getRepository().getRepositoryBase()+"/"+getName()+"/";
27
+ default String getBaseName() {
28
+ return getName().substring(0, getName().lastIndexOf("-"));
29 29
}
30
-
31
- default URL getReleaseNotesURL() throws MalformedURLException {
32
- return new URL(getFolderURL()+RELEASE_NOTES_FILE_NAME);
30
+
31
+ default TimePoint getCreationDate() {
32
+ final String dateSubstring = getName().substring(getName().lastIndexOf("-")+1);
33
+ try {
34
+ final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmm");
35
+ simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
36
+ return TimePoint.of(simpleDateFormat.parse(dateSubstring));
37
+ } catch (ParseException e) {
38
+ logger.log(Level.WARNING, "Error parsing release date "+dateSubstring+". Returning null instead.", e);
39
+ return null;
40
+ }
33 41
}
42
+
43
+ URL getReleaseNotesURL();
34 44
35
- default URL getDeployableArchiveURL() throws MalformedURLException {
36
- return new URL(getFolderURL()+getName()+ARCHIVE_EXTENSION);
37
- }
45
+ URL getDeployableArchiveURL();
38 46
39 47
@Override
40 48
default Map<ProcessConfigurationVariable, String> getUserData() {
java/com.sap.sse.landscape/src/com/sap/sse/landscape/ReleaseRepository.java
... ...
@@ -12,8 +12,8 @@ public interface ReleaseRepository extends Iterable<Release> {
12 12
* @return the latest build with name prefix {@link #MASTER_RELEASE_NAME_PREFIX} if such a release exists in this
13 13
* repository, or {@code null} otherwise
14 14
*/
15
- default Release getLatestMasterRelease() {
16
- return getLatestRelease(getMainReleaseNamePrefix());
15
+ default Release getLatestDefaultRelease() {
16
+ return getLatestRelease(getDefaultReleaseNamePrefix());
17 17
}
18 18
19 19
/**
... ...
@@ -26,7 +26,5 @@ public interface ReleaseRepository extends Iterable<Release> {
26 26
*/
27 27
Release getRelease(String releaseName);
28 28
29
- String getRepositoryBase();
30
-
31
- String getMainReleaseNamePrefix();
29
+ String getDefaultReleaseNamePrefix();
32 30
}
java/com.sap.sse.landscape/src/com/sap/sse/landscape/application/impl/ApplicationProcessImpl.java
... ...
@@ -36,7 +36,6 @@ import com.sap.sse.landscape.RotatingFileBasedLog;
36 36
import com.sap.sse.landscape.application.ApplicationProcess;
37 37
import com.sap.sse.landscape.application.ApplicationProcessMetrics;
38 38
import com.sap.sse.landscape.impl.ProcessImpl;
39
-import com.sap.sse.landscape.impl.ReleaseImpl;
40 39
import com.sap.sse.landscape.ssh.SshCommandChannel;
41 40
import com.sap.sse.shared.util.Wait;
42 41
import com.sap.sse.util.HttpUrlConnectionHelper;
... ...
@@ -106,7 +105,7 @@ implements ApplicationProcess<ShardingKey, MetricsT, ProcessT> {
106 105
} else {
107 106
final Matcher matcher = pattern.matcher(versionTxt);
108 107
if (matcher.find()) {
109
- result = new ReleaseImpl(matcher.group(1), releaseRepository);
108
+ result = releaseRepository.getRelease(matcher.group(1));
110 109
} else {
111 110
result = null;
112 111
}
java/com.sap.sse.landscape/src/com/sap/sse/landscape/impl/AbstractRelease.java
... ...
@@ -0,0 +1,12 @@
1
+package com.sap.sse.landscape.impl;
2
+
3
+import com.sap.sse.common.impl.NamedImpl;
4
+import com.sap.sse.landscape.Release;
5
+
6
+public abstract class AbstractRelease extends NamedImpl implements Release {
7
+ private static final long serialVersionUID = 4872094283926485605L;
8
+
9
+ public AbstractRelease(String name) {
10
+ super(name);
11
+ }
12
+}
java/com.sap.sse.landscape/src/com/sap/sse/landscape/impl/AbstractReleaseRepository.java
... ...
@@ -7,23 +7,16 @@ import com.sap.sse.landscape.Release;
7 7
import com.sap.sse.landscape.ReleaseRepository;
8 8
9 9
public abstract class AbstractReleaseRepository implements ReleaseRepository {
10
- private final String mainReleaseNamePrefix;
11
- private final String repositoryBase;
10
+ private final String defaultReleaseNamePrefix;
12 11
13
- public AbstractReleaseRepository(String repositoryBase, String mainReleaseNamePrefix) {
12
+ public AbstractReleaseRepository(String defaultReleaseNamePrefix) {
14 13
super();
15
- this.repositoryBase = repositoryBase;
16
- this.mainReleaseNamePrefix = mainReleaseNamePrefix;
14
+ this.defaultReleaseNamePrefix = defaultReleaseNamePrefix;
17 15
}
18 16
19 17
@Override
20
- public String getRepositoryBase() {
21
- return repositoryBase;
22
- }
23
-
24
- @Override
25
- public String getMainReleaseNamePrefix() {
26
- return mainReleaseNamePrefix;
18
+ public String getDefaultReleaseNamePrefix() {
19
+ return defaultReleaseNamePrefix;
27 20
}
28 21
29 22
protected abstract Iterable<Release> getAvailableReleases();
java/com.sap.sse.landscape/src/com/sap/sse/landscape/impl/FolderBasedReleaseImpl.java
... ...
@@ -0,0 +1,44 @@
1
+package com.sap.sse.landscape.impl;
2
+
3
+import java.net.MalformedURLException;
4
+import java.net.URL;
5
+
6
+import com.sap.sse.landscape.Release;
7
+
8
+/**
9
+ * Collaborates with {@link FolderBasedReleaseRepositoryImpl}.
10
+ *
11
+ * @author Axel Uhl (d043530)
12
+ *
13
+ */
14
+public class FolderBasedReleaseImpl extends AbstractRelease implements Release {
15
+ private static final long serialVersionUID = -225240683033821028L;
16
+ private final String repositoryBase;
17
+
18
+ public FolderBasedReleaseImpl(String name, String repositoryBase) {
19
+ super(name);
20
+ this.repositoryBase = repositoryBase;
21
+ }
22
+
23
+ @Override
24
+ public URL getReleaseNotesURL() {
25
+ try {
26
+ return new URL(getDownloadFolderURL()+RELEASE_NOTES_FILE_NAME);
27
+ } catch (MalformedURLException e) {
28
+ throw new RuntimeException(e);
29
+ }
30
+ }
31
+
32
+ @Override
33
+ public URL getDeployableArchiveURL() {
34
+ try {
35
+ return new URL(getDownloadFolderURL()+getName()+ARCHIVE_EXTENSION);
36
+ } catch (MalformedURLException e) {
37
+ throw new RuntimeException(e);
38
+ }
39
+ }
40
+
41
+ private String getDownloadFolderURL() {
42
+ return repositoryBase+"/"+getName()+"/";
43
+ }
44
+}
java/com.sap.sse.landscape/src/com/sap/sse/landscape/impl/FolderBasedReleaseRepositoryImpl.java
... ...
@@ -0,0 +1,67 @@
1
+package com.sap.sse.landscape.impl;
2
+
3
+import java.io.ByteArrayOutputStream;
4
+import java.io.IOException;
5
+import java.io.InputStream;
6
+import java.net.URL;
7
+import java.net.URLConnection;
8
+import java.util.Collections;
9
+import java.util.LinkedList;
10
+import java.util.List;
11
+import java.util.logging.Level;
12
+import java.util.logging.Logger;
13
+import java.util.regex.Matcher;
14
+import java.util.regex.Pattern;
15
+
16
+import com.sap.sse.landscape.Release;
17
+import com.sap.sse.util.HttpUrlConnectionHelper;
18
+
19
+/**
20
+ * Assumes a simple folder exposed by a web server, such as Apache httpd, where the "repository base" references the
21
+ * folder which shows an "index" of the sub-folders in it, so that we can explore it. Each sub-folder is expected to be
22
+ * named after the corresponding release and must contain the release-notes.txt file (see
23
+ * {@link Release#RELEASE_NOTES_FILE_NAME}) as well as the .tar.gz file (see {@link Release#ARCHIVE_EXTENSION}) that
24
+ * represents the actual release file.
25
+ *
26
+ * @author Axel Uhl (d043530)
27
+ *
28
+ */
29
+public class FolderBasedReleaseRepositoryImpl extends AbstractReleaseRepository {
30
+ private static final Logger logger = Logger.getLogger(FolderBasedReleaseRepositoryImpl.class.getName());
31
+ private final String repositoryBase;
32
+
33
+ public FolderBasedReleaseRepositoryImpl(String repositoryBase, String defaultReleaseNamePrefix) {
34
+ super(defaultReleaseNamePrefix);
35
+ this.repositoryBase = repositoryBase;
36
+ }
37
+
38
+ private String getRepositoryBase() {
39
+ return repositoryBase;
40
+ }
41
+
42
+ protected Iterable<Release> getAvailableReleases() {
43
+ final List<Release> result = new LinkedList<>();
44
+ try {
45
+ final URLConnection connection = HttpUrlConnectionHelper.redirectConnection(new URL(getRepositoryBase()));
46
+ final InputStream index = (InputStream) connection.getContent();
47
+ int read = 0;
48
+ final ByteArrayOutputStream bos = new ByteArrayOutputStream();
49
+ while ((read=index.read()) != -1) {
50
+ bos.write(read);
51
+ }
52
+ index.close();
53
+ final String contents = bos.toString();
54
+ final Pattern pattern = Pattern.compile("<a href=\"(([^/]*)-([0-9]*))/\">([^/]*)-([0-9]*)/</a>");
55
+ final Matcher m = pattern.matcher(contents);
56
+ int lastMatch = 0;
57
+ while (m.find(lastMatch)) {
58
+ result.add(new FolderBasedReleaseImpl(m.group(1), getRepositoryBase()));
59
+ lastMatch = m.end();
60
+ }
61
+ } catch (IOException e) {
62
+ logger.log(Level.SEVERE, "Error loading releases from repository at "+getRepositoryBase()+". Continuing empty.", e);
63
+ return Collections.emptyList();
64
+ }
65
+ return result;
66
+ }
67
+}
java/com.sap.sse.landscape/src/com/sap/sse/landscape/impl/GithubRelease.java
... ...
@@ -0,0 +1,37 @@
1
+package com.sap.sse.landscape.impl;
2
+
3
+import java.net.MalformedURLException;
4
+import java.net.URL;
5
+
6
+import com.sap.sse.landscape.Release;
7
+
8
+public class GithubRelease extends AbstractRelease implements Release {
9
+ private static final long serialVersionUID = -8587383557709591724L;
10
+ private final String downloadURL;
11
+ private final String releaseNotesURL;
12
+
13
+ public GithubRelease(String name, String downloadURL, String releaseNotesURL) {
14
+ super(name);
15
+ this.downloadURL = downloadURL;
16
+ this.releaseNotesURL = releaseNotesURL;
17
+ }
18
+
19
+ @Override
20
+ public URL getReleaseNotesURL() {
21
+ try {
22
+ return new URL(releaseNotesURL);
23
+ } catch (MalformedURLException e) {
24
+ throw new RuntimeException(e);
25
+ }
26
+ }
27
+
28
+ @Override
29
+ public URL getDeployableArchiveURL() {
30
+ try {
31
+ return new URL(downloadURL);
32
+ } catch (MalformedURLException e) {
33
+ throw new RuntimeException(e);
34
+ }
35
+ }
36
+
37
+}
java/com.sap.sse.landscape/src/com/sap/sse/landscape/impl/GithubReleasesRepository.java
... ...
@@ -5,9 +5,14 @@ import java.io.InputStream;
5 5
import java.io.InputStreamReader;
6 6
import java.net.URL;
7 7
import java.net.URLConnection;
8
+import java.util.LinkedList;
9
+import java.util.List;
8 10
import java.util.logging.Logger;
11
+import java.util.regex.Matcher;
12
+import java.util.regex.Pattern;
9 13
10 14
import org.json.simple.JSONArray;
15
+import org.json.simple.JSONObject;
11 16
import org.json.simple.parser.JSONParser;
12 17
import org.json.simple.parser.ParseException;
13 18
... ...
@@ -15,26 +20,85 @@ import com.sap.sse.landscape.Release;
15 20
import com.sap.sse.landscape.ReleaseRepository;
16 21
import com.sap.sse.util.HttpUrlConnectionHelper;
17 22
23
+/**
24
+ * Assumes a public GitHub repository where releases can be freely downloaded from
25
+ * <code>https://github.com/{owner}/{repo}/releases/download/{release-name}</code>.
26
+ *
27
+ * @author Axel Uhl (d043530)
28
+ */
18 29
public class GithubReleasesRepository extends AbstractReleaseRepository implements ReleaseRepository {
19 30
private final static Logger logger = Logger.getLogger(GithubReleasesRepository.class.getName());
31
+ private final static String GITHUB_API_BASE_URL = "https://api.github.com";
32
+ private final static String GITHUB_BASE_URL = "https://github.com";
33
+ private final String owner;
34
+ private final String repositoryName;
20 35
21
- public GithubReleasesRepository(String repositoryBase, String mainReleaseNamePrefix) {
22
- super(repositoryBase, mainReleaseNamePrefix);
36
+ public GithubReleasesRepository(String owner, String repositoryName, String defaultReleaseNamePrefix) {
37
+ super(defaultReleaseNamePrefix);
38
+ this.owner = owner;
39
+ this.repositoryName = repositoryName;
40
+ }
41
+
42
+ private String getRepositoryPath() {
43
+ return owner+"/"+repositoryName;
44
+ }
45
+
46
+ private String getReleasesURL() {
47
+ return GITHUB_API_BASE_URL+"/repos/"+getRepositoryPath()+"/releases?per_page=100"; // TODO for unauthenticated requests there is a harsh rate limit of 60 requests per hour...
48
+ }
49
+
50
+ @Override
51
+ public Release getRelease(String releaseName) {
52
+ return new GithubRelease(releaseName, GITHUB_BASE_URL+"/"+getRepositoryPath()+"/releases/download/"+releaseName+"/"+releaseName+Release.ARCHIVE_EXTENSION,
53
+ GITHUB_BASE_URL+"/"+getRepositoryPath()+"/releases/download/"+releaseName+"/"+Release.RELEASE_NOTES_FILE_NAME);
23 54
}
24 55
25 56
@Override
26 57
protected Iterable<Release> getAvailableReleases() {
27
- URLConnection connection;
58
+ final List<Release> result = new LinkedList<>();
28 59
try {
29
- connection = HttpUrlConnectionHelper.redirectConnection(new URL(getRepositoryBase()));
30
- final InputStream index = (InputStream) connection.getContent();
31
- final JSONArray releasesJson = (JSONArray) new JSONParser().parse(new InputStreamReader(index));
32
- // TODO
33
- return null;
60
+ String nextPageURL = getReleasesURL();
61
+ do {
62
+ final URLConnection connection = HttpUrlConnectionHelper.redirectConnection(new URL(nextPageURL));
63
+ final InputStream index = (InputStream) connection.getContent();
64
+ final String linkHeader = connection.getHeaderField("link");
65
+ final JSONArray releasesJson = (JSONArray) new JSONParser().parse(new InputStreamReader(index));
66
+ addAllReleasesTo(releasesJson, result);
67
+ nextPageURL = getNextPageURL(linkHeader);
68
+ } while (nextPageURL != null);
34 69
} catch (IOException | ParseException e) {
35 70
logger.warning("Exception trying to find releases: "+e.getMessage());
36 71
}
37
- return null; // TODO
72
+ return result;
73
+ }
74
+
75
+ private void addAllReleasesTo(JSONArray releasesJson, List<Release> result) {
76
+ for (final Object releaseObject : releasesJson) {
77
+ final JSONObject releaseJson = (JSONObject) releaseObject;
78
+ final String name = releaseJson.get("name").toString();
79
+ String archiveDownloadURL = null;
80
+ String releaseNotesURL = null;
81
+ for (final Object archiveAsset : (JSONArray) releaseJson.get("assets")) {
82
+ final JSONObject archiveAssetJson = (JSONObject) archiveAsset;
83
+ if (archiveAssetJson.get("content_type").equals("application/x-tar")) {
84
+ archiveDownloadURL = archiveAssetJson.get("browser_download_url").toString();
85
+ } else if (archiveAssetJson.get("name").equals(Release.RELEASE_NOTES_FILE_NAME)) {
86
+ releaseNotesURL = archiveAssetJson.get("browser_download_url").toString();
87
+ }
88
+ }
89
+ result.add(new GithubRelease(name, archiveDownloadURL, releaseNotesURL));
90
+ }
38 91
}
39 92
93
+ private static final Pattern nextPagePattern = Pattern.compile(".*<([^<]*)>; rel=\"next\".*");
94
+ String getNextPageURL(String linkHeader) {
95
+ final String result;
96
+ final Matcher m = nextPagePattern.matcher(linkHeader);
97
+ if (m.matches()) {
98
+ result = m.group(1);
99
+ } else {
100
+ result = null;
101
+ }
102
+ return result;
103
+ }
40 104
}
java/com.sap.sse.landscape/src/com/sap/sse/landscape/impl/ReleaseImpl.java
... ...
@@ -1,47 +0,0 @@
1
-package com.sap.sse.landscape.impl;
2
-
3
-import java.text.ParseException;
4
-import java.text.SimpleDateFormat;
5
-import java.util.TimeZone;
6
-import java.util.logging.Level;
7
-import java.util.logging.Logger;
8
-
9
-import com.sap.sse.common.TimePoint;
10
-import com.sap.sse.common.impl.NamedImpl;
11
-import com.sap.sse.landscape.Release;
12
-import com.sap.sse.landscape.ReleaseRepository;
13
-
14
-public class ReleaseImpl extends NamedImpl implements Release {
15
- private static final Logger logger = Logger.getLogger(ReleaseImpl.class.getName());
16
- private static final long serialVersionUID = -225240683033821028L;
17
-
18
- private final ReleaseRepository repository;
19
-
20
- public ReleaseImpl(String name, ReleaseRepository repository) {
21
- super(name);
22
- this.repository = repository;
23
- }
24
-
25
- @Override
26
- public ReleaseRepository getRepository() {
27
- return repository;
28
- }
29
-
30
- @Override
31
- public String getBaseName() {
32
- return getName().substring(0, getName().lastIndexOf("-"));
33
- }
34
-
35
- @Override
36
- public TimePoint getCreationDate() {
37
- final String dateSubstring = getName().substring(getName().lastIndexOf("-")+1);
38
- try {
39
- final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmm");
40
- simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
41
- return TimePoint.of(simpleDateFormat.parse(dateSubstring));
42
- } catch (ParseException e) {
43
- logger.log(Level.WARNING, "Error parsing release date "+dateSubstring+". Returning null instead.", e);
44
- return null;
45
- }
46
- }
47
-}
java/com.sap.sse.landscape/src/com/sap/sse/landscape/impl/ReleaseRepositoryImpl.java
... ...
@@ -1,51 +0,0 @@
1
-package com.sap.sse.landscape.impl;
2
-
3
-import java.io.ByteArrayOutputStream;
4
-import java.io.IOException;
5
-import java.io.InputStream;
6
-import java.net.URL;
7
-import java.net.URLConnection;
8
-import java.util.Collections;
9
-import java.util.LinkedList;
10
-import java.util.List;
11
-import java.util.logging.Level;
12
-import java.util.logging.Logger;
13
-import java.util.regex.Matcher;
14
-import java.util.regex.Pattern;
15
-
16
-import com.sap.sse.landscape.Release;
17
-import com.sap.sse.util.HttpUrlConnectionHelper;
18
-
19
-public class ReleaseRepositoryImpl extends AbstractReleaseRepository {
20
- private static final Logger logger = Logger.getLogger(ReleaseRepositoryImpl.class.getName());
21
-
22
- public ReleaseRepositoryImpl(String repositoryBase, String mainReleaseNamePrefix) {
23
- super(repositoryBase, mainReleaseNamePrefix);
24
- }
25
-
26
- protected Iterable<Release> getAvailableReleases() {
27
- final List<Release> result = new LinkedList<>();
28
- try {
29
- final URLConnection connection = HttpUrlConnectionHelper.redirectConnection(new URL(getRepositoryBase()));
30
- final InputStream index = (InputStream) connection.getContent();
31
- int read = 0;
32
- final ByteArrayOutputStream bos = new ByteArrayOutputStream();
33
- while ((read=index.read()) != -1) {
34
- bos.write(read);
35
- }
36
- index.close();
37
- final String contents = bos.toString();
38
- final Pattern pattern = Pattern.compile("<a href=\"(([^/]*)-([0-9]*))/\">([^/]*)-([0-9]*)/</a>");
39
- final Matcher m = pattern.matcher(contents);
40
- int lastMatch = 0;
41
- while (m.find(lastMatch)) {
42
- result.add(new ReleaseImpl(m.group(1), this));
43
- lastMatch = m.end();
44
- }
45
- } catch (IOException e) {
46
- logger.log(Level.SEVERE, "Error loading releases from repository at "+getRepositoryBase()+". Continuing empty.", e);
47
- return Collections.emptyList();
48
- }
49
- return result;
50
- }
51
-}