50fa9267de1640651dc5b1a72ed5382a8478abca
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 | -} |