result = new HashMap<>();
+ result.put(ProcessConfigurationVariable.REPLICATE_MASTER_USERNAME, username);
+ result.put(ProcessConfigurationVariable.REPLICATE_MASTER_PASSWORD, password);
+ return result;
+ }
+}
diff --git a/java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/procedures/DeployProcessOnMultiServer.java b/java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/procedures/DeployProcessOnMultiServer.java
new file mode 100644
index 00000000000..8684fd72319
--- /dev/null
+++ b/java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/procedures/DeployProcessOnMultiServer.java
@@ -0,0 +1,75 @@
+package com.sap.sailing.landscape.procedures;
+
+import com.sap.sailing.landscape.SailingAnalyticsHost;
+import com.sap.sailing.landscape.SailingAnalyticsMaster;
+import com.sap.sailing.landscape.SailingAnalyticsMetrics;
+import com.sap.sailing.landscape.SailingAnalyticsProcess;
+import com.sap.sailing.landscape.SailingAnalyticsReplica;
+import com.sap.sse.landscape.Landscape;
+import com.sap.sse.landscape.orchestration.Procedure;
+
+/**
+ * Deploys a single {@link SailingAnalyticsProcess} to a given {@link SailingAnalyticsHost} which ideally has been
+ * launched using the {@link StartMultiServer} procedure, but may also work for hosts started in another way.
+ *
+ * TODO This is similar to {@link StartSailingAnalyticsHost} but doesn't need to fire up a new host. The assembly
+ * of user data that {@link StartSailingAnalyticsHost} implements shall be factored and used here as well to append
+ * to a barebones {@code env.sh} as obtained from a trivial installation. This trivial installation would start by
+ * creating the {@link #serverName} folder under {@code /home/sailing/servers}, then copy the {@code refreshInstance.sh}
+ * script there, run {@code ./refreshInstance.sh install-release}, then {@code ./refreshInstance.sh install-env environment-name},
+ * then append specific variable values to the end of the {@code env.sh} file. In both cases, a set of variable assignments
+ * needs to be generated which becomes the "user data" for {@link StartSailingAnalyticsHost} and becomes an appendix to
+ * {@code env.sh} in this case.
+ *
+ * @author Axel Uhl (D043530)
+ *
+ * @param
+ * @param
+ */
+public class DeployProcessOnMultiServer> implements
+ Procedure, SailingAnalyticsReplica> {
+ private final Landscape, SailingAnalyticsReplica> landscape;
+ private final SailingAnalyticsHost hostToDeployTo;
+ private final String serverName;
+
+ /**
+ * The process launched by this procedure. {@link #hostToDeployTo} is expected to be identical to
+ * {@link SailingAnalyticsProcess#getHost() process.getHost()}.
+ */
+ private SailingAnalyticsProcess process;
+
+ public DeployProcessOnMultiServer(Landscape, SailingAnalyticsReplica> landscape,
+ SailingAnalyticsHost hostToDeployTo, String serverName) {
+ super();
+ this.landscape = landscape;
+ this.hostToDeployTo = hostToDeployTo;
+ this.serverName = serverName;
+ }
+
+ @Override
+ public void run() {
+ // TODO Implement Runnable.run(...)
+
+ }
+
+ public SailingAnalyticsProcess getProcess() {
+ return process;
+ }
+
+ public void setProcess(SailingAnalyticsProcess process) {
+ this.process = process;
+ }
+
+ public SailingAnalyticsHost getHostToDeployTo() {
+ return hostToDeployTo;
+ }
+
+ public String getServerName() {
+ return serverName;
+ }
+
+ @Override
+ public Landscape, SailingAnalyticsReplica> getLandscape() {
+ return landscape;
+ }
+}
diff --git a/java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/procedures/StartMultiServer.java b/java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/procedures/StartMultiServer.java
new file mode 100644
index 00000000000..dbbe3f75764
--- /dev/null
+++ b/java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/procedures/StartMultiServer.java
@@ -0,0 +1,91 @@
+package com.sap.sailing.landscape.procedures;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Optional;
+import java.util.logging.Logger;
+
+import com.sap.sse.common.Duration;
+import com.sap.sse.landscape.application.ApplicationMasterProcess;
+import com.sap.sse.landscape.application.ApplicationProcessMetrics;
+import com.sap.sse.landscape.application.ApplicationReplicaProcess;
+import com.sap.sse.landscape.aws.AwsInstance;
+import com.sap.sse.landscape.ssh.SshCommandChannel;
+
+import software.amazon.awssdk.services.ec2.model.InstanceType;
+
+/**
+ * Starts an empty multi-server. The image will cause a {@code /home/sailing/servers/server} directory to exist, but
+ * after successfully launching, that directory will be removed. A {@link DeployProcessOnMultiServer} procedure needs to
+ * be run with the {@link #getHost()} of this procedure telling the host on which to deploy the process.
+ *
+ * The implementation specializes the {@link UpgradeAmi} procedure in {@link Builder#setNoShutdown(boolean) no-shutdown} mode.
+ * After running that part, the {@code httpd} service is launched.
+ *
+ * You want to at least specify an {@link Builder#setInstanceName(String) instance name} and {@link Builder#setInstanceType(InstanceType)}.
+ *
+ * @author Axel Uhl (D043530)
+ *
+ * @param
+ * @param
+ */
+public class StartMultiServer,
+ReplicaProcessT extends ApplicationReplicaProcess,
+HostT extends AwsInstance>
+extends UpgradeAmi {
+ private static final Logger logger = Logger.getLogger(StartMultiServer.class.getName());
+ private Optional optionalTimeout;
+
+ /**
+ * Under all circumstances, this builder will return {@code true} for {@link #isNoShutdown()}, making sure
+ * that after the upgrade progress the server does not try to re-boot.
+ *
+ * @author Axel Uhl (D043530)
+ */
+ public static interface Builder,
+ ReplicaProcessT extends ApplicationReplicaProcess,
+ HostT extends AwsInstance>
+ extends UpgradeAmi.Builder {
+ @Override
+ default boolean isNoShutdown() {
+ return true;
+ }
+ }
+
+ protected static class BuilderImpl,
+ ReplicaProcessT extends ApplicationReplicaProcess,
+ HostT extends AwsInstance>
+ extends UpgradeAmi.BuilderImpl
+ implements Builder {
+ @Override
+ public StartMultiServer build() {
+ return new StartMultiServer<>(this);
+ }
+ }
+
+ public static ,
+ ReplicaProcessT extends ApplicationReplicaProcess,
+ HostT extends AwsInstance> Builder builder() {
+ return new BuilderImpl<>();
+ }
+
+ protected StartMultiServer(Builder builder) {
+ super(builder);
+ this.optionalTimeout = builder.getOptionalTimeout();
+ }
+
+ @Override
+ public void run() throws Exception {
+ super.run();
+ final SshCommandChannel sshCommandChannel = getHost().createRootSshChannel(optionalTimeout);
+ final ByteArrayOutputStream stderr = new ByteArrayOutputStream();
+ sshCommandChannel.sendCommandLineSynchronously("service httpd start", stderr);
+ final String instanceId = getHost().getInstanceId();
+ logger.info("stdout for starting httpd service on instance "+instanceId+": "+sshCommandChannel.getStreamContentsAsString());
+ logger.info("stderr for starting httpd service on instance \"+instanceId+\": "+stderr.toString());
+ logger.info("exit status for starting httpd service on instance \"+instanceId+\": "+sshCommandChannel.getExitStatus());
+ }
+}
diff --git a/java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/procedures/StartSailingAnalyticsHost.java b/java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/procedures/StartSailingAnalyticsHost.java
new file mode 100644
index 00000000000..eb4fc2afa77
--- /dev/null
+++ b/java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/procedures/StartSailingAnalyticsHost.java
@@ -0,0 +1,73 @@
+package com.sap.sailing.landscape.procedures;
+
+import java.util.Optional;
+
+import com.sap.sailing.landscape.SailingAnalyticsHost;
+import com.sap.sailing.landscape.SailingAnalyticsMaster;
+import com.sap.sailing.landscape.SailingAnalyticsMetrics;
+import com.sap.sailing.landscape.SailingAnalyticsReplica;
+import com.sap.sailing.landscape.SailingReleaseRepository;
+import com.sap.sse.landscape.Release;
+import com.sap.sse.landscape.aws.impl.AmazonMachineImage;
+import com.sap.sse.landscape.aws.orchestration.StartAwsHost;
+import com.sap.sse.landscape.orchestration.Procedure;
+
+/**
+ * TODO handle the naming problem: base name drives instance "Name" tag generation ("SL ... (Master)"), exchange name,
+ * database name and SERVER_NAME. When moving up the inheritance hierarchy, name is interpreted in some places as the instance name
+ * which obviously doesn't equal the "Name" tag value. So, we have to clearly distinguish these.
+ *
+ * @author Axel Uhl (D043530)
+ *
+ * @param
+ */
+public abstract class StartSailingAnalyticsHost>
+extends StartAwsHost, SailingAnalyticsReplica, HostT>
+implements Procedure, SailingAnalyticsReplica> {
+ private final static String IMAGE_TYPE_TAG_VALUE_SAILING = "sailing-analytics-server";
+ private final static String INSTANCE_NAME_DEFAULT_PREFIX = "SL ";
+
+ /**
+ * The following defaults, in addition to the defaults implemented by the more general {@link StartAwsHost.Builder},
+ * are:
+ *
+ * - If no {@link #setInstanceName(String) instance name} is provided, the instance name is constructed from the {@link #getServerName() server name}
+ * by pre-pending the prefix "SL ".
+ * - Uses the latest machine image of the type described by
+ * {@link StartSailingAnalyticsHost#IMAGE_TYPE_TAG_VALUE_SAILING} if no explicit {@link #setMachineImage(AmazonMachineImage) machine image is set}
+ * and no {@link #setImageType(String) image type is set} of which the latest version would be used otherwise.
+ * - If no {@link Release} is explicitly {@link #setRelease set}, or that {@link Optional} is empty,
+ * {@link SailingReleaseRepository#INSTANCE}{@link SailingReleaseRepository#getLatestMasterRelease()
+ * getLatestMasterRelease()} will be used instead.
+ *
+ *
+ * @author Axel Uhl (D043530)
+ */
+ public static interface Builder, ShardingKey, HostT extends SailingAnalyticsHost>
+ extends StartAwsHost.Builder, SailingAnalyticsReplica, HostT> {
+ }
+
+ protected abstract static class BuilderImpl, ShardingKey, HostT extends SailingAnalyticsHost>
+ extends StartAwsHost.BuilderImpl, SailingAnalyticsReplica, HostT>
+ implements Builder {
+ @Override
+ public String getImageType() {
+ return super.getImageType() == null ? StartSailingAnalyticsHost.IMAGE_TYPE_TAG_VALUE_SAILING : super.getImageType();
+ }
+
+ @Override
+ public Optional getRelease() {
+ return Optional.of(super.getRelease().orElse(SailingReleaseRepository.INSTANCE.getLatestMasterRelease()));
+ }
+
+ @Override
+ public String getInstanceName() {
+ return super.getInstanceName() == null ? INSTANCE_NAME_DEFAULT_PREFIX+getServerName() : super.getInstanceName();
+ }
+ }
+
+ protected StartSailingAnalyticsHost(Builder extends StartSailingAnalyticsHost, ShardingKey, HostT> builder) {
+ super(builder);
+ }
+}
diff --git a/java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/procedures/StartSailingAnalyticsMaster.java b/java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/procedures/StartSailingAnalyticsMaster.java
new file mode 100644
index 00000000000..b9ce06b4427
--- /dev/null
+++ b/java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/procedures/StartSailingAnalyticsMaster.java
@@ -0,0 +1,44 @@
+package com.sap.sailing.landscape.procedures;
+
+import com.sap.sailing.landscape.SailingAnalyticsHost;
+import com.sap.sse.landscape.ProcessConfigurationVariable;
+
+/**
+ * This procedure does two things: it {@link StartSailingAnalyticsHost starts} a {@link SailingAnalyticsHost}, and
+ * (currently implicitly, based on the way the /etc/init.d/sailing script works) also starts a "master" process
+ * that is expected to have the default working directory {@code /home/sailing/servers/server}.
+ *
+ * TODO What we should probably be doing instead is harmonize the way the set-up / launching of a regular default master
+ * works with how a {@link StartMultiServer multi-server is started}. We could start both empty, with only the default
+ * reverse proxy mappings for {@code internal-server-status} and the plain access through the {@code ec2-...} hostname.
+ * From there on, all process launching and stopping would work through the {@link DeployProcessOnMultiServer} procedure
+ * (which then should be renamed to {@code DeployApplicationProcessOnServer}). That procedure then would have the
+ *
+ * @author Axel Uhl (D043530)
+ */
+public class StartSailingAnalyticsMaster
+ extends StartSailingAnalyticsHost> {
+ public static interface Builder
+ extends StartSailingAnalyticsHost.Builder, ShardingKey, SailingAnalyticsHost> {
+ }
+
+ // TODO model an AwsLandscape subclass describing the specifics of the Sailing landscape, with a central security service that a master replicates by default
+
+ protected static class BuilderImpl
+ extends StartSailingAnalyticsHost.BuilderImpl, ShardingKey, SailingAnalyticsHost>
+ implements Builder {
+ @Override
+ public StartSailingAnalyticsMaster build() {
+ return new StartSailingAnalyticsMaster(this);
+ }
+ }
+
+ public static Builder builder() {
+ return new BuilderImpl<>();
+ }
+
+ protected StartSailingAnalyticsMaster(Builder builder) {
+ super(builder);
+ addUserData(ProcessConfigurationVariable.USE_ENVIRONMENT, "live-master-server"); // TODO maybe this should be handled by this procedure adding the correct defaults, e.g., for replicating security/sharedsailing?
+ }
+}
diff --git a/java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/procedures/StartSailingAnalyticsReplica.java b/java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/procedures/StartSailingAnalyticsReplica.java
new file mode 100644
index 00000000000..d2b12e64191
--- /dev/null
+++ b/java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/procedures/StartSailingAnalyticsReplica.java
@@ -0,0 +1,29 @@
+package com.sap.sailing.landscape.procedures;
+
+import com.sap.sailing.landscape.SailingAnalyticsHost;
+import com.sap.sse.landscape.ProcessConfigurationVariable;
+
+public class StartSailingAnalyticsReplica
+ extends StartSailingAnalyticsHost> {
+ public static interface Builder
+ extends StartSailingAnalyticsHost.Builder, ShardingKey, SailingAnalyticsHost> {
+ }
+
+ protected static class BuilderImpl
+ extends StartSailingAnalyticsHost.BuilderImpl, ShardingKey, SailingAnalyticsHost>
+ implements Builder {
+ @Override
+ public StartSailingAnalyticsReplica build() {
+ return new StartSailingAnalyticsReplica(this);
+ }
+ }
+
+ public static Builder builder() {
+ return new BuilderImpl<>();
+ }
+
+ protected StartSailingAnalyticsReplica(Builder builder) {
+ super(builder);
+ addUserData(ProcessConfigurationVariable.USE_ENVIRONMENT, "live-replica-server"); // TODO maybe this should be handled by this procedure adding the correct defaults, e.g., for replicating security/sharedsailing?
+ }
+}
diff --git a/java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/procedures/UpgradeAmi.java b/java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/procedures/UpgradeAmi.java
new file mode 100644
index 00000000000..7f407ae2e91
--- /dev/null
+++ b/java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/procedures/UpgradeAmi.java
@@ -0,0 +1,114 @@
+package com.sap.sailing.landscape.procedures;
+
+import java.util.Collections;
+
+import com.sap.sse.landscape.MachineImage;
+import com.sap.sse.landscape.application.ApplicationMasterProcess;
+import com.sap.sse.landscape.application.ApplicationProcessMetrics;
+import com.sap.sse.landscape.application.ApplicationReplicaProcess;
+import com.sap.sse.landscape.aws.AwsInstance;
+import com.sap.sse.landscape.aws.orchestration.StartAwsHost;
+import com.sap.sse.landscape.orchestration.Procedure;
+
+/**
+ * Upgrades an existing Amazon Machine Image that is expected to be prepared for such an upgrade, by
+ * invoking it with very specific user data that trigger the automatic upgrade. The resulting AMI can
+ * be obtained after this procedure has completed by calling {@link #getUpgradedAmi()}.
+ *
+ * @author Axel Uhl (D043530)
+ *
+ * @param
+ * @param
+ */
+public class UpgradeAmi,
+ReplicaProcessT extends ApplicationReplicaProcess,
+HostT extends AwsInstance>
+extends StartAwsHost
+implements Procedure {
+ private static final String IMAGE_UPGRADE_USER_DATA = "image-upgrade";
+ private static final String NO_SHUTDOWN_USER_DATA = "no-shutdown";
+
+ private MachineImage upgradedAmi;
+
+ /**
+ * Additional default rules in addition to what the {@link StartAwsHost.Builder parent builder} defines:
+ *
+ *
+ * - If no {@link #getInstanceName() instance name} is set, the default instance name will be constructed as
+ * {@code IMAGE_UPGRADE+" for "+machineImage.getId()}
+ * - The user data are set to the string defined by {@link UpgradeAmi#IMAGE_UPGRADE_USER_DATA}, forcing the image to
+ * boot without trying to launch a process instance.
+ *
+ * @author Axel Uhl (D043530)
+ */
+ public static interface Builder,
+ ReplicaProcessT extends ApplicationReplicaProcess,
+ HostT extends AwsInstance>
+ extends StartAwsHost.Builder, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> {
+ boolean isNoShutdown();
+
+ Builder setNoShutdown(boolean noShutdown);
+ }
+
+ protected static class BuilderImpl,
+ ReplicaProcessT extends ApplicationReplicaProcess,
+ HostT extends AwsInstance>
+ extends StartAwsHost.BuilderImpl, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
+ implements Builder {
+ private boolean noShutdown;
+
+ @Override
+ public UpgradeAmi build() {
+ return new UpgradeAmi<>(this);
+ }
+
+ @Override
+ public boolean isNoShutdown() {
+ return noShutdown;
+ }
+
+ @Override
+ public Builder setNoShutdown(boolean noShutdown) {
+ this.noShutdown = noShutdown;
+ return this;
+ }
+
+ @Override
+ public String getInstanceName() {
+ return super.getInstanceName() == null ? IMAGE_UPGRADE_USER_DATA+" for "+getMachineImage().getId() : super.getInstanceName();
+ }
+ }
+
+ public static ,
+ ReplicaProcessT extends ApplicationReplicaProcess,
+ HostT extends AwsInstance> Builder builder() {
+ return new BuilderImpl<>();
+ }
+
+ public UpgradeAmi(Builder builder) {
+ super(builder);
+ addUserData(Collections.singleton(IMAGE_UPGRADE_USER_DATA));
+ if (builder.isNoShutdown()) {
+ addUserData(Collections.singleton(NO_SHUTDOWN_USER_DATA));
+ }
+ }
+
+ @Override
+ public void run() throws Exception {
+ super.run(); // launches the machine in upgrade mode and shuts it down again, preparing for AMI creation
+ // TODO now comes the waiting for the shutdown and initiating the creation of an AMI for the instance
+ // TODO then comes the tagging of the volume snapshots created
+ // TODO then tag the resulting AMI according to the original image's tags, except for the name where automatic version number increment should be implemented
+ }
+
+ /**
+ * @return the resulting AMI that has the upgraded version of everything
+ */
+ public MachineImage getUpgradedAmi() {
+ return upgradedAmi;
+ }
+}
diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/MongoClientURITest.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/MongoClientURITest.java
index 39385c06732..ecf88b9c1bb 100755
--- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/MongoClientURITest.java
+++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/MongoClientURITest.java
@@ -25,5 +25,6 @@ public class MongoClientURITest {
assertEquals(12345, config.getPort());
assertEquals("mydb", config.getDatabaseName());
assertSame(ReadPreference.primary(), config.getMongoClientURI().getOptions().getReadPreference());
+ assertEquals("rs0", config.getMongoClientURI().getOptions().getRequiredReplicaSetName());
}
}
diff --git a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF
index 38c0ee75115..76e6fd4fe6b 100644
--- a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF
+++ b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF
@@ -41,9 +41,9 @@ Import-Package:
javax.ws.rs.core;version="1.1.1",
javax.xml.bind,
org.apache.http,
- org.apache.http.client;version="[4.2.6,4.3.0)",
- org.apache.http.client.methods;version="[4.2.6,4.3.0)",
- org.apache.http.impl.client;version="[4.2.6,4.3.0)",
+ org.apache.http.client;version="4.5.5",
+ org.apache.http.client.methods;version="4.5.5",
+ org.apache.http.impl.client;version="4.5.5",
org.json.simple,
org.json.simple.parser
Web-ContextPath: /polars
diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/jaxrs/client/PolarDataClient.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/jaxrs/client/PolarDataClient.java
index 9137d1e28b7..dec7b2a56f5 100644
--- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/jaxrs/client/PolarDataClient.java
+++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/jaxrs/client/PolarDataClient.java
@@ -8,7 +8,7 @@ import java.util.logging.Logger;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.SystemDefaultHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
import org.json.simple.parser.ParseException;
import com.sap.sailing.polars.ReplicablePolarService;
@@ -69,7 +69,7 @@ public class PolarDataClient {
}
protected InputStream getContentFromResponse() throws IOException, ParseException {
- HttpClient client = new SystemDefaultHttpClient();
+ HttpClient client = HttpClientBuilder.create().build();
HttpGet getProcessor = new HttpGet(getAPIString());
HttpResponse processorResponse = client.execute(getProcessor);
return processorResponse.getEntity().getContent();
diff --git a/java/com.sap.sailing.selenium.test/.gitignore b/java/com.sap.sailing.selenium.test/.gitignore
index ae3c1726048..e2b7ea322ea 100644
--- a/java/com.sap.sailing.selenium.test/.gitignore
+++ b/java/com.sap.sailing.selenium.test/.gitignore
@@ -1 +1,2 @@
/bin/
+/debug.log
diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java
index 6d7b28431f1..223972cc9bf 100755
--- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java
+++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java
@@ -718,16 +718,12 @@ public class LeaderboardsResource extends AbstractLeaderboardsResource {
@QueryParam(RaceLogServletConstants.PARAMS_RACE_FLEET_NAME) String fleetName,
@QueryParam(RaceLogServletConstants.PARAMS_TRACK_WIND) Boolean trackWind,
@QueryParam(RaceLogServletConstants.PARAMS_CORRECT_WIND_DIRECTION_BY_MAGNETIC_DECLINATION) Boolean correctWindDirectionByMagneticDeclination,
- @QueryParam(RaceLogServletConstants.PARAMS_TRACKED_RACE_NAME) String optionalTrackedRaceName,
- @QueryParam("secret") String secret)
+ @QueryParam(RaceLogServletConstants.PARAMS_TRACKED_RACE_NAME) String optionalTrackedRaceName)
throws NotDenotedForRaceLogTrackingException, Exception {
final LeaderboardAndRaceColumnAndFleetAndResponse leaderboardAndRaceColumnAndFleetAndResponse = getLeaderboardAndRaceColumnAndFleet(
leaderboardName, raceColumnName, fleetName);
- boolean skip = getService().skipChecksDueToCorrectSecret(leaderboardName, secret);
- if (!skip) {
- SecurityUtils.getSubject().checkPermission(SecuredDomainType.LEADERBOARD.getStringPermissionForObject(
- DefaultActions.UPDATE, leaderboardAndRaceColumnAndFleetAndResponse.getLeaderboard()));
- }
+ SecurityUtils.getSubject().checkPermission(SecuredDomainType.LEADERBOARD.getStringPermissionForObject(
+ DefaultActions.UPDATE, leaderboardAndRaceColumnAndFleetAndResponse.getLeaderboard()));
final Callable innerAction = () -> {
final Response result;
if (leaderboardAndRaceColumnAndFleetAndResponse.getFleet() != null) {
diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/LeaderboardWithEliminationTransitiveRemovalTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/LeaderboardWithEliminationTransitiveRemovalTest.java
new file mode 100644
index 00000000000..426042ede98
--- /dev/null
+++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/LeaderboardWithEliminationTransitiveRemovalTest.java
@@ -0,0 +1,88 @@
+package com.sap.sailing.server.test;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.UUID;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.sap.sailing.domain.base.Regatta;
+import com.sap.sailing.domain.common.CompetitorRegistrationType;
+import com.sap.sailing.domain.common.RankingMetrics;
+import com.sap.sailing.domain.common.dto.RegattaCreationParametersDTO;
+import com.sap.sailing.domain.common.dto.SeriesCreationParametersDTO;
+import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
+import com.sap.sailing.domain.leaderboard.impl.DelegatingRegattaLeaderboardWithCompetitorElimination;
+import com.sap.sailing.domain.leaderboard.impl.LowPoint;
+import com.sap.sailing.domain.persistence.PersistenceFactory;
+import com.sap.sailing.server.impl.RacingEventServiceImpl;
+import com.sap.sailing.server.interfaces.RacingEventService;
+import com.sap.sailing.server.operationaltransformation.AddSpecificRegatta;
+import com.sap.sailing.server.operationaltransformation.CreateRegattaLeaderboard;
+import com.sap.sailing.server.operationaltransformation.CreateRegattaLeaderboardWithEliminations;
+import com.sap.sailing.server.operationaltransformation.RemoveLeaderboard;
+import com.sap.sailing.server.operationaltransformation.RemoveRegatta;
+import com.sap.sse.security.SecurityService;
+import com.sap.sse.security.testsupport.SecurityServiceMockFactory;
+
+public class LeaderboardWithEliminationTransitiveRemovalTest {
+ private RacingEventService server;
+ private Regatta regatta;
+ private RegattaLeaderboard regattaLeaderboard;
+ private DelegatingRegattaLeaderboardWithCompetitorElimination regattaLeaderboardWithEliminations;
+
+ @Before
+ public void setUp() {
+ PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory().getDatabase().drop();
+ final SecurityService securityService = SecurityServiceMockFactory.mockSecurityService();
+ server = new RacingEventServiceImpl() {
+ @Override
+ public SecurityService getSecurityService() {
+ return securityService;
+ }
+ };
+ final LinkedHashMap seriesStructure = new LinkedHashMap<>();
+ final RegattaCreationParametersDTO regattaStructure = new RegattaCreationParametersDTO(seriesStructure);
+ regatta = server.apply(new AddSpecificRegatta("Test", "Laser Int.", /* canBoatsOfCompetitorsChangePerRace */ false,
+ /* competitorRegistrationType */ CompetitorRegistrationType.CLOSED, /* registrationLinkSecret */ null,
+ /* startDate */ null, /* endDate */ null, /* id */ UUID.randomUUID(),
+ regattaStructure, /* persistent */ true, new LowPoint(),
+ /* courseAreaIds */ Collections.singleton(server.getBaseDomainFactory().getOrCreateCourseArea(UUID.randomUUID(), "Default").getId()),
+ /* buoyZoneRadiusInHullLengths */ null, /* useStartTimeInference */ false,
+ /* controlTrackingFromStartAndFinishTimes */ true, /* autoRestartTrackingUponCompetitorSetChange */ false,
+ RankingMetrics.ONE_DESIGN));
+ regattaLeaderboard = server.apply(new CreateRegattaLeaderboard(regatta.getRegattaIdentifier(),
+ /* leaderboardDisplayName */ null, /* discardThresholds */ new int[0]));
+ regattaLeaderboardWithEliminations =
+ server.apply(new CreateRegattaLeaderboardWithEliminations("With Eliminations", /* display name */ null,
+ regattaLeaderboard.getName()));
+ }
+
+ @Test
+ public void testAllThere() {
+ assertNotNull(regatta);
+ assertNotNull(regattaLeaderboard);
+ assertNotNull(regattaLeaderboardWithEliminations);
+ assertSame(regatta, regattaLeaderboard.getRegatta());
+ assertSame(regatta, regattaLeaderboardWithEliminations.getRegatta());
+ }
+
+ @Test
+ public void testRemovingRegattaRemovesAllLeaderboards() {
+ server.apply(new RemoveRegatta(regatta.getRegattaIdentifier()));
+ assertNull(server.getLeaderboardByName(regattaLeaderboard.getName()));
+ assertNull(server.getLeaderboardByName(regattaLeaderboardWithEliminations.getName()));
+ }
+
+ @Test
+ public void testRemovingRegattaLeaderboardRemovesLeaderboardWithEliminations() {
+ server.apply(new RemoveLeaderboard(regattaLeaderboard.getName()));
+ assertNull(server.getLeaderboardByName(regattaLeaderboard.getName()));
+ assertNull(server.getLeaderboardByName(regattaLeaderboardWithEliminations.getName()));
+ }
+}
diff --git a/java/com.sap.sailing.simulator/META-INF/MANIFEST.MF b/java/com.sap.sailing.simulator/META-INF/MANIFEST.MF
index e43f2468896..6acadf2b352 100644
--- a/java/com.sap.sailing.simulator/META-INF/MANIFEST.MF
+++ b/java/com.sap.sailing.simulator/META-INF/MANIFEST.MF
@@ -10,8 +10,8 @@ Require-Bundle: com.sap.sailing.domain.common;bundle-version="1.0.0",
com.sap.sailing.domain.tractracadapter,
com.sap.sse,
com.sap.sse.common,
- com.sap.sse.security.common,
- org.apache.httpcomponents.httpclient;bundle-version="[4.2.6,4.3.0)",
+ com.sap.sse.security.common,
+ org.apache.httpcomponents.httpclient;bundle-version="4.5.5",
org.json.simple;bundle-version="1.1.0"
Bundle-ActivationPolicy: lazy
Import-Package: org.osgi.framework;version="1.6.0"
diff --git a/java/com.sap.sailing.targetplatform.base/features/target-base/feature.xml b/java/com.sap.sailing.targetplatform.base/features/target-base/feature.xml
index 5633f7133d3..5b76ac1a73d 100755
--- a/java/com.sap.sailing.targetplatform.base/features/target-base/feature.xml
+++ b/java/com.sap.sailing.targetplatform.base/features/target-base/feature.xml
@@ -82,13 +82,6 @@
version="1.1.2"
unpack="false"/>
-
-
-
+
@@ -29,8 +29,8 @@
-
-
+
+
@@ -43,10 +43,11 @@
-
-
-
-
+
+
+
+
+
@@ -120,7 +121,7 @@
-
+
@@ -166,6 +167,7 @@
+
@@ -285,4 +287,4 @@
-
+
\ No newline at end of file
diff --git a/java/com.sap.sailing.windestimation.lab/src/com/sap/sailing/windestimation/data/importer/ManeuverAndWindImporter.java b/java/com.sap.sailing.windestimation.lab/src/com/sap/sailing/windestimation/data/importer/ManeuverAndWindImporter.java
index 0ca91010d4d..ae6a98d4ae1 100644
--- a/java/com.sap.sailing.windestimation.lab/src/com/sap/sailing/windestimation/data/importer/ManeuverAndWindImporter.java
+++ b/java/com.sap.sailing.windestimation.lab/src/com/sap/sailing/windestimation/data/importer/ManeuverAndWindImporter.java
@@ -22,11 +22,10 @@ import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
+import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.SystemDefaultHttpClient;
-import org.apache.http.params.BasicHttpParams;
-import org.apache.http.params.HttpConnectionParams;
-import org.apache.http.params.HttpParams;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
@@ -102,13 +101,13 @@ public class ManeuverAndWindImporter {
}
public HttpClient createNewHttpClient() {
- HttpParams httpParams = new BasicHttpParams();
- HttpConnectionParams.setConnectionTimeout(httpParams, CONNECTION_TIMEOUT_MILLIS);
- HttpClient client = new SystemDefaultHttpClient(httpParams);
- client.getParams().setParameter("http.socket.timeout", CONNECTION_TIMEOUT_MILLIS);
- client.getParams().setParameter("http.connection.timeout", CONNECTION_TIMEOUT_MILLIS);
- client.getParams().setParameter("http.connection-manager.timeout", new Long(CONNECTION_TIMEOUT_MILLIS));
- client.getParams().setParameter("http.protocol.head-body-timeout", CONNECTION_TIMEOUT_MILLIS);
+ CloseableHttpClient client = HttpClientBuilder.create()
+ .setDefaultRequestConfig(RequestConfig.custom()
+ .setConnectTimeout(CONNECTION_TIMEOUT_MILLIS)
+ .setConnectionRequestTimeout(CONNECTION_TIMEOUT_MILLIS)
+ .setSocketTimeout(CONNECTION_TIMEOUT_MILLIS)
+ .build())
+ .build();
return client;
}
diff --git a/java/com.sap.sailing.windestimation.lab/src/com/sap/sailing/windestimation/data/importer/PolarDataImporter.java b/java/com.sap.sailing.windestimation.lab/src/com/sap/sailing/windestimation/data/importer/PolarDataImporter.java
index 4014ae1e2d4..77046dc9316 100644
--- a/java/com.sap.sailing.windestimation.lab/src/com/sap/sailing/windestimation/data/importer/PolarDataImporter.java
+++ b/java/com.sap.sailing.windestimation.lab/src/com/sap/sailing/windestimation/data/importer/PolarDataImporter.java
@@ -8,7 +8,7 @@ import org.apache.commons.io.FileUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.SystemDefaultHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
import org.json.simple.parser.ParseException;
import com.sap.sailing.windestimation.util.LoggingUtil;
@@ -42,7 +42,7 @@ public class PolarDataImporter {
}
protected InputStream getContentFromResponse() throws IOException, ParseException {
- HttpClient client = new SystemDefaultHttpClient();
+ HttpClient client = HttpClientBuilder.create().build();
HttpGet getProcessor = new HttpGet(getAPIString());
HttpResponse processorResponse = client.execute(getProcessor);
return processorResponse.getEntity().getContent();
diff --git a/java/com.sap.sailing.windestimation/META-INF/MANIFEST.MF b/java/com.sap.sailing.windestimation/META-INF/MANIFEST.MF
index 1db9c22f449..b0ca0099db5 100644
--- a/java/com.sap.sailing.windestimation/META-INF/MANIFEST.MF
+++ b/java/com.sap.sailing.windestimation/META-INF/MANIFEST.MF
@@ -8,8 +8,8 @@ Bundle-Vendor: SAP
Require-Bundle: org.eclipse.osgi;bundle-version="3.10.1",
org.json.simple;bundle-version="1.1.0",
org.mongodb.mongo-java-driver;bundle-version="3.6.4",
- org.apache.httpcomponents.httpclient;bundle-version="[4.2.5,4.3.0)",
- org.apache.httpcomponents.httpcore;bundle-version="[4.0.0,4.3.0)",
+ org.apache.httpcomponents.httpclient;bundle-version="4.5.5",
+ org.apache.httpcomponents.httpcore;bundle-version="4.4.9",
com.sap.sse,
com.sap.sse.common,
com.sap.sailing.domain.shared.android,
@@ -40,9 +40,9 @@ Import-Package: javax.ws.rs;version="1.1.1",
javax.ws.rs.core;version="1.1.1",
javax.xml.bind,
org.apache.http,
- org.apache.http.client;version="[4.2.6,4.3.0)",
- org.apache.http.client.methods;version="[4.2.6,4.3.0)",
- org.apache.http.impl.client;version="[4.2.6,4.3.0)"
+ org.apache.http.client;version="4.5.5",
+ org.apache.http.client.methods;version="4.5.5",
+ org.apache.http.impl.client;version="4.5.5"
Web-ContextPath: /windestimation
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Bundle-ActivationPolicy: lazy
diff --git a/java/com.sap.sailing.windestimation/src/com/sap/sailing/windestimation/jaxrs/client/WindEstimationDataClient.java b/java/com.sap.sailing.windestimation/src/com/sap/sailing/windestimation/jaxrs/client/WindEstimationDataClient.java
index 1788be37c78..0c5b1f93ce1 100644
--- a/java/com.sap.sailing.windestimation/src/com/sap/sailing/windestimation/jaxrs/client/WindEstimationDataClient.java
+++ b/java/com.sap.sailing.windestimation/src/com/sap/sailing/windestimation/jaxrs/client/WindEstimationDataClient.java
@@ -8,7 +8,7 @@ import java.util.logging.Logger;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.SystemDefaultHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
import org.json.simple.parser.ParseException;
import com.sap.sailing.windestimation.integration.ReplicableWindEstimationFactoryService;
@@ -61,7 +61,7 @@ public class WindEstimationDataClient {
}
protected InputStream getContentFromResponse() throws IOException, ParseException {
- HttpClient client = new SystemDefaultHttpClient();
+ HttpClient client = HttpClientBuilder.create().build();
HttpGet getProcessor = new HttpGet(getAPIString());
HttpResponse processorResponse = client.execute(getProcessor);
return processorResponse.getEntity().getContent();
diff --git a/java/com.sap.sse.common/src/com/sap/sse/common/media/MimeType.java b/java/com.sap.sse.common/src/com/sap/sse/common/media/MimeType.java
index d837a2d69e8..8caf72ba0a8 100644
--- a/java/com.sap.sse.common/src/com/sap/sse/common/media/MimeType.java
+++ b/java/com.sap.sse.common/src/com/sap/sse/common/media/MimeType.java
@@ -40,5 +40,9 @@ public enum MimeType {
return null;
}
}
+
+ public static MimeType[] mp4MimeTypes() {
+ return new MimeType[] { mp4, mp4panorama, mp4panoramaflip};
+ }
}
\ No newline at end of file
diff --git a/java/com.sap.sse.feature.runtime/feature.xml b/java/com.sap.sse.feature.runtime/feature.xml
index 23205b68f5d..dab5bd85d0a 100644
--- a/java/com.sap.sse.feature.runtime/feature.xml
+++ b/java/com.sap.sse.feature.runtime/feature.xml
@@ -38,6 +38,13 @@
version="9.4.8.v20171121"
unpack="false"/>
+
+
+
+
optionalTimeout = Optional.of(Duration.ONE_MINUTE.times(5));
private AwsLandscape landscape;
private AwsRegion region;
private byte[] keyPass;
@@ -59,13 +79,59 @@ public class ConnectivityTest {
}
@Test
- public void testConnectivity() {
+ public , ReplicaT extends ApplicationReplicaProcess>
+ void testConnectivity() throws JSchException, IOException, SftpException, NumberFormatException, InterruptedException {
+ final String TARGET_GROUP_NAME_PREFIX = "S-test-";
+ final String hostedZoneName = "wiesen-weg.de";
+ final String hostname = "test-"+new Random().nextInt()+"."+hostedZoneName;
+ final String keyName = "MyKey-"+UUID.randomUUID();
+ createKeyPair(keyName);
final AwsInstance host = landscape.launchHost(landscape.getImage(region, "ami-01b4b27a5699e33e6"),
- InstanceType.T3_SMALL, landscape.getAvailabilityZoneByName(region, "eu-west-2b"), "Axel", Collections.singleton(()->"sg-0b2afd48960251280"));
+ InstanceType.T3_SMALL, landscape.getAvailabilityZoneByName(region, "eu-west-2b"), keyName, Collections.singleton(()->"sg-0b2afd48960251280"),
+ Optional.of(Tags.with("Name", "MyHost").and("Hello", "World")));
try {
assertNotNull(host);
+ final Instance instance = landscape.getInstance(host.getInstanceId(), region);
+ boolean foundName = false;
+ boolean foundHello = false;
+ for (final Tag tag : instance.tags()) {
+ if (tag.key().equals("Name") && tag.value().equals("MyHost")) {
+ foundName = true;
+ }
+ if (tag.key().equals("Hello") && tag.value().equals("World")) {
+ foundHello = true;
+ }
+ }
+ assertTrue(foundName);
+ assertTrue(foundHello);
+ // check env.sh access
+ final ApplicationProcess process = new ApplicationProcessImpl<>(8888, host, "/home/sailing/servers/server");
+ final String envSh = process.getEnvSh(optionalTimeout);
+ assertFalse(envSh.isEmpty());
+ assertTrue(envSh.contains("SERVER_NAME="));
+ final Release release = process.getRelease(new ReleaseRepositoryImpl("http://releases.sapsailing.com", "build"), optionalTimeout);
+ assertNotNull(release);
+ assertEquals(14888, process.getTelnetPortToOSGiConsole(optionalTimeout));
+ @SuppressWarnings("unchecked")
+ final AwsLandscape castLandscape = (AwsLandscape) landscape;
+ final CreateDNSBasedLoadBalancerMapping> createDNSBasedLoadBalancerMappingProcedure =
+ new CreateDNSBasedLoadBalancerMapping>(
+ process, hostname, TARGET_GROUP_NAME_PREFIX, castLandscape, optionalTimeout);
+ final String wiesenWegId = landscape.getDNSHostedZoneId(hostedZoneName);
+ try {
+ createDNSBasedLoadBalancerMappingProcedure.run();
+ assertNotNull(createDNSBasedLoadBalancerMappingProcedure.getLoadBalancerUsed());
+ assertNotNull(createDNSBasedLoadBalancerMappingProcedure.getMasterTargetGroupCreated());
+ assertEquals(TARGET_GROUP_NAME_PREFIX+process.getServerName(optionalTimeout), createDNSBasedLoadBalancerMappingProcedure.getPublicTargetGroupCreated().getName());
+ } finally {
+ if (createDNSBasedLoadBalancerMappingProcedure.getLoadBalancerUsed() != null) {
+ createDNSBasedLoadBalancerMappingProcedure.getLoadBalancerUsed().delete();
+ landscape.removeDNSRecord(wiesenWegId, hostname, RRType.CNAME, createDNSBasedLoadBalancerMappingProcedure.getLoadBalancerUsed().getDNSName());
+ }
+ }
} finally {
landscape.terminate(host);
+ landscape.deleteKeyPair(region, keyName);
}
}
@@ -131,31 +197,27 @@ public class ConnectivityTest {
private void testSshConnectWithKey(final String keyName) throws InterruptedException, JSchException, IOException {
final AwsInstance host = landscape.launchHost(landscape.getImage(region, "ami-01b4b27a5699e33e6"),
- InstanceType.T3_SMALL, landscape.getAvailabilityZoneByName(region, "eu-west-2b"), keyName, Collections.singleton(()->"sg-0b2afd48960251280"));
+ InstanceType.T3_SMALL, landscape.getAvailabilityZoneByName(region, "eu-west-2b"), keyName, Collections.singleton(()->"sg-0b2afd48960251280"), /* tags */ Optional.empty());
try {
assertNotNull(host);
logger.info("Created instance with ID "+host.getInstanceId());
logger.info("Waiting for public IP address...");
// wait for public IPv4 address to become available:
- InetAddress address = host.getPublicAddress(Duration.ONE_SECOND.times(20));
+ InetAddress address = host.getPublicAddress(optionalTimeout);
assertNotNull(address);
logger.info("Obtained public IP address "+address);
- SshCommandChannel shellChannel = null;
- int sshConnectAttempts = 20;
- while (shellChannel == null && sshConnectAttempts-- > 0) {
- try {
- shellChannel = host.createRootSshChannel();
- } catch (JSchException e) {
- logger.info(e.getMessage()
- + " while trying to connect. Probably timeout trying early SSH connection. Retrying "
- + sshConnectAttempts + " more times...");
- Thread.sleep(10000);
- }
- }
+ SshCommandChannel shellChannel = host.createRootSshChannel(optionalTimeout);
assertNotNull(shellChannel);
logger.info("Shell channel connected. Waiting for it to become responsive...");
shellChannel.sendCommandLineSynchronously("pwd", System.err);
assertEquals("/root\n", turnAllLineSeparatorsIntoLineFeed(new String(shellChannel.getStreamContentsAsByteArray())));
+ // now try a simple command, checking for the "init" process to be found
+ final SshCommandChannel commandChannel = host.createRootSshChannel(Optional.empty());
+ final String processToLookFor = "init";
+ commandChannel.sendCommandLineSynchronously("ps axlw | grep "+processToLookFor, new ByteArrayOutputStream());
+ final String output = new String(commandChannel.getStreamContentsAsByteArray());
+ assertTrue(output.contains(processToLookFor));
+ assertEquals(0, commandChannel.getExitStatus());
} finally {
landscape.terminate(host);
landscape.deleteKeyPair(region, keyName);
@@ -181,31 +243,44 @@ public class ConnectivityTest {
@Test
public void setDNSRecordTest() {
- final String hostname = "my-test-host-"+new Random().nextInt()+".wiesen-weg.de.";
+ final String testHostedZoneDnsName = "wiesen-weg.de";
+ final String hostname = "my-test-host-"+new Random().nextInt()+"."+testHostedZoneDnsName+".";
final String ipAddress = "1.2.3.4";
+ final String dnsHostedZoneId = landscape.getDNSHostedZoneId(testHostedZoneDnsName);
try {
- ChangeInfo changeInfo = landscape.setDNSRecordToValue(landscape.getDefaultDNSHostedZoneId(), hostname, ipAddress);
+ ChangeInfo changeInfo = landscape.setDNSRecordToValue(dnsHostedZoneId, hostname, ipAddress);
int attempts = 10;
while ((changeInfo=landscape.getUpdatedChangeInfo(changeInfo)).status() != ChangeStatus.INSYNC && --attempts > 0) {
- Thread.sleep(5000);
+ Thread.sleep(10000);
};
assertEquals(ChangeStatus.INSYNC, changeInfo.status());
} catch (Exception e) {
fail(e.getMessage());
} finally {
- landscape.removeDNSRecord(landscape.getDefaultDNSHostedZoneId(), hostname, ipAddress);
+ landscape.removeDNSRecord(dnsHostedZoneId, hostname, ipAddress);
}
}
@Test
- public void createEmptyLoadBalancerTest() {
+ public void createEmptyLoadBalancerTest() throws InterruptedException {
final String albName = "MyAlb"+new Random().nextInt();
- final ApplicationLoadBalancer alb = landscape.createLoadBalancer(albName, region);
+ final ApplicationLoadBalancer alb = landscape.createLoadBalancer(albName, region);
try {
assertNotNull(alb);
assertEquals(albName, alb.getName());
+ assertTrue(Util.contains(Util.map(landscape.getLoadBalancers(region), ApplicationLoadBalancer::getArn), alb.getArn()));
+ // now add two rules to the load balancer and check they arrive:
+ final String hostnameCondition = "a.wiesen-weg.de";
+ @SuppressWarnings("unchecked")
+ final Iterable rulesCreated = alb
+ .addRules(Rule.builder()
+ .priority("5")
+ .conditions(r -> r.field("host-header").hostHeaderConfig(hhc -> hhc.values(hostnameCondition)))
+ .actions(a -> a.type(ActionTypeEnum.FIXED_RESPONSE).fixedResponseConfig(frc -> frc.statusCode("200").messageBody("Hello world"))).build());
+ assertEquals(1, Util.size(rulesCreated));
+ assertTrue(hostnameCondition, rulesCreated.iterator().next().conditions().iterator().next().hostHeaderConfig().values().contains(hostnameCondition));
} finally {
- landscape.deleteLoadBalancer(alb);
+ alb.delete();
}
}
@@ -220,4 +295,13 @@ public class ConnectivityTest {
landscape.deleteTargetGroup(targetGroup);
}
}
+
+ @Test
+ public void testCentralReverseProxyInEuWest2IsAvailable() throws IOException, InterruptedException, JSchException {
+ final ReverseProxyCluster proxy = landscape.getCentralReverseProxy(new AwsRegion("eu-west-2"));
+ assertEquals(1, Util.size(proxy.getHosts()));
+ final HttpURLConnection healthCheckConnection = (HttpURLConnection) new URL("http://"+proxy.getHosts().iterator().next().getPublicAddress().getCanonicalHostName()+proxy.getHealthCheckPath()).openConnection();
+ assertEquals(200, healthCheckConnection.getResponseCode());
+ healthCheckConnection.disconnect();
+ }
}
diff --git a/java/com.sap.sse.landscape.aws.test/src/com/sap/sse/landscape/aws/MongoTests.java b/java/com.sap.sse.landscape.aws.test/src/com/sap/sse/landscape/aws/MongoTests.java
new file mode 100644
index 00000000000..ee24c9ee449
--- /dev/null
+++ b/java/com.sap.sse.landscape.aws.test/src/com/sap/sse/landscape/aws/MongoTests.java
@@ -0,0 +1,60 @@
+package com.sap.sse.landscape.aws;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.net.InetAddress;
+import java.net.URISyntaxException;
+import java.net.UnknownHostException;
+import java.util.Optional;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import com.sap.sse.common.Duration;
+import com.sap.sse.landscape.Host;
+import com.sap.sse.landscape.mongodb.impl.MongoProcessImpl;
+import com.sap.sse.landscape.mongodb.impl.MongoProcessInReplicaSetImpl;
+import com.sap.sse.landscape.mongodb.impl.MongoReplicaSetImpl;
+
+public class MongoTests {
+ private static final Optional optionalTimeout = Optional.of(Duration.ONE_MINUTE.times(5));
+
+ private Host localhost;
+ private MongoProcessImpl mongoProcess;
+ private MongoProcessInReplicaSetImpl mongoProcessInReplicaSet;
+ private MongoReplicaSetImpl mongoReplicaSet;
+
+ @Before
+ public void setUp() throws UnknownHostException {
+ localhost = Mockito.mock(Host.class);
+ Mockito.when(localhost.getPublicAddress()).thenReturn(InetAddress.getByName("127.0.0.1"));
+ mongoProcess = new MongoProcessImpl(localhost);
+ mongoReplicaSet = new MongoReplicaSetImpl("rs0");
+ mongoProcessInReplicaSet = new MongoProcessInReplicaSetImpl(mongoReplicaSet, 10222, localhost);
+ mongoReplicaSet.addReplica(mongoProcessInReplicaSet);
+ }
+
+ @Test
+ public void testMongoReadiness() {
+ assertTrue(mongoProcess.isReady(optionalTimeout));
+ }
+
+ @Test
+ public void testMongoProcessInReplicaSetIsAvailable() {
+ assertTrue(mongoProcessInReplicaSet.isReady(optionalTimeout));
+ }
+
+ @Test
+ public void testMongoProcessInReplicaSetCanReportPriority() throws URISyntaxException {
+ assertTrue(mongoProcessInReplicaSet.isInReplicaSet());
+ }
+
+ @Test
+ public void testMd5() throws URISyntaxException {
+ final String hash = mongoReplicaSet.getMD5Hash("local");
+ assertNotNull(hash);
+ assertTrue(!hash.isEmpty());
+ }
+}
diff --git a/java/com.sap.sse.landscape.aws/META-INF/MANIFEST.MF b/java/com.sap.sse.landscape.aws/META-INF/MANIFEST.MF
index e9ce37d9216..6360435d78a 100755
--- a/java/com.sap.sse.landscape.aws/META-INF/MANIFEST.MF
+++ b/java/com.sap.sse.landscape.aws/META-INF/MANIFEST.MF
@@ -12,7 +12,10 @@ Require-Bundle: com.amazon.aws.aws-java-api;bundle-version="2.13.50",
com.jcraft.jsch;bundle-version="0.1.54",
com.sap.sse.landscape.aws.persistence,
com.sap.sse.security,
- com.sap.sse.mongodb
+ com.sap.sse.mongodb,
+ com.sap.sse.shared.android
Import-Package: org.osgi.framework;version="1.8.0"
Bundle-Activator: com.sap.sse.landscape.aws.impl.Activator
-Export-Package: com.sap.sse.landscape.aws
+Export-Package: com.sap.sse.landscape.aws,
+ com.sap.sse.landscape.aws.impl,
+ com.sap.sse.landscape.aws.orchestration
diff --git a/java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/ApplicationLoadBalancer.java b/java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/ApplicationLoadBalancer.java
index a2724e9bb02..5fec1f2e65c 100755
--- a/java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/ApplicationLoadBalancer.java
+++ b/java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/ApplicationLoadBalancer.java
@@ -2,6 +2,10 @@ package com.sap.sse.landscape.aws;
import com.sap.sse.common.Named;
import com.sap.sse.landscape.Region;
+import com.sap.sse.landscape.application.ApplicationProcessMetrics;
+
+import software.amazon.awssdk.services.elasticloadbalancingv2.model.Listener;
+import software.amazon.awssdk.services.elasticloadbalancingv2.model.Rule;
/**
* Represents an AWS Application Load Balancer (ALB). When created, a default configuration with the following
@@ -9,25 +13,76 @@ import com.sap.sse.landscape.Region;
* and {@code access_logs.s3.prefix}, enabling {@code deletion_protection.enabled} and setting
* {@code idle_timeout.timeout_seconds} to the maximum value of 4000s, furthermore spanning all availability
* zones available in the region in which the ALB is deployed and using a specific security group that
- * allows for HTTP and HTTPS traffic.
+ * allows for HTTP and HTTPS traffic.