diff --git a/configuration/archiveDb.sh b/configuration/archiveDb.sh new file mode 100755 index 00000000000..7caa3a9fa26 --- /dev/null +++ b/configuration/archiveDb.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# Uses mongodump / mongorestore to copy a database from the live replica set to +# the archive database. Afterwards, a comparison is made using the mongohash +# command +# Usage: archiveDb.sh + +DB=$1 +DUMPBASEDIR=/var/lib/mongodb + +echo -n "This will drop the database $DB in the archive DB before importing. Are you sure (y/n)? " +read CONFIRMATION +if [ "$CONFIRMATION" != "y" ]; then + exit 2; +fi + +cd "$DUMPBASEDIR" +mongodump --port 10203 --db $DB +# Drop the entire DB in the archive DB to ensure there are no left-over collections +echo "use $DB +db.dropDatabase()" | mongo --port 10202 +mongorestore --noIndexRestore --drop --port 10202 dump +rm -rf dump/$DB +ORIGINAL_HASH=$(./mongohash -p 10203 -d $DB) +ARCHIVE_HASH=$(./mongohash -p 10202 -d $DB) +if [ $ORIGINAL_HASH = $ARCHIVE_HASH ]; then + echo Hashes of old and new are equal: $ORIGINAL_HASH and $ARCHIVE_HASH. Dropping $DB in live DB + echo "use $DB +db.dropDatabase()" | mongo "mongodb://localhost:10203/$DB?replicaSet=live" + echo "Don't forget to switch your server's env.sh so it has MONGODB_URI=\"mongodb://dbserver.internal.sapsailing.com:10202/$DB\"" + exit 0 +else + echo Hashes of old and new differ: $ORIGINAL_HASH and $ARCHIVE_HASH + exit 1 +fi diff --git a/configuration/createHudsonJobForBug.sh b/configuration/createHudsonJobForBug.sh index f00301605c2..6c639df1dcc 100755 --- a/configuration/createHudsonJobForBug.sh +++ b/configuration/createHudsonJobForBug.sh @@ -1,7 +1,5 @@ #!/bin/bash -BUG_ID="$1" - if [ $# -eq 0 ]; then echo "$0 [-t -r ] " echo "" @@ -19,12 +17,16 @@ while getopts $options option do case $option in t) SKIP_TESTS=1;; - b) BUILD_RELEASE=1;; + r) BUILD_RELEASE=1;; \?) echo "Invalid option" exit 4;; esac done +shift $((OPTIND-1)) + +BUG_ID="$1" + CONFIGFILE=$(mktemp mylocalconfigXXXX.xml) RESPONSE_HEADERS=$(mktemp responseheadersXXXX) HUDSON_BASE_URL=https://hudson.sapsailing.com @@ -40,7 +42,7 @@ if [ "$SKIP_TESTS" = "1" ]; then sed -i -e 's|\([^<]*\)build *|\1-t build|' "$CONFIGFILE" fi if [ "$BUILD_RELEASE" = "1" ]; then - sed -i -e 's|\([^<]*\)|\1 \&\& PATH=/usr/local/bin:${PATH} configuration/buildAndUpdateProduct.sh -n bug'$BUG_ID' -w trac@sapsailing.com -u release|' "$CONFIGFILE" + sed -i -e 's|\([^<]*\)|\1 \&\& PATH=/usr/local/bin:${PATH} configuration/buildAndUpdateProduct.sh -n bug'$BUG_ID' -w trac@sapsailing.com -u release|' "$CONFIGFILE" fi sed -i -e '/$/{ N @@ -49,7 +51,7 @@ s|[^<]*|bug'$BUG_ID'| }' "$CONFIGFILE" curl -D "$RESPONSE_HEADERS" -s -XPOST "$HUDSON_BASE_URL/createItem?name=bug$BUG_ID" -u "$USERNAME:$PASSWORD" --data-binary "@$CONFIGFILE" -H "Content-Type:text/xml" >/dev/null 2>/dev/null RESPONSE_CODE=$(cat "$RESPONSE_HEADERS" | head -n 1 | cut -d ' ' -f2 ) -if [[ "$RESPONSE_CODE" =~ "2.." ]]; then +if [[ "$RESPONSE_CODE" =~ 2.. ]]; then echo "Find your new, enabled Hudson job at $HUDSON_BASE_URL/job/bug$BUG_ID/" else echo "Error. HTTP response code $RESPONSE_CODE. Did the job already exist?" diff --git a/configuration/launchhudsonslave b/configuration/launchhudsonslave index d4d4a284db9..db987877788 100755 --- a/configuration/launchhudsonslave +++ b/configuration/launchhudsonslave @@ -1,7 +1,7 @@ #!/bin/bash AWS=/root/.local/bin/aws REGION=eu-west-1 -HUDSON_SLAVE_AMI_ID=ami-0f6962a59a3a52d8b +HUDSON_SLAVE_AMI_ID=ami-0d5067168c7514538 echo Launching instance... instanceid=`$AWS ec2 run-instances --image-id $HUDSON_SLAVE_AMI_ID --count 1 --instance-type c5d.4xlarge --key-name Axel --security-groups "Sailing Analytics App" --region $REGION --placement AvailabilityZone=eu-west-1c --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=Hudson Ubuntu Slave}]' --instance-initiated-shutdown-behavior terminate | tee /tmp/slavelaunch.out | jq .Instances[0].InstanceId | sed -e 's/"//g'` if [ "$instanceid" = "" ]; then diff --git a/configuration/mongo_instance_setup/README b/configuration/mongo_instance_setup/README new file mode 100644 index 00000000000..d96e64fcaf8 --- /dev/null +++ b/configuration/mongo_instance_setup/README @@ -0,0 +1,3 @@ +Deploy the .mount and .service units to /etc/systemd/system. +Deploy the ephemeralvolume script to /usr/local/bin +Deploy mongod.conf to /etc diff --git a/configuration/mongo_instance_setup/chownvarlibmongo.service b/configuration/mongo_instance_setup/chownvarlibmongo.service new file mode 100644 index 00000000000..c40dcdec755 --- /dev/null +++ b/configuration/mongo_instance_setup/chownvarlibmongo.service @@ -0,0 +1,14 @@ +[Unit] +Description=Ensures all files under /var/lib/mongo are owned by mongod user/group +Requires=var-lib-mongo.mount +After=var-lib-mongo.mount +Before=mongod.service + +[Install] +RequiredBy=mongod.service + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStart=/bin/chown -R mongod /var/lib/mongo/ +ExecStart=/bin/chgrp -R mongod /var/lib/mongo/ diff --git a/configuration/mongo_instance_setup/ephemeralvolume b/configuration/mongo_instance_setup/ephemeralvolume new file mode 100644 index 00000000000..5d9fc1142df --- /dev/null +++ b/configuration/mongo_instance_setup/ephemeralvolume @@ -0,0 +1,14 @@ +#!/bin/bash + +# Script to deploy on an instance that has an ephemeral volume as /dev/nvme0n1 (adjust env var PARTITION if different) +# Ensures the partition is xfs-formatted, any existing partition contents will be overwritten if formatted otherwise. +# An existing xfs partition will be left alone. + +PARTITION=/dev/nvme0n1 +FSTYPE=$(blkid -p $PARTITION -s TYPE -o value) +if [ "$FSTYPE" != "xfs" ]; then + echo FSTYPE was "$FSTYPE" but should have been xfs. Formatting $PARTITION... + mkfs.xfs -f $PARTITION +else + echo FSTYPE was "$FSTYPE" which is just right :-\) +fi diff --git a/configuration/mongo_instance_setup/ephemeralvolume.service b/configuration/mongo_instance_setup/ephemeralvolume.service new file mode 100644 index 00000000000..bc7138d8caf --- /dev/null +++ b/configuration/mongo_instance_setup/ephemeralvolume.service @@ -0,0 +1,11 @@ +[Unit] +Description=Ensures /dev/nvme0n1 is XFS-formatted +Requires=-.mount +After=-.mount + +[Install] + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStart=/usr/local/bin/ephemeralvolume diff --git a/configuration/mongo_instance_setup/mongod.conf b/configuration/mongo_instance_setup/mongod.conf new file mode 100644 index 00000000000..fe13710a654 --- /dev/null +++ b/configuration/mongo_instance_setup/mongod.conf @@ -0,0 +1,49 @@ +# mongod.conf + +# for documentation of all options, see: +# http://docs.mongodb.org/manual/reference/configuration-options/ + +# where to write logging data. +systemLog: + destination: file + logAppend: true + path: /var/log/mongodb/mongod.log + +# Where and how to store data. +storage: + dbPath: /var/lib/mongo + journal: + enabled: true + directoryPerDB: true +# engine: +# mmapv1: +# wiredTiger: + +# how the process runs +processManagement: + fork: true # fork and run in background + pidFilePath: /var/run/mongodb/mongod.pid # location of pidfile + timeZoneInfo: /usr/share/zoneinfo + +# network interfaces +net: + port: 27017 +# bindIp: 127.0.0.1 # Listen to local interface only, comment to listen on all interfaces. +# bindIp: 172.31.33.146 + bindIp: 0.0.0.0 + +#security: + +#operationProfiling: + +replication: + replSetName: live + +#sharding: + +## Enterprise-Only Options + +#auditLog: + +#snmp: + diff --git a/configuration/mongohash b/configuration/mongohash new file mode 100755 index 00000000000..e7ce3a458aa --- /dev/null +++ b/configuration/mongohash @@ -0,0 +1,54 @@ +#!/bin/bash +PORT=27017 +HOST=localhost + +if [ -z "$1" ]; then + echo "Usage: $0 [ OPTIONS ] [ \"mongodb://...\" ]" + echo "Where [ OPTIONS ] are:" + echo " -p PORT the port to use, default $PORT" + echo " -d DATABASE the name of the database to use; mandatory, if no URI is specified" + echo " -h HOST the host to use, default $HOST" + echo " -r REPLICA_SET the name of the replica set; no default, assuming connecting to PRIMARY" + echo " -a show all hashes of all collections in the database specified, not just the DB hash" + echo + echo "Prints the dbHash(...) output to stdout and the URI used to stderr." + echo "Example: $0 \"mongodb://localhost:10203/49er?replicaSet=live\"" + echo " stderr: URI=mongodb://localhost:10203/49er?replicaSet=live" + echo " stdout: f17293af5ff32f9e2ad76056b1ff9897" + echo "Example: $0 -h dbserver.internal.sapsailing.com -p 10202 -d 49er" + echo " stderr: URI=mongodb://dbserver.internal.sapsailing.com:1020r/49er" + echo " stdout: f17293af5ff32f9e2ad76056b1ff9897" + exit 1 +fi + +options='p:d:h:r:a' +while getopts $options option +do + case $option in + a) ALL=1;; + p) PORT=$OPTARG;; + d) DB=$OPTARG;; + h) HOST=$OPTARG;; + r) REPLICA_SET=$OPTARG;; + \?) echo "Invalid option" >&2 + exit 4;; + esac +done +# Consume the remaining argument as an optional URI: +URI=${!OPTIND} +if [ -z "$DB" -a -z "$URI" ]; then + echo "Database unknown. Either -d or URI argument required." >&2 + exit 5 +fi +if [ -z "$URI" ]; then + URI="mongodb://$HOST:$PORT/$DB" + if [ -n "$REPLICA_SET" ]; then + URI="${URI}?replicaSet=$REPLICA_SET" + fi +fi +echo "URI: $URI" >&2 +if [ -z $ALL ]; then + echo "db.runCommand({dbHash: 1})" | mongo --quiet "$URI" | grep -v "^$(date +%Y-%m-%d)" | grep md5 | sed -e 's/^.*"md5" : "\(.*\)".*$/\1/' +else + echo "db.runCommand({dbHash: 1})" | mongo --quiet "$URI" | grep -v "^$(date +%Y-%m-%d)" +fi diff --git a/docker/Dockerfile.tpl b/docker/Dockerfile.tpl index 0cad3406014..48c0c247355 100755 --- a/docker/Dockerfile.tpl +++ b/docker/Dockerfile.tpl @@ -2,15 +2,15 @@ FROM donaldduck70/sapjvm8:8.1.046 LABEL maintainer=axel.uhl@sap.com # Download and extract the release WORKDIR /home/sailing/servers/server -RUN wget -O /tmp/RELEASE.tar.gz http://releases.sapsailing.com/RELEASE/RELEASE.tar.gz \ - && tar xzvpf /tmp/RELEASE.tar.gz \ - && rm /tmp/RELEASE.tar.gz RUN apt-get update \ && apt-get install -y vim telnet dnsutils net-tools jq COPY vimrc /root/.vimrc RUN wget -O /tmp/rds.pem https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem \ && /opt/sapjvm_8/bin/keytool -importcert -alias AWSRDS -file /tmp/rds.pem -keystore /opt/sapjvm_8/jre/lib/security/cacerts -noprompt -storepass changeit \ && rm /tmp/rds.pem +RUN wget -O /tmp/RELEASE.tar.gz http://releases.sapsailing.com/RELEASE/RELEASE.tar.gz \ + && tar xzvpf /tmp/RELEASE.tar.gz \ + && rm /tmp/RELEASE.tar.gz COPY env.sh . COPY start . COPY JavaSE-11.profile . diff --git a/docker/install-helm b/docker/install-helm index a4114a8537c..d51aac67dd0 100755 --- a/docker/install-helm +++ b/docker/install-helm @@ -1,9 +1,22 @@ #!/bin/bash -# If helm executable is missing, get it from https://github.com/helm/helm/releases -# and for Linux amd64 in particular grab it from https://storage.googleapis.com/kubernetes-helm/helm-v2.12.0-rc.1-linux-amd64.tar.gz -kubectl create serviceaccount --namespace kube-system tiller -kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller -kubectl patch deploy --namespace kube-system tiller-deploy -p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}' -helm init -helm list -helm repo update +cat </dev/null | grep build- | tail -1 | sed -e 's/^.*\(build-[0-9]*\).*$/\1/') echo Latest release is $release fi -GITROOT=`dirname $0`/.. +GITROOT="`dirname $0`/.." +DOCKERDIR="${GITROOT}/docker" +DOCKERFILE="$DOCKERDIR/Dockerfile" echo "Copying files from GITROOT $GITROOT into Docker workspace" -cp "$GITROOT/java/target/env.sh" . -cp "$GITROOT/java/target/start" . -cp "$GITROOT/java/target/configuration/JavaSE-11.profile" . -cat Dockerfile.tpl | sed -e "s/RELEASE/${release}/g" >Dockerfile +cp "$GITROOT/java/target/env.sh" "$DOCKERDIR" +cp "$GITROOT/java/target/start" "$DOCKERDIR" +cp "$GITROOT/java/target/configuration/JavaSE-11.profile" "$DOCKERDIR" +cat ${DOCKERFILE}.tpl | sed -e "s/RELEASE/${release}/g" >${DOCKERFILE} +cd "$DOCKERDIR" docker build -t sapsailing:$release . echo "Cleaning up..." rm start env.sh JavaSE-11.profile Dockerfile +docker tag sapsailing:$release donaldduck70/sapsailing:$release +docker push donaldduck70/sapsailing:$release +if [ "$SET_LATEST" = "1" ]; then + docker tag sapsailing:$release donaldduck70/sapsailing:latest + docker push donaldduck70/sapsailing:latest +fi diff --git a/docker/sailing-pod-kw2019.yaml b/docker/sailing-pod-kw2019.yaml index 028cf5c4fb5..fb1f33ef1d7 100644 --- a/docker/sailing-pod-kw2019.yaml +++ b/docker/sailing-pod-kw2019.yaml @@ -7,7 +7,7 @@ metadata: spec: containers: - name: sailing - image: donaldduck70/sapsailing:bug4018-201901182300 + image: donaldduck70/sapsailing:latest imagePullPolicy: Always ports: - containerPort: 8888 diff --git a/docker/sailing-pod-tw2019.yaml b/docker/sailing-pod-tw2019.yaml index ca287a7aae3..ea6b01f597c 100755 --- a/docker/sailing-pod-tw2019.yaml +++ b/docker/sailing-pod-tw2019.yaml @@ -7,7 +7,7 @@ metadata: spec: containers: - name: sailing - image: donaldduck70/sapsailing:bug4018-201901182300 + image: donaldduck70/sapsailing:latest imagePullPolicy: Always ports: - containerPort: 8888 diff --git a/home.md b/home.md index e9f66c2858d..781c0587b0b 100644 --- a/home.md +++ b/home.md @@ -35,6 +35,7 @@ SAP is at the center of today’s technology revolution, developing innovations * [[Creating an EC2 image from scratch|wiki/info/landscape/creating-ec2-image-from-scratch]] * [[Creating a webserver EC2 image from scratch|wiki/info/landscape/creating-ec2-image-for-webserver-from-scratch]] * [[EC2 mail relaying|wiki/info/landscape/mail-relaying]] + * [[Creating an EC2 image for a MongoDB Replica Set from scratch|wiki/info/landscape/creating-ec2-mongodb-image-from-scratch]] * [[Setting up dedicated S3 buckets|wiki/info/landscape/s3-bucket-setup]] * [[Building and Deploying|wiki/info/landscape/building-and-deploying]] * [[Data Mining Architecture|wiki/info/landscape/data-mining-architecture]] diff --git a/java/com.sap.sailing.domain.igtimiadapter.persistence/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.igtimiadapter.persistence/META-INF/MANIFEST.MF index 587f0d8635f..7a6e6171681 100644 --- a/java/com.sap.sailing.domain.igtimiadapter.persistence/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.igtimiadapter.persistence/META-INF/MANIFEST.MF @@ -7,7 +7,7 @@ Bundle-Version: 1.0.0.qualifier Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Require-Bundle: com.sap.sse.mongodb, - org.eclipse.orbit.mongodb;bundle-version="3.2.2" + org.mongodb.mongo-java-driver;bundle-version="3.6.4" Export-Package: com.sap.sailing.domain.igtimiadapter.persistence Import-Package: org.osgi.framework Bundle-Activator: com.sap.sailing.domain.igtimiadapter.persistence.impl.Activator diff --git a/java/com.sap.sailing.domain.persistence/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.persistence/META-INF/MANIFEST.MF index a7d809ddfc3..21b85cf42dc 100644 --- a/java/com.sap.sailing.domain.persistence/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.persistence/META-INF/MANIFEST.MF @@ -15,7 +15,7 @@ Require-Bundle: com.sap.sse.mongodb, com.sap.sse, com.sap.sailing.server.gateway.serialization.shared.android, com.sap.sse.shared.android, - org.eclipse.orbit.mongodb;bundle-version="3.2.2" + org.mongodb.mongo-java-driver;bundle-version="3.6.4" Export-Package: com.sap.sailing.domain.persistence, com.sap.sailing.domain.persistence.impl; x-friends:="com.sap.sailing.domain.persistence.test, diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java index ef510c8ab10..8de3de4cd2c 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java @@ -27,6 +27,8 @@ import java.util.logging.Level; import java.util.logging.Logger; import org.bson.Document; +import org.bson.json.JsonMode; +import org.bson.json.JsonWriterSettings; import org.bson.types.ObjectId; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; @@ -41,7 +43,6 @@ import com.mongodb.client.MongoDatabase; import com.mongodb.client.model.IndexOptions; import com.mongodb.client.model.RenameCollectionOptions; import com.mongodb.client.result.DeleteResult; -import com.mongodb.util.JSON; import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl; import com.sap.sailing.domain.abstractlog.race.CompetitorResult.MergeState; @@ -1267,9 +1268,10 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { } RegattaConfiguration configuration = null; if (dbRegatta.containsKey(FieldNames.REGATTA_REGATTA_CONFIGURATION.name())) { + final JsonWriterSettings writerSettings = JsonWriterSettings.builder().outputMode(JsonMode.RELAXED).build(); try { JSONObject json = Helpers.toJSONObjectSafe(new JSONParser() - .parse(JSON.serialize(dbRegatta.get(FieldNames.REGATTA_REGATTA_CONFIGURATION.name())))); + .parse(((Document) dbRegatta.get(FieldNames.REGATTA_REGATTA_CONFIGURATION.name())).toJson(writerSettings))); configuration = RegattaConfigurationJsonDeserializer.create().deserialize(json); } catch (JsonDeserializationException | ParseException e) { logger.log(Level.WARNING, "Error loading racing procedure configration for regatta.", e); @@ -1502,7 +1504,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { raceLogEventAndOptionalUpdateInstructions.getB().ifPresent(dbObjectForUpdate->{ final Document q = new Document("_id", o.get("_id")); o.put(FieldNames.RACE_LOG_EVENT.name(), dbObjectForUpdate); - raceLog.updateOne(q, o); + raceLog.replaceOne(q, o); }); } catch (IllegalStateException e) { logger.log(Level.SEVERE, "Couldn't load race log event " + o + ": " + e.getMessage(), e); @@ -2317,7 +2319,6 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { device = loadDeviceId(deviceIdentifierServiceFinder, (Document) dbObject.get(FieldNames.DEVICE_ID.name())); } catch (Exception e) { logger.log(Level.WARNING, "Could not load deviceId for RaceLogEvent", e); - e.printStackTrace(); } ItemType mappedTo = itemResolver.get(); @SuppressWarnings("deprecation") @@ -2484,8 +2485,9 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { Map competitorsById = new HashMap<>(); MongoCollection collection = database.getCollection(CollectionNames.COMPETITORS.name()); try { + final JsonWriterSettings writerSettings = JsonWriterSettings.builder().outputMode(JsonMode.RELAXED).build(); for (Document o : collection.find()) { - JSONObject json = Helpers.toJSONObjectSafe(new JSONParser().parse(JSON.serialize(o))); + JSONObject json = Helpers.toJSONObjectSafe(new JSONParser().parse(o.toJson(writerSettings))); DynamicCompetitor c = competitorWithBoatRefDeserializer.deserialize(json); // ensure that in case there should be multiple competitors with equal IDs in the DB // only one will survive @@ -2531,8 +2533,9 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { MongoCollection collection = database.getCollection(CollectionNames.COMPETITORS_BAK.name()); try { logger.log(Level.INFO, "Bug2822 DB-Migration: Load old competitors with embedded boats from COMPETITORS_BAK."); + final JsonWriterSettings writerSettings = JsonWriterSettings.builder().outputMode(JsonMode.RELAXED).build(); for (Document o : collection.find()) { - JSONObject json = Helpers.toJSONObjectSafe(new JSONParser().parse(JSON.serialize(o))); + JSONObject json = Helpers.toJSONObjectSafe(new JSONParser().parse(o.toJson(writerSettings))); CompetitorWithBoat c = legacyCompetitorWithBoatDeserializer.deserialize(json); // accept only the first instance for any given ID if (!competitorsById.containsKey(c.getId())) { @@ -2554,8 +2557,9 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { ArrayList result = new ArrayList<>(); MongoCollection collection = database.getCollection(CollectionNames.BOATS.name()); try { + final JsonWriterSettings writerSettings = JsonWriterSettings.builder().outputMode(JsonMode.RELAXED).build(); for (Document o : collection.find()) { - JSONObject json = Helpers.toJSONObjectSafe(new JSONParser().parse(JSON.serialize(o))); + JSONObject json = Helpers.toJSONObjectSafe(new JSONParser().parse(o.toJson(writerSettings))); DynamicBoat b = boatDeserializer.deserialize(json); result.add(b); } @@ -2606,8 +2610,9 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { private DeviceConfiguration loadConfiguration(Document configObject) { DeviceConfiguration configuration = null; try { + final JsonWriterSettings writerSettings = JsonWriterSettings.builder().outputMode(JsonMode.RELAXED).build(); JsonDeserializer deserializer = DeviceConfigurationJsonDeserializer.create(); - JSONObject json = Helpers.toJSONObjectSafe(new JSONParser().parse(JSON.serialize(configObject))); + JSONObject json = Helpers.toJSONObjectSafe(new JSONParser().parse(configObject.toJson(writerSettings))); configuration = deserializer.deserialize(json); } catch (JsonDeserializationException | ParseException e) { logger.log(Level.SEVERE, diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java index 3b048d79d62..bad31a7e434 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java @@ -549,7 +549,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory { MongoCollection leaderboardGroupCollection = database.getCollection(CollectionNames.LEADERBOARD_GROUPS.name()); Document query = new Document(FieldNames.LEADERBOARD_GROUP_NAME.name(), oldName); Document update = new Document("$set", new Document(FieldNames.LEADERBOARD_GROUP_NAME.name(), newName)); - leaderboardGroupCollection.withWriteConcern(WriteConcern.ACKNOWLEDGED).replaceOne(query, update, new UpdateOptions().upsert(true)); + leaderboardGroupCollection.withWriteConcern(WriteConcern.ACKNOWLEDGED).updateOne(query, update, new UpdateOptions().upsert(true)); } @Override @@ -647,7 +647,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory { MongoCollection eventCollection = database.getCollection(CollectionNames.EVENTS.name()); Document query = new Document(FieldNames.EVENT_ID.name(), id); Document renameUpdate = new Document("$set", new Document(FieldNames.EVENT_NAME.name(), newName)); - eventCollection.withWriteConcern(WriteConcern.ACKNOWLEDGED).replaceOne(query, renameUpdate, new UpdateOptions().upsert(true)); + eventCollection.withWriteConcern(WriteConcern.ACKNOWLEDGED).updateOne(query, renameUpdate, new UpdateOptions().upsert(true)); } @Override diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/META-INF/MANIFEST.MF index 81dbe9152d3..fbb1d459487 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/META-INF/MANIFEST.MF @@ -17,6 +17,6 @@ Require-Bundle: com.sap.sailing.domain.shared.android, org.junit;bundle-version="4.8.2", com.sap.sse.common, com.sap.sailing.server.gateway.serialization.shared.android, - org.eclipse.orbit.mongodb;bundle-version="3.2.2" + org.mongodb.mongo-java-driver;bundle-version="3.6.4" Export-Package: com.sap.sailing.domain.racelog.tracking.test.mock Automatic-Module-Name: com.sap.sailing.domain.racelogtrackingadapter.testsupport diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.racelogtrackingadapter/META-INF/MANIFEST.MF index e1772ba9edb..2f7d884fe83 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/META-INF/MANIFEST.MF @@ -18,7 +18,7 @@ Require-Bundle: com.sap.sailing.domain, com.sap.sse, com.sap.sailing.server.gateway.serialization.shared.android, com.sap.sse.mail, - org.eclipse.orbit.mongodb;bundle-version="3.2.2" + org.mongodb.mongo-java-driver;bundle-version="3.6.4" Import-Package: com.google.zxing;version="3.1.0", com.mongodb, com.sap.sse.concurrent, diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/RegattaLogFixTrackerRegattaListener.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/RegattaLogFixTrackerRegattaListener.java index 13dc6bb874b..200486cd9f5 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/RegattaLogFixTrackerRegattaListener.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/RegattaLogFixTrackerRegattaListener.java @@ -31,7 +31,7 @@ import com.sap.sse.replication.OperationsToMasterSender; import com.sap.sse.replication.ReplicableWithObjectInputStream; import com.sap.sse.replication.ReplicationMasterDescriptor; import com.sap.sse.replication.ReplicationService; -import com.sap.sse.replication.UnsentOperationsToMasterSender; +import com.sap.sse.replication.OperationsToMasterSendingQueue; /** * This is the main entry point of the {@link SensorFixStore} based fix tracking. @@ -57,10 +57,10 @@ public class RegattaLogFixTrackerRegattaListener extends AbstractTrackedRegattaA /** * This field is expected to be set by the {@link ReplicationService} once it has "adopted" this replicable. * The {@link ReplicationService} "injects" this service so it can be used here as a delegate for the - * {@link UnsentOperationsToMasterSender#retrySendingLater(OperationWithResult, OperationsToMasterSender)} + * {@link OperationsToMasterSendingQueue#scheduleForSending(OperationWithResult, OperationsToMasterSender)} * method. */ - private UnsentOperationsToMasterSender unsentOperationsToMasterSender; + private OperationsToMasterSendingQueue unsentOperationsToMasterSender; public RegattaLogFixTrackerRegattaListener( ServiceTracker racingEventServiceTracker, @@ -228,15 +228,15 @@ public class RegattaLogFixTrackerRegattaListener extends AbstractTrackedRegattaA } @Override - public void setUnsentOperationToMasterSender(UnsentOperationsToMasterSender service) { + public void setUnsentOperationToMasterSender(OperationsToMasterSendingQueue service) { this.unsentOperationsToMasterSender = service; } @Override - public , T> void retrySendingLater( + public , T> void scheduleForSending( OperationWithResult operationWithResult, OperationsToMasterSender sender) { if (unsentOperationsToMasterSender != null) { - unsentOperationsToMasterSender.retrySendingLater(operationWithResult, sender); + unsentOperationsToMasterSender.scheduleForSending(operationWithResult, sender); } } } diff --git a/java/com.sap.sailing.domain.swisstimingadapter.persistence/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.swisstimingadapter.persistence/META-INF/MANIFEST.MF index 3abe7c9fe3a..e8c2180d414 100644 --- a/java/com.sap.sailing.domain.swisstimingadapter.persistence/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.swisstimingadapter.persistence/META-INF/MANIFEST.MF @@ -13,7 +13,7 @@ Require-Bundle: com.sap.sailing.domain.swisstimingadapter, com.sap.sse.common, com.sap.sailing.domain.persistence, com.sap.sse, - org.eclipse.orbit.mongodb;bundle-version="3.2.2" + org.mongodb.mongo-java-driver;bundle-version="3.6.4" Export-Package: com.sap.sailing.domain.swisstimingadapter.persistence, com.sap.sailing.domain.swisstimingadapter.persistence.impl;x-friends:="com.sap.sailing.domain.swisstimingadapter.test,com.sap.sailing.mongodb.test" Import-Package: org.osgi.framework, diff --git a/java/com.sap.sailing.domain.swisstimingadapter.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.swisstimingadapter.test/META-INF/MANIFEST.MF index cf6b8ec9a08..02d8a87771c 100644 --- a/java/com.sap.sailing.domain.swisstimingadapter.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.swisstimingadapter.test/META-INF/MANIFEST.MF @@ -20,7 +20,7 @@ Require-Bundle: com.sap.sailing.domain, org.hamcrest.core;bundle-version="1.1.0", com.sap.sse, com.sap.sse.common, - org.eclipse.orbit.mongodb;bundle-version="3.2.2" + org.mongodb.mongo-java-driver;bundle-version="3.6.4" Bundle-ClassPath: resources/, . Import-Package: com.sap.sailing.server diff --git a/java/com.sap.sailing.domain.tractracadapter.persistence/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.tractracadapter.persistence/META-INF/MANIFEST.MF index f77ec08dbd5..9d6f5c7262a 100644 --- a/java/com.sap.sailing.domain.tractracadapter.persistence/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.tractracadapter.persistence/META-INF/MANIFEST.MF @@ -13,7 +13,7 @@ Require-Bundle: com.sap.sse.mongodb, com.sap.sse.common, com.sap.sailing.domain.persistence, com.sap.sse, - org.eclipse.orbit.mongodb;bundle-version="3.2.2" + org.mongodb.mongo-java-driver;bundle-version="3.6.4" Export-Package: com.sap.sailing.domain.tractracadapter.persistence, com.sap.sailing.domain.tractracadapter.persistence.impl;x-friends:="com.sap.sailing.mongodb.test" Import-Package: org.osgi.framework, diff --git a/java/com.sap.sailing.domain.windfinderadapter.test/src/com/sap/sailing/domain/windfinderadapter/impl/WindFinderPollerTest.java b/java/com.sap.sailing.domain.windfinderadapter.test/src/com/sap/sailing/domain/windfinderadapter/impl/WindFinderPollerTest.java index 6785021b74d..0fccc8cab65 100644 --- a/java/com.sap.sailing.domain.windfinderadapter.test/src/com/sap/sailing/domain/windfinderadapter/impl/WindFinderPollerTest.java +++ b/java/com.sap.sailing.domain.windfinderadapter.test/src/com/sap/sailing/domain/windfinderadapter/impl/WindFinderPollerTest.java @@ -9,6 +9,7 @@ import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -31,7 +32,7 @@ public class WindFinderPollerTest { final Object monitor = new Object(); WindFinderTrackerFactoryImpl factory = new WindFinderTrackerFactoryImpl(); factory.addReviewedSpotCollection(new ReviewedSpotsCollectionImpl("schilksee")); - final List> wind = new ArrayList<>(); + final List> wind = Collections.synchronizedList(new ArrayList<>()); final RaceDefinition mockedRaceDefinition = mock(RaceDefinition.class); final DynamicTrackedRegatta mockedTrackedRegatta = mock(DynamicTrackedRegatta.class); final DynamicTrackedRace mockedTrackedRace = mock(DynamicTrackedRace.class); @@ -60,8 +61,10 @@ public class WindFinderPollerTest { monitor.wait(5000); } tracker.stop(); - assertFalse(wind.isEmpty()); // at least one latest measurement - Pair firstFix = wind.iterator().next(); - assertTrue(new HashSet<>(Arrays.asList("de15", "10044N")).contains(((WindSourceWithAdditionalID) firstFix.getB()).getId())); + synchronized (wind) { + assertFalse(wind.isEmpty()); // at least one latest measurement + Pair firstFix = wind.iterator().next(); + assertTrue(new HashSet<>(Arrays.asList("de15", "10044N")).contains(((WindSourceWithAdditionalID) firstFix.getB()).getId())); + } } } diff --git a/java/com.sap.sailing.expeditionconnector.persistence/META-INF/MANIFEST.MF b/java/com.sap.sailing.expeditionconnector.persistence/META-INF/MANIFEST.MF index 1c13328a325..e64539513b3 100755 --- a/java/com.sap.sailing.expeditionconnector.persistence/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.expeditionconnector.persistence/META-INF/MANIFEST.MF @@ -15,6 +15,6 @@ Require-Bundle: com.sap.sailing.domain.persistence, com.sap.sailing.expeditionconnector.common, com.sap.sailing.server.gateway.serialization.shared.android, com.sap.sse, - org.eclipse.orbit.mongodb;bundle-version="3.2.2" + org.mongodb.mongo-java-driver;bundle-version="3.6.4" Export-Package: com.sap.sailing.expeditionconnector.persistence Automatic-Module-Name: com.sap.sailing.expeditionconnector.persistence diff --git a/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF b/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF index 82fabcd640f..e7868987bf7 100644 --- a/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF @@ -59,7 +59,7 @@ Require-Bundle: com.sap.sailing.domain, com.sap.sailing.expeditionconnector.common, org.mp4parser.isoparser;bundle-version="1.9.37", com.sap.sse.datamining.ui, - org.eclipse.orbit.mongodb;bundle-version="3.2.2" + org.mongodb.mongo-java-driver;bundle-version="3.6.4" Bundle-Activator: com.sap.sailing.gwt.ui.server.Activator Bundle-ActivationPolicy: lazy Import-Package: javax.servlet;version="3.1.0", diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties index 295b305cefc..af869c7b83a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties @@ -90,7 +90,7 @@ currentSpeedOverGroundInKnots=SOG (speed over ground) currentSpeedOverGroundInKnotsTooltip=Velocidad actual sobre el fondo. estimatedTimeToNextWaypointInSeconds=ETA (estimated time of arrival) estimatedTimeToNextWaypointInSecondsTooltip=Tiempo estimado que necesita el competidor para llegar al siguiente punto de ruta en función de su VMG actual y teniendo en cuenta la dirección real del viento. -gapToLeaderInSeconds=Gap to leader (diferencia con respecto al líder) +gapToLeaderInSeconds=Diferencia con respecto al líder gapToLeaderInSecondsTooltip=Realiza un cálculo estimado del tiempo de llegada a la posición actual de la embarcación más adelantada basándose en la VMG media para el tramo actual y un rendimiento igual al del líder de la carrera para tramos posteriores. La diferencia con respecto al líder indica con cuánta anticipación el competidor debería haber estado donde está actualmente con el fin de ocupar el mismo lugar que el líder en la llegada prevista en la posición actual de la embarcación más adelantada. velocityMadeGoodInKnots=VMG (velocity made good) velocityMadeGoodInKnotsTooltip=Velocidad efectiva a rumbo óptimo, es decir, la velocidad a barlovento/sotavento para ceñidas/empopadas, respectivamente, o la velocidad a lo largo de la derrota propuesta para alcanzar tramos. @@ -174,7 +174,7 @@ distanceTraveledTooltip=Distancia recorrida desde el inicio del tramo hasta el f raceDistanceTraveledTooltip=Distancia recorrida desde el inicio de la carrera hasta el final, o hasta el momento actual si la carrera no ha finalizado. velocityMadeGoodLong=Velocity Made Good speedOverGroundLong=Speed Over Ground -gapToLeaderLong=Gap To Leader +gapToLeaderLong=Diferencia con respecto al líder pointsToLoad=Puntos para cargar speed=Velocidad distance=Distancia @@ -218,7 +218,7 @@ actionContinueToBoatAssignment=Continuar a Asignaciones de embarcación actionEditScores=Editar puntuaciones actionOpenBrowser=Abrir en navegador actionRemove=Eliminar -actionEditSeries=Editar series +actionEditSeries=Tratar series actionRaceEdit=Editar o renombrar carrera actionRaceUnlink=Desvincular carrera actionRaceRemove=Eliminar carrera @@ -704,7 +704,7 @@ noWindFixesAvailable=No hay correcciones de viento disponibles distanceToStarboardEndOfStartlineWhenPassingStart=Dist.banda estribor o línea cuando se ha cruzado línea de salida distanceToStarboardEndOfStartlineWhenPassingStartTooltip=Distancia hasta la banda estribor de la línea de salida o hasta la baliza de salida cuando el competidor ha cruzado la línea de salida. actionAddFilter=Añadir filtro -actionEditFilter=Editar filtro +actionEditFilter=Tratar filtro availableFilters=Filtros disponibles createFilterHint=Crear un conjunto de filtros para filtrar la lista de competidores. filterThisNameAlreadyExists=Ya existe un filtro con este nombre. @@ -1221,7 +1221,7 @@ enterRaceName=Indicar nombre de carrera... userManagement=Gestión de usuarios regattaStructureImport=Importación de la estructura de regata filteredBy=Filtrado por -editSeries=Editar serie +editSeries=Tratar serie pleaseSelectAtLeastOneRegatta=Seleccione al menos una regata pleaseSelectAnEvent=Seleccione un evento selectALeaderboard=Seleccione una tabla de clasificación @@ -2132,7 +2132,7 @@ tagCreateTag=Crear etiqueta tagEditCustomTagButtons=Tratar botones de selección de etiqueta tagAddCustomTagButton=Añadir botón de selección tagNotSpecified=La etiqueta no puede estár en blanco -tagEditFilter=Tratar etiqueta +tagEditFilter=Tratar filtro tagEditCustomTagButton=Tratar botón de etiqueta tagSaveCustomTagButton=Grabar tagPreview=Vista previa de etiqueta: diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties index 49bc66dec0b..c8c6c90d6cd 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties @@ -2182,6 +2182,6 @@ noEventsFoundForCompetitors=選択された競技者に利用可能なイベン noStatisticsFoundForCompetitors=選択された競技者に利用可能な統計がありません。 pleaseSelectCompetitorFirst=最初に競技者を選択してください。 sailorProfileRemoveMessage=このセーラープロファイルを削除しますか -moreLoginInformationSectionSailorProfilesDescription=ログインすると、セーラープロファイルと呼ばれるユーザプロファイルの下に新しい領域が表示されます。そうしたセーラープロファイルに競技者を追加して、これらの競技者についてさらにインサイトを得ることができます。セーラープロファイルでは、お気に入りの競技者が参加しているすべてのイベントを表示でき、競技者間で順位を比較できます。さらに、たとえば最速の競技者がその記録に達するなどした正確な時点を表示するレースマップへの直接のリンクとともに、最大速度やスタートラインまでの距離など、最も重要な統計についての概要が得られます。レーススタート時のスタートラインまでの平均距離といった集約的な統計について、矢印をクリックして、平均値をドリルダウンできるデータマイニングに進むことができます。 +moreLoginInformationSectionSailorProfilesDescription=ログインすると、セーラープロファイルと呼ばれるユーザプロファイルの下に新しい領域が表示されます。そうしたセーラープロファイルに競技者を追加して、これらの競技者についてさらにインサイトを得ることができます。セーラープロファイルでは、お気に入りの競技者が参加しているすべてのイベントを表示でき、競技者間で順位を比較できます。さらに、たとえば最速の競技者がその記録に達するなどした正確な時点を表示するレースマップへの直接のリンクとともに、最大速度やスタートラインまでの距離など、最も重要な統計についての概要が得られます。レーススタート時のスタートラインまでの平均距離といった集約的な統計について、矢印をクリックして、平均値をドリルダウンできるデータマイニングに進むことができます。 moreLoginInformationSectionSailorProfilesHeading=セーラープロファイル confirmDeletion=削除確認 diff --git a/java/com.sap.sailing.media.persistence.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.media.persistence.test/META-INF/MANIFEST.MF index b40f293bb90..f4bb0dd67eb 100644 --- a/java/com.sap.sailing.media.persistence.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.media.persistence.test/META-INF/MANIFEST.MF @@ -14,5 +14,5 @@ Require-Bundle: com.sap.sse.mongodb, com.sap.sailing.domain.common;bundle-version="1.0.0", org.hamcrest;bundle-version="1.1.0", com.sap.sse.common, - org.eclipse.orbit.mongodb;bundle-version="3.2.2" + org.mongodb.mongo-java-driver;bundle-version="3.6.4" Automatic-Module-Name: com.sap.sailing.media.persistence.test diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadCompetitorsTest.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadCompetitorsTest.java index be2af4ca296..7ee4d091498 100644 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadCompetitorsTest.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadCompetitorsTest.java @@ -8,6 +8,7 @@ import java.net.URISyntaxException; import java.net.UnknownHostException; import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.UUID; import org.junit.Before; @@ -50,9 +51,9 @@ public class StoreAndLoadCompetitorsTest extends AbstractMongoDBTest { public static Competitor createCompetitor(String competitorName, Serializable id) { return new CompetitorImpl(id, competitorName, "KYC", Color.RED, null, null, new TeamImpl("STG", Collections.singleton( new PersonImpl(competitorName, new NationalityImpl("GER"), - /* dateOfBirth */ null, "This is famous "+competitorName)), + /* dateOfBirth */ new Date(), "This is famous "+competitorName)), new PersonImpl("Rigo van Maas", new NationalityImpl("NED"), - /* dateOfBirth */null, "This is Rigo, the coach")), + /* dateOfBirth */new Date(), "This is Rigo, the coach")), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); } @@ -104,6 +105,7 @@ public class StoreAndLoadCompetitorsTest extends AbstractMongoDBTest { assertEquals(flagImageURI1, loadedCompetitor.getFlagImage()); assertEquals(competitorName1, loadedCompetitor.getName()); assertEquals(competitorShortName1, loadedCompetitor.getShortName()); + assertEquals(c.getTeam().getCoach().getDateOfBirth(), loadedCompetitor.getTeam().getCoach().getDateOfBirth()); loadedCompetitor.setName(competitorName2); loadedCompetitor.setShortName(competitorShortName2); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index bfb2fdbd997..d19966a2a7a 100755 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -55,7 +55,7 @@ import com.sap.sse.replication.OperationWithResultWithIdWrapper; import com.sap.sse.replication.OperationsToMasterSender; import com.sap.sse.replication.ReplicationMasterDescriptor; import com.sap.sse.replication.ReplicationService; -import com.sap.sse.replication.UnsentOperationsToMasterSender; +import com.sap.sse.replication.OperationsToMasterSendingQueue; import com.sap.sse.util.ClearStateTestSupport; /** @@ -92,10 +92,10 @@ public class PolarDataServiceImpl implements ReplicablePolarService, ClearStateT /** * This field is expected to be set by the {@link ReplicationService} once it has "adopted" this replicable. * The {@link ReplicationService} "injects" this service so it can be used here as a delegate for the - * {@link UnsentOperationsToMasterSender#retrySendingLater(OperationWithResult, OperationsToMasterSender)} + * {@link OperationsToMasterSendingQueue#scheduleForSending(OperationWithResult, OperationsToMasterSender)} * method. */ - private UnsentOperationsToMasterSender unsentOperationsToMasterSender; + private OperationsToMasterSendingQueue unsentOperationsToMasterSender; /** * Constructs the polar data service with default generation settings. @@ -434,15 +434,15 @@ public class PolarDataServiceImpl implements ReplicablePolarService, ClearStateT } @Override - public void setUnsentOperationToMasterSender(UnsentOperationsToMasterSender service) { + public void setUnsentOperationToMasterSender(OperationsToMasterSendingQueue service) { this.unsentOperationsToMasterSender = service; } @Override - public , T> void retrySendingLater( + public , T> void scheduleForSending( OperationWithResult operationWithResult, OperationsToMasterSender sender) { if (unsentOperationsToMasterSender != null) { - unsentOperationsToMasterSender.retrySendingLater(operationWithResult, sender); + unsentOperationsToMasterSender.scheduleForSending(operationWithResult, sender); } } } diff --git a/java/com.sap.sailing.selenium.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.selenium.test/META-INF/MANIFEST.MF index 6e86deee6ea..970c9f3dd30 100644 --- a/java/com.sap.sailing.selenium.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.selenium.test/META-INF/MANIFEST.MF @@ -13,7 +13,7 @@ Require-Bundle: org.hamcrest;bundle-version="1.1.0", com.sap.sse.common, jcl.over.slf4j;bundle-version="1.6.4";resolution:=optional, slf4j.api;bundle-version="1.6.4";resolution:=optional, - org.eclipse.orbit.mongodb;bundle-version="3.2.2" + org.mongodb.mongo-java-driver;bundle-version="3.6.4" Bundle-ClassPath: lib/cglib-nodep-2.2.3.jar, . Automatic-Module-Name: com.sap.sailing.selenium.test diff --git a/java/com.sap.sailing.server.gateway.test.support/META-INF/MANIFEST.MF b/java/com.sap.sailing.server.gateway.test.support/META-INF/MANIFEST.MF index 291442e1081..027236c1e70 100644 --- a/java/com.sap.sailing.server.gateway.test.support/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.server.gateway.test.support/META-INF/MANIFEST.MF @@ -12,7 +12,7 @@ Require-Bundle: javax.servlet;bundle-version="3.1.0", com.sap.sse.mongodb;bundle-version="1.0.0", com.sap.sse.common, com.sap.sse, - org.eclipse.orbit.mongodb;bundle-version="3.2.2" + org.mongodb.mongo-java-driver;bundle-version="3.6.4" Bundle-ClassPath: . Web-ContextPath: /sailingserver/test-support Import-Package: org.osgi.framework, diff --git a/java/com.sap.sailing.server.gateway/META-INF/MANIFEST.MF b/java/com.sap.sailing.server.gateway/META-INF/MANIFEST.MF index 0dca706f51a..6c5bcdca04b 100644 --- a/java/com.sap.sailing.server.gateway/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.server.gateway/META-INF/MANIFEST.MF @@ -61,8 +61,8 @@ Require-Bundle: com.sap.sailing.domain, com.sap.sailing.geocoding, com.googlecode.java-diff-utils, com.sun.xml.bind.jaxb-impl;bundle-version="2.3.0", - org.eclipse.orbit.mongodb;bundle-version="3.2.2", - com.sap.sailing.server.interface;bundle-version="1.0.0" + com.sap.sailing.server.interface;bundle-version="1.0.0", + org.mongodb.mongo-java-driver;bundle-version="3.6.4" Bundle-ClassPath: . Web-ContextPath: /sailingserver Export-Package: com.sap.sailing.server.gateway, diff --git a/java/com.sap.sailing.server.replication.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.server.replication.test/META-INF/MANIFEST.MF index b001596265d..0d674363083 100644 --- a/java/com.sap.sailing.server.replication.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.server.replication.test/META-INF/MANIFEST.MF @@ -31,5 +31,5 @@ Require-Bundle: com.sap.sailing.server, com.sap.sailing.server.gateway, javax.ws.rs, com.sap.sailing.server.testsupport, - org.eclipse.orbit.mongodb;bundle-version="3.2.2" + org.mongodb.mongo-java-driver;bundle-version="3.6.4" Automatic-Module-Name: com.sap.sailing.server.replication.test diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/PostingOperationFromReplicaToMasterTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/PostingOperationFromReplicaToMasterTest.java new file mode 100755 index 00000000000..94e1cd31dd8 --- /dev/null +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/PostingOperationFromReplicaToMasterTest.java @@ -0,0 +1,51 @@ +package com.sap.sailing.server.replication.test; + +import static org.junit.Assert.assertTrue; + +import java.net.URISyntaxException; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.Timeout; + +import com.sap.sailing.domain.base.CompetitorAndBoatStore; +import com.sap.sailing.domain.leaderboard.Leaderboard; +import com.sap.sailing.domain.leaderboard.impl.LowPoint; +import com.sap.sailing.server.operationaltransformation.AllowBoatResetToDefaults; +import com.sap.sailing.server.operationaltransformation.CreateFlexibleLeaderboard; +import com.sap.sailing.server.operationaltransformation.UpdateBoat; + +/** + * Tests replication of boats in conjunction with the {@link CompetitorAndBoatStore} concepts, particularly the + * possibility to allow for boat data to be updated, either explicitly or implicitly from a tracking provider + * after marking the boat using + * {@link CompetitorAndBoatStore#allowBoatResetToDefaults(com.sap.sailing.domain.base.Boat)}. + * + * @author Frank Mittag + * + */ +public class PostingOperationFromReplicaToMasterTest extends AbstractServerReplicationTest { + @Rule public Timeout timeout = new Timeout(30, TimeUnit.SECONDS); + + /** + * Add a tracked race to the master that includes a competitor; check that the boat was properly replicated to + * the replica's {@link CompetitorAndBoatStore}. Afterwards, use the {@link UpdateBoat} operation on the master to + * perform an explicit update; ensure that the update arrived on the replica. Then execute an + * {@link AllowBoatResetToDefaults} operation on the master, afterwards update the boat on the master, + * @throws URISyntaxException + */ + @Test + public void testPostOperationToMaster() throws InterruptedException, URISyntaxException { + final String leaderboardName = "My Leaderboard"; + final CreateFlexibleLeaderboard operation = new CreateFlexibleLeaderboard(/* leaderboardName */ leaderboardName, + /* leaderboardDisplayName */ null, /* discardThresholds */ new int[0], /* scoringScheme */ new LowPoint(), + /* courseAreaId */ UUID.randomUUID()); + replica.apply(operation); + Thread.sleep(1000); + assertTrue(master.getLeaderboards().containsKey(leaderboardName)); + final Leaderboard masterLeaderboard = master.getLeaderboards().get(leaderboardName); + assertTrue(masterLeaderboard.getScoringScheme() instanceof LowPoint); + } +} \ No newline at end of file diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/UnsentOperationSenderJobTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/UnsentOperationSenderJobTest.java index 4237832c067..cad53fbcf11 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/UnsentOperationSenderJobTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/UnsentOperationSenderJobTest.java @@ -7,9 +7,12 @@ import java.io.OutputStream; import java.io.Serializable; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.Timeout; import com.sap.sailing.server.interfaces.RacingEventService; import com.sap.sse.replication.OperationWithResult; @@ -19,31 +22,48 @@ import com.sap.sse.replication.ReplicationMasterDescriptor; import com.sap.sse.replication.impl.UnsentOperationsSenderJob; public class UnsentOperationSenderJobTest implements OperationsToMasterSender> { + private static final int MAX_RESEND_COUNT = 10; private UnsentOperationsSenderJob job; private int resendCount; private CountDownLatch latch; -// @Rule public Timeout timeout = new Timeout(10, TimeUnit.SECONDS); + @Rule public Timeout timeout = new Timeout(10, TimeUnit.SECONDS); @Before public void setUp() { job = new UnsentOperationsSenderJob(); resendCount = 0; - latch = new CountDownLatch(1); } @Test public void testResendThatFailsAtLeastOnce() throws InterruptedException, BrokenBarrierException { final OperationWithResult operationWithResult = null; final OperationsToMasterSender> sender = this; - job.retrySendingLater(operationWithResult, sender); + latch = new CountDownLatch(1); + job.scheduleForSending(operationWithResult, sender); latch.await(); - assertEquals(2, resendCount); + assertEquals(MAX_RESEND_COUNT+1, resendCount); + } + + @Test + public void testSendingTwoOpsFollowedByAnotherOne() throws InterruptedException, BrokenBarrierException { + final OperationWithResult operationWithResult = null; + final OperationsToMasterSender> sender = this; + latch = new CountDownLatch(2); + job.scheduleForSending(operationWithResult, sender); + job.scheduleForSending(operationWithResult, sender); + latch.await(); + Thread.sleep(100); + assertEquals(MAX_RESEND_COUNT+2, resendCount); + latch = new CountDownLatch(1); + job.scheduleForSending(operationWithResult, sender); + latch.await(); + assertEquals(MAX_RESEND_COUNT+3, resendCount); } public void sendReplicaInitiatedOperationToMaster(OperationWithResult operation) throws IOException { logger.info("resending at count "+resendCount); - if (resendCount++ == 0) { + if (resendCount++ < MAX_RESEND_COUNT) { throw new IOException("First resend failed"); } else { latch.countDown(); @@ -51,9 +71,9 @@ public class UnsentOperationSenderJobTest implements OperationsToMasterSender, T> void retrySendingLater( + public , T> void scheduleForSending( OperationWithResult operationWithResult, OperationsToMasterSender sender) { - job.retrySendingLater(operationWithResult, sender); + job.scheduleForSending(operationWithResult, sender); } @Override @@ -71,4 +91,7 @@ public class UnsentOperationSenderJobTest implements OperationsToMasterSender operationWithResultWithIdWrapper) {} + + @Override + public boolean hasSentOperationToMaster(OperationWithResult operation) { return false; } } diff --git a/java/com.sap.sailing.server.testsupport/META-INF/MANIFEST.MF b/java/com.sap.sailing.server.testsupport/META-INF/MANIFEST.MF index 3b3d4b6c7d9..a0cc4cbcae8 100644 --- a/java/com.sap.sailing.server.testsupport/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.server.testsupport/META-INF/MANIFEST.MF @@ -34,6 +34,6 @@ Require-Bundle: com.sap.sailing.server, org.apache.shiro.web;bundle-version="1.2.2", org.apache.shiro.core;bundle-version="1.2.2", javax.servlet;bundle-version="3.1.0", - org.eclipse.orbit.mongodb;bundle-version="3.2.2", - com.sap.sailing.server.interface + com.sap.sailing.server.interface, + org.mongodb.mongo-java-driver;bundle-version="3.6.4" Automatic-Module-Name: com.sap.sailing.server.testsupport diff --git a/java/com.sap.sailing.server/META-INF/MANIFEST.MF b/java/com.sap.sailing.server/META-INF/MANIFEST.MF index da1dd9f1e57..5c109ac0c1b 100644 --- a/java/com.sap.sailing.server/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.server/META-INF/MANIFEST.MF @@ -39,8 +39,8 @@ Require-Bundle: com.sap.sailing.domain, org.apache.shiro.core;bundle-version="1.2.2", javax.servlet;bundle-version="3.1.0", com.sap.sse.mail, - org.eclipse.orbit.mongodb;bundle-version="3.2.2", - com.sap.sailing.server.interface;bundle-version="1.0.0" + com.sap.sailing.server.interface;bundle-version="1.0.0", + org.mongodb.mongo-java-driver;bundle-version="3.6.4" Bundle-ClassPath: . Export-Package: com.sap.sailing.server, com.sap.sailing.server.anniversary, diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy OSX).launch b/java/com.sap.sailing.server/SailingServer (No Proxy OSX).launch index 50ddbbe9d46..4a2ef7a20e5 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy OSX).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy OSX).launch @@ -30,9 +30,9 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy).launch b/java/com.sap.sailing.server/SailingServer (No Proxy).launch index 65b6a0bc131..81459223847 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy).launch @@ -32,9 +32,9 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Cached MTB).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Cached MTB).launch index 2e9fa84f8a6..4498de7a037 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Cached MTB).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Cached MTB).launch @@ -29,9 +29,9 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, GENERATED).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, GENERATED).launch index 5f620d8523c..cff6ea95743 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, GENERATED).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, GENERATED).launch @@ -32,9 +32,9 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Igtimi Local Test).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Igtimi Local Test).launch index ef07f20774a..b6284803d57 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Igtimi Local Test).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Igtimi Local Test).launch @@ -32,9 +32,9 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Java11).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Java11).launch index bdba6c8261e..33649382d4d 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Java11).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Java11).launch @@ -32,9 +32,9 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Java11, JFR Recording).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Java11, JFR Recording).launch index 36516983080..a78bb964b82 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Java11, JFR Recording).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Java11, JFR Recording).launch @@ -32,9 +32,9 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889).launch index 29bd61cf48e..967f3689322 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889).launch @@ -23,7 +23,7 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, Cached MTB).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, Cached MTB).launch index 60a9f51b779..e60d05d9561 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, Cached MTB).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, Cached MTB).launch @@ -23,7 +23,7 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate dev).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate dev).launch index 20b562174a4..54aaf776949 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate dev).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate dev).launch @@ -23,7 +23,7 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate).launch index e2c3ab1f954..599e4dc1e93 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate).launch @@ -25,10 +25,10 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890).launch index 627b5ae07e2..5b69c00f492 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890).launch @@ -23,7 +23,7 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890, auto-replicate from replica at 8889).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890, auto-replicate from replica at 8889).launch index de2160c2fec..e047a17883e 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890, auto-replicate from replica at 8889).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890, auto-replicate from replica at 8889).launch @@ -23,7 +23,7 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Remote Debug SAP VM).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Remote Debug SAP VM).launch index bace671df05..1d0d964c55e 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Remote Debug SAP VM).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Remote Debug SAP VM).launch @@ -23,9 +23,9 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, bundesliga2-2017 DB).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, bundesliga2-2017 DB).launch index 8aba021c9cc..fc7f7ef9cae 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, bundesliga2-2017 DB).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, bundesliga2-2017 DB).launch @@ -32,9 +32,9 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch index 0ecb3307d2f..07dcca858cf 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch @@ -29,9 +29,9 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy).launch b/java/com.sap.sailing.server/SailingServer (Proxy).launch index ceb4b8acf05..88dcfda4fd1 100644 --- a/java/com.sap.sailing.server/SailingServer (Proxy).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy).launch @@ -32,10 +32,10 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889).launch b/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889).launch index 221636115db..599b6fef229 100644 --- a/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889).launch @@ -23,10 +23,10 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889, Polar Import from 8888).launch b/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889, Polar Import from 8888).launch index 582ef3838ce..d65b87458df 100755 --- a/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889, Polar Import from 8888).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889, Polar Import from 8888).launch @@ -23,10 +23,10 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889, auto-replicate).launch b/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889, auto-replicate).launch index 25dbc385305..158bcbcd325 100755 --- a/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889, auto-replicate).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889, auto-replicate).launch @@ -23,10 +23,10 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8890).launch b/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8890).launch index a54afde979d..93f958c708c 100755 --- a/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8890).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8890).launch @@ -23,10 +23,10 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8890, auto-replicate from replica at 8889).launch b/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8890, auto-replicate from replica at 8889).launch index 4863680e1d0..5cc0386e30c 100755 --- a/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8890, auto-replicate from replica at 8889).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8890, auto-replicate from replica at 8889).launch @@ -23,10 +23,10 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8890, auto-replicate only SecurityService).launch b/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8890, auto-replicate only SecurityService).launch index 09122004024..dd34380d804 100755 --- a/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8890, auto-replicate only SecurityService).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8890, auto-replicate only SecurityService).launch @@ -23,10 +23,10 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy, LYC DB).launch b/java/com.sap.sailing.server/SailingServer (Proxy, LYC DB).launch index 76a88f1cb14..cd025d5e2ae 100755 --- a/java/com.sap.sailing.server/SailingServer (Proxy, LYC DB).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy, LYC DB).launch @@ -32,10 +32,10 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy, Remote Debug SAP VM).launch b/java/com.sap.sailing.server/SailingServer (Proxy, Remote Debug SAP VM).launch index 1bd352c6503..8be4d5b8af4 100644 --- a/java/com.sap.sailing.server/SailingServer (Proxy, Remote Debug SAP VM).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy, Remote Debug SAP VM).launch @@ -23,9 +23,9 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy, SwissTiming Live Simulation).launch b/java/com.sap.sailing.server/SailingServer (Proxy, SwissTiming Live Simulation).launch index a6443cff4a0..bd5ece3f53e 100644 --- a/java/com.sap.sailing.server/SailingServer (Proxy, SwissTiming Live Simulation).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy, SwissTiming Live Simulation).launch @@ -22,9 +22,9 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy, winddbTest).launch b/java/com.sap.sailing.server/SailingServer (Proxy, winddbTest).launch index 8961738f34d..8b4559b26fe 100644 --- a/java/com.sap.sailing.server/SailingServer (Proxy, winddbTest).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy, winddbTest).launch @@ -24,10 +24,10 @@ - + - + diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index e75a07b1aa3..39a563f0c4c 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -289,7 +289,7 @@ import com.sap.sse.replication.OperationWithResultWithIdWrapper; import com.sap.sse.replication.OperationsToMasterSender; import com.sap.sse.replication.ReplicationMasterDescriptor; import com.sap.sse.replication.ReplicationService; -import com.sap.sse.replication.UnsentOperationsToMasterSender; +import com.sap.sse.replication.OperationsToMasterSendingQueue; import com.sap.sse.shared.media.ImageDescriptor; import com.sap.sse.shared.media.VideoDescriptor; import com.sap.sse.util.ClearStateTestSupport; @@ -517,10 +517,10 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes /** * This field is expected to be set by the {@link ReplicationService} once it has "adopted" this replicable. * The {@link ReplicationService} "injects" this service so it can be used here as a delegate for the - * {@link UnsentOperationsToMasterSender#retrySendingLater(OperationWithResult, OperationsToMasterSender)} + * {@link OperationsToMasterSendingQueue#scheduleForSending(OperationWithResult, OperationsToMasterSender)} * method. */ - private UnsentOperationsToMasterSender unsentOperationsToMasterSender; + private OperationsToMasterSendingQueue unsentOperationsToMasterSender; /** * Providing the constructor parameters for a new {@link RacingEventServiceImpl} instance is a bit tricky @@ -4379,15 +4379,15 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes } @Override - public void setUnsentOperationToMasterSender(UnsentOperationsToMasterSender service) { + public void setUnsentOperationToMasterSender(OperationsToMasterSendingQueue service) { this.unsentOperationsToMasterSender = service; } @Override - public , T> void retrySendingLater( + public , T> void scheduleForSending( OperationWithResult operationWithResult, OperationsToMasterSender sender) { if (unsentOperationsToMasterSender != null) { - unsentOperationsToMasterSender.retrySendingLater(operationWithResult, sender); + unsentOperationsToMasterSender.scheduleForSending(operationWithResult, sender); } } } 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 92baa5364ed..ec6bac81dec 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 @@ -107,7 +107,7 @@ id="org.mongodb.mongo-java-driver" download-size="0" install-size="0" - version="2.13.0.RELEASE" + version="3.6.4" unpack="false"/> - + @@ -55,7 +55,6 @@ - @@ -256,7 +255,7 @@ - + diff --git a/java/com.sap.sailing.www/release_notes_admin.html b/java/com.sap.sailing.www/release_notes_admin.html index 7a314bc16df..f2439e1f173 100755 --- a/java/com.sap.sailing.www/release_notes_admin.html +++ b/java/com.sap.sailing.www/release_notes_admin.html @@ -42,6 +42,14 @@ available. +
  • Upgraded the MongoDB driver to version 3.6.4. This allows you to specify the new + retryWrites parameter, e.g., as in "mongodb://mongo0.internal.sapsailing.com/mydb?replicaSet=live&retryWrites=true". + This will improve your system's behavior if you run with a MongoDB replica set and have to + assume that at some point the PRIMARY MongoDB system may fail, with the remaining systems + in the replica set voting for a new PRIMARY. Without retryWrites your system will bluntly + fail, but with retryWrites, write operations will be re-tried once, with a timeout usually + enough for the remaining replicas to agree on a new PRIMARY. +
  • October 2018

    diff --git a/java/com.sap.sse.common/src/com/sap/sse/common/Duration.java b/java/com.sap.sse.common/src/com/sap/sse/common/Duration.java index 78ed317937c..0df88394dce 100755 --- a/java/com.sap.sse.common/src/com/sap/sse/common/Duration.java +++ b/java/com.sap.sse.common/src/com/sap/sse/common/Duration.java @@ -13,6 +13,7 @@ import com.sap.sse.common.impl.MillisecondsDurationImpl; public interface Duration extends Serializable, Comparable { static final Duration NULL = new MillisecondsDurationImpl(0); + static final Duration ONE_MILLISECOND = new MillisecondsDurationImpl(1); static final Duration ONE_SECOND = new MillisecondsDurationImpl(1000); static final Duration ONE_MINUTE = ONE_SECOND.times(60); static final Duration ONE_HOUR = ONE_MINUTE.times(60); diff --git a/java/com.sap.sse.feature.runtime/feature.xml b/java/com.sap.sse.feature.runtime/feature.xml index f134e5cdbb4..f083a0d236f 100644 --- a/java/com.sap.sse.feature.runtime/feature.xml +++ b/java/com.sap.sse.feature.runtime/feature.xml @@ -17,6 +17,13 @@ [Enter License Description here.] + + - - diff --git a/java/com.sap.sse.filestorage/META-INF/MANIFEST.MF b/java/com.sap.sse.filestorage/META-INF/MANIFEST.MF index 279cffb4e9b..4365b26af0c 100644 --- a/java/com.sap.sse.filestorage/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.filestorage/META-INF/MANIFEST.MF @@ -22,5 +22,5 @@ Require-Bundle: org.apache.servicemix.bundles.aws-java-sdk;bundle-version="1.9.8 com.sap.sse.operationaltransformation, com.sap.sse, org.apache.commons.io, - org.eclipse.orbit.mongodb;bundle-version="3.2.2" + org.mongodb.mongo-java-driver;bundle-version="3.6.4" Automatic-Module-Name: com.sap.sse.filestorage diff --git a/java/com.sap.sse.filestorage/src/com/sap/sse/filestorage/impl/FileStorageManagementServiceImpl.java b/java/com.sap.sse.filestorage/src/com/sap/sse/filestorage/impl/FileStorageManagementServiceImpl.java index 61b74bf6923..74ec3ff118e 100644 --- a/java/com.sap.sse.filestorage/src/com/sap/sse/filestorage/impl/FileStorageManagementServiceImpl.java +++ b/java/com.sap.sse.filestorage/src/com/sap/sse/filestorage/impl/FileStorageManagementServiceImpl.java @@ -29,7 +29,7 @@ import com.sap.sse.replication.OperationWithResultWithIdWrapper; import com.sap.sse.replication.OperationsToMasterSender; import com.sap.sse.replication.ReplicationMasterDescriptor; import com.sap.sse.replication.ReplicationService; -import com.sap.sse.replication.UnsentOperationsToMasterSender; +import com.sap.sse.replication.OperationsToMasterSendingQueue; /** * Implements {@link ServiceTrackerCustomizer} so that all {@link FileStorageServices} announced in the registry can @@ -61,10 +61,10 @@ public class FileStorageManagementServiceImpl implements ReplicableFileStorageMa /** * This field is expected to be set by the {@link ReplicationService} once it has "adopted" this replicable. * The {@link ReplicationService} "injects" this service so it can be used here as a delegate for the - * {@link UnsentOperationsToMasterSender#retrySendingLater(OperationWithResult, OperationsToMasterSender)} + * {@link OperationsToMasterSendingQueue#scheduleForSending(OperationWithResult, OperationsToMasterSender)} * method. */ - private UnsentOperationsToMasterSender unsentOperationForMasterQueue; + private OperationsToMasterSendingQueue unsentOperationForMasterQueue; public FileStorageManagementServiceImpl(TypeBasedServiceFinder serviceFinder, FileStorageServicePropertyStore propertyStore) { @@ -248,15 +248,15 @@ public class FileStorageManagementServiceImpl implements ReplicableFileStorageMa } @Override - public void setUnsentOperationToMasterSender(UnsentOperationsToMasterSender service) { + public void setUnsentOperationToMasterSender(OperationsToMasterSendingQueue service) { this.unsentOperationForMasterQueue = service; } @Override - public , T> void retrySendingLater( + public , T> void scheduleForSending( OperationWithResult operationWithResult, OperationsToMasterSender sender) { if (unsentOperationForMasterQueue != null) { - unsentOperationForMasterQueue.retrySendingLater(operationWithResult, sender); + unsentOperationForMasterQueue.scheduleForSending(operationWithResult, sender); } } } diff --git a/java/com.sap.sse.mail/src/com/sap/sse/mail/impl/MailServiceImpl.java b/java/com.sap.sse.mail/src/com/sap/sse/mail/impl/MailServiceImpl.java index c5f19a385aa..3dce9ca661e 100644 --- a/java/com.sap.sse.mail/src/com/sap/sse/mail/impl/MailServiceImpl.java +++ b/java/com.sap.sse.mail/src/com/sap/sse/mail/impl/MailServiceImpl.java @@ -33,7 +33,7 @@ import com.sap.sse.replication.OperationWithResult; import com.sap.sse.replication.OperationWithResultWithIdWrapper; import com.sap.sse.replication.OperationsToMasterSender; import com.sap.sse.replication.ReplicationMasterDescriptor; -import com.sap.sse.replication.UnsentOperationsToMasterSender; +import com.sap.sse.replication.OperationsToMasterSendingQueue; import com.sap.sse.util.ObjectInputStreamResolvingAgainstCache; public class MailServiceImpl implements ReplicableMailService { @@ -57,10 +57,10 @@ public class MailServiceImpl implements ReplicableMailService { /** * This field is expected to be set by the {@link ReplicationService} once it has "adopted" this replicable. * The {@link ReplicationService} "injects" this service so it can be used here as a delegate for the - * {@link UnsentOperationsToMasterSender#retrySendingLater(OperationWithResult, OperationsToMasterSender)} + * {@link OperationsToMasterSendingQueue#scheduleForSending(OperationWithResult, OperationsToMasterSender)} * method. */ - private UnsentOperationsToMasterSender unsentOperationForMasterQueue; + private OperationsToMasterSendingQueue unsentOperationForMasterQueue; public MailServiceImpl(Properties mailProperties, MailServiceResolver mailServiceResolver) { this.mailProperties = mailProperties; @@ -256,15 +256,15 @@ public class MailServiceImpl implements ReplicableMailService { } @Override - public void setUnsentOperationToMasterSender(UnsentOperationsToMasterSender service) { + public void setUnsentOperationToMasterSender(OperationsToMasterSendingQueue service) { this.unsentOperationForMasterQueue = service; } @Override - public , T> void retrySendingLater( + public , T> void scheduleForSending( OperationWithResult operationWithResult, OperationsToMasterSender sender) { if (unsentOperationForMasterQueue != null) { - unsentOperationForMasterQueue.retrySendingLater(operationWithResult, sender); + unsentOperationForMasterQueue.scheduleForSending(operationWithResult, sender); } } } diff --git a/java/com.sap.sse.mongodb/META-INF/MANIFEST.MF b/java/com.sap.sse.mongodb/META-INF/MANIFEST.MF index 8186acf2021..43e890156b7 100644 --- a/java/com.sap.sse.mongodb/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.mongodb/META-INF/MANIFEST.MF @@ -6,7 +6,7 @@ Bundle-Version: 1.0.0.qualifier Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Require-Bundle: com.sap.sse.common, - org.eclipse.orbit.mongodb;bundle-version="3.2.2" + org.mongodb.mongo-java-driver;bundle-version="3.6.4" Export-Package: com.sap.sse.mongodb Bundle-ActivationPolicy: lazy Import-Package: com.sap.sailing.util, diff --git a/java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/OperationsToMasterSender.java b/java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/OperationsToMasterSender.java index 3923ecdb5dd..058e42da17d 100755 --- a/java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/OperationsToMasterSender.java +++ b/java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/OperationsToMasterSender.java @@ -11,7 +11,7 @@ import java.util.logging.Logger; import com.sap.sse.common.WithID; -public interface OperationsToMasterSender> extends UnsentOperationsToMasterSender, WithID { +public interface OperationsToMasterSender> extends OperationsToMasterSendingQueue, WithID { final Logger logger = Logger.getLogger(OperationsToMasterSender.class.getName()); /** @@ -63,4 +63,11 @@ public interface OperationsToMasterSender * {@code operationWithResultWithIdWrapper} exactly once after this method has returned. */ void addOperationSentToMasterForReplication(OperationWithResultWithIdWrapper operationWithResultWithIdWrapper); + + /** + * If an operation equal to operationWithResultWithIdWrapper has previously been passed to a call to + * {@link OperationsToMasterSender#addOperationSentToMasterForReplication(OperationWithResultWithIdWrapper)}, the call returns true + * exactly once. + */ + boolean hasSentOperationToMaster(OperationWithResult operation); } diff --git a/java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/OperationsToMasterSendingQueue.java b/java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/OperationsToMasterSendingQueue.java new file mode 100755 index 00000000000..ba715d5d5c7 --- /dev/null +++ b/java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/OperationsToMasterSendingQueue.java @@ -0,0 +1,21 @@ +package com.sap.sse.replication; + +/** + * Objects of this type can queue operations whose delivery to the master server has failed temporarily. + * + * @author Axel Uhl (d043530) + * + */ +public interface OperationsToMasterSendingQueue { + /** + * Adds the operation to the queue. A sending task is scheduled after the current send delay if none has been + * scheduled yet. When this replica was unable to send an operation to the master, e.g., for connectivity issues or + * the master currently not being available, passing the operation to this method will enqueue the operation for + * later re-send attempts. Implementations have to make sure that operations are queued and sent in the order in + * which they are passed to this method. Therefore, implementations should {@code synchronize} on this object. + * + * @param sender + * the object to use to try to send the operation to the master server upon the next attempt + */ + , T> void scheduleForSending(OperationWithResult operationWithResult, OperationsToMasterSender sender); +} diff --git a/java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/Replicable.java b/java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/Replicable.java index 6bb7ed4799e..b6260dcbef9 100755 --- a/java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/Replicable.java +++ b/java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/Replicable.java @@ -242,18 +242,10 @@ extends OperationsToMasterSender>, Replicator } /** - * If an operation equal to operationWithResultWithIdWrapper has previously been passed to a call to - * {@link OperationsToMasterSender#addOperationSentToMasterForReplication(OperationWithResultWithIdWrapper)}, the call returns true - * exactly once. - */ - boolean hasSentOperationToMaster(OperationWithResult operation); - - - /** - * Injects a service into this replicable that this instance of {@link UnsentOperationsToMasterSender} can use - * as a delegate to implement the {@link UnsentOperationsToMasterSender#retrySendingLater(OperationWithResult, OperationsToMasterSender)} + * Injects a service into this replicable that this instance of {@link OperationsToMasterSendingQueue} can use + * as a delegate to implement the {@link OperationsToMasterSendingQueue#scheduleForSending(OperationWithResult, OperationsToMasterSender)} * method. This replicable may not be able to enqueue operations for re-trying sending to master after * an error occurred unless this method has been used to announce the service. */ - void setUnsentOperationToMasterSender(UnsentOperationsToMasterSender service); + void setUnsentOperationToMasterSender(OperationsToMasterSendingQueue service); } diff --git a/java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/ReplicableWithObjectInputStream.java b/java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/ReplicableWithObjectInputStream.java index 2c9e3ee71b4..60a4732da28 100755 --- a/java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/ReplicableWithObjectInputStream.java +++ b/java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/ReplicableWithObjectInputStream.java @@ -120,15 +120,7 @@ public interface ReplicableWithObjectInputStream, T> void retrySendingLater(OperationWithResult operationWithResult, OperationsToMasterSender sender); -} diff --git a/java/com.sap.sse.replication.testsupport/META-INF/MANIFEST.MF b/java/com.sap.sse.replication.testsupport/META-INF/MANIFEST.MF index 8ff85f8f4d4..b164927defa 100644 --- a/java/com.sap.sse.replication.testsupport/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.replication.testsupport/META-INF/MANIFEST.MF @@ -13,3 +13,10 @@ Require-Bundle: org.junit;bundle-version="4.8.2", com.sap.sse.replication.interfaces;bundle-version="1.0.0" Export-Package: com.sap.sse.replication.testsupport Automatic-Module-Name: com.sap.sse.replication.testsupport +Import-Package: javax.servlet;version="3.1.0", + javax.servlet.http;version="3.1.0", + org.mockito;version="1.10.14", + org.mockito.invocation;version="1.10.14", + org.mockito.mock;version="1.10.14", + org.mockito.stubbing;version="1.10.14", + org.osgi.util.tracker;version="1.5.1" diff --git a/java/com.sap.sse.replication.testsupport/src/com/sap/sse/replication/testsupport/AbstractServerReplicationTestSetUp.java b/java/com.sap.sse.replication.testsupport/src/com/sap/sse/replication/testsupport/AbstractServerReplicationTestSetUp.java index 36ebfc1764d..a6ad3b833de 100644 --- a/java/com.sap.sse.replication.testsupport/src/com/sap/sse/replication/testsupport/AbstractServerReplicationTestSetUp.java +++ b/java/com.sap.sse.replication.testsupport/src/com/sap/sse/replication/testsupport/AbstractServerReplicationTestSetUp.java @@ -2,9 +2,9 @@ package com.sap.sse.replication.testsupport; import static org.junit.Assert.assertFalse; -import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStreamReader; +import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStreamWriter; @@ -24,8 +24,18 @@ import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; +import javax.servlet.ReadListener; +import javax.servlet.ServletException; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + import org.junit.Rule; import org.junit.rules.Timeout; +import org.mockito.Matchers; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import com.rabbitmq.client.Channel; import com.rabbitmq.client.QueueingConsumer; @@ -42,6 +52,7 @@ import com.sap.sse.replication.impl.ReplicationInstancesManager; import com.sap.sse.replication.impl.ReplicationMasterDescriptorImpl; import com.sap.sse.replication.impl.ReplicationReceiverImpl; import com.sap.sse.replication.impl.ReplicationServiceImpl; +import com.sap.sse.replication.impl.ReplicationServlet; import com.sap.sse.replication.impl.SingletonReplicablesProvider; import net.jpountz.lz4.LZ4BlockOutputStream; @@ -276,44 +287,101 @@ public abstract class AbstractServerReplicationTestSetUp 0; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void setReadListener(ReadListener readListener) { + } + + @Override + public int read() throws IOException { + return inputStream.read(); + } + }); + final HttpServletResponse responseMock = Mockito.mock(HttpServletResponse.class); + final boolean[] error = { false }; + Mockito.doAnswer(new Answer() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + pw.println("HTTP/1.1 500 Bad Request"); + pw.println("Content-Type: text/plain"); + pw.println(); + pw.println(invocation.getArgumentAt(1, String.class)); + pw.flush(); + error[0] = true; + return null; + } + }).when(responseMock).sendError(Matchers.anyInt(), Matchers.isA(String.class)); + while (!readLine(inputStream).isEmpty()); + servlet.doPost(requestMock, responseMock); + if (!error[0]) { + pw.println("HTTP/1.1 200 OK"); + pw.println("Content-Type: text/plain"); + pw.println(); + pw.flush(); + } + } else { + pw.println("HTTP/1.1 200 OK"); + pw.println("Content-Type: text/plain"); + pw.println(); + pw.flush(); + if (request.contains("DEREGISTER")) { + // assuming that it is safe to unregister all replicas for tests + for (ReplicaDescriptor descriptor : getReplicaInfo()) { + unregisterReplica(descriptor); + } + } else if (request.contains("REGISTER")) { + final String uuid = UUID.randomUUID().toString(); + registerReplicaUuidForMaster(uuid, masterDescriptor); + masterReplicationService.registerReplica(replicaDescriptor); + pw.print(uuid); + } else if (request.contains("INITIAL_LOAD")) { + Channel channel = masterReplicationService.createMasterChannel(); + RabbitOutputStream ros = new RabbitOutputStream(INITIAL_LOAD_PACKAGE_SIZE, channel, + /* queueName */ "initial-load-for-TestClient-"+UUID.randomUUID(), /* syncAfterTimeout */ false); + pw.println(ros.getQueueName()); + final LZ4BlockOutputStream compressingOutputStream = new LZ4BlockOutputStream(ros); + master.serializeForInitialReplication(compressingOutputStream); + compressingOutputStream.finish(); + ros.close(); + } else if (request.contains("STOP")) { + stop = true; + logger.info("received STOP request"); } - } else if (request.contains("REGISTER")) { - final String uuid = UUID.randomUUID().toString(); - registerReplicaUuidForMaster(uuid, masterDescriptor); - masterReplicationService.registerReplica(replicaDescriptor); - pw.print(uuid); - } else if (request.contains("INITIAL_LOAD")) { - Channel channel = masterReplicationService.createMasterChannel(); - RabbitOutputStream ros = new RabbitOutputStream(INITIAL_LOAD_PACKAGE_SIZE, channel, - /* queueName */ "initial-load-for-TestClient-"+UUID.randomUUID(), /* syncAfterTimeout */ false); - pw.println(ros.getQueueName()); - final LZ4BlockOutputStream compressingOutputStream = new LZ4BlockOutputStream(ros); - master.serializeForInitialReplication(compressingOutputStream); - compressingOutputStream.finish(); - ros.close(); - } else if (request.contains("STOP")) { - stop = true; - logger.info("received STOP request"); } - pw.close(); + pw.flush(); // ensure response is delivered s.close(); logger.info("Request handled successfully."); } - } catch (IOException e) { + } catch (IOException | ServletException e) { throw new RuntimeException(e); } finally { + logger.info("replication servlet emulation done"); try { if (ss != null) { ss.close(); @@ -323,6 +391,17 @@ public abstract class AbstractServerReplicationTestSetUp, T> void retrySendingLater(OperationWithResult operationWithResult, OperationsToMasterSender sender) { - unsentOperationsSenderJob.retrySendingLater(operationWithResult, sender); + public , T> void scheduleForSending(OperationWithResult operationWithResult, OperationsToMasterSender sender) { + unsentOperationsSenderJob.scheduleForSending(operationWithResult, sender); } } diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServlet.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServlet.java index 2c267d9dfe2..48b7a9b3d69 100755 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServlet.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServlet.java @@ -21,7 +21,6 @@ import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringEscapeUtils; import org.json.simple.JSONArray; import org.json.simple.JSONObject; -import org.osgi.framework.BundleContext; import org.osgi.util.tracker.ServiceTracker; import com.rabbitmq.client.Channel; @@ -29,6 +28,7 @@ import com.sap.sse.gateway.AbstractHttpServlet; import com.sap.sse.replication.OperationWithResult; import com.sap.sse.replication.ReplicaDescriptor; import com.sap.sse.replication.Replicable; +import com.sap.sse.replication.ReplicablesProvider; import com.sap.sse.replication.ReplicationReceiver; import com.sap.sse.replication.ReplicationService; import com.sap.sse.util.impl.CountingOutputStream; @@ -72,17 +72,24 @@ public class ReplicationServlet extends AbstractHttpServlet { private final ServiceTracker replicationServiceTracker; - private final OSGiReplicableTracker replicablesProvider; + private final ReplicablesProvider replicablesProvider; public ReplicationServlet() throws Exception { - BundleContext context = Activator.getDefaultContext(); - replicablesProvider = new OSGiReplicableTracker(context); - replicationServiceTracker = new ServiceTracker(context, ReplicationService.class.getName(), null); - replicationServiceTracker.open(); + this(new OSGiReplicableTracker(Activator.getDefaultContext()), + new ServiceTracker(Activator.getDefaultContext(), + ReplicationService.class.getName(), /* tracker customizer */ null)); } + public ReplicationServlet(ReplicablesProvider replicablesProvider, ServiceTracker replicationServiceTracker) { + this.replicablesProvider = replicablesProvider; + this.replicationServiceTracker = replicationServiceTracker; + if (replicationServiceTracker != null) { + replicationServiceTracker.open(); + } + } + protected ReplicationService getReplicationService() { - return replicationServiceTracker.getService(); + return replicationServiceTracker == null ? null : replicationServiceTracker.getService(); } /** @@ -212,8 +219,11 @@ public class ReplicationServlet extends AbstractHttpServlet { resp.getWriter().print(result.toJSONString()); } + /** + * Made public for test support + */ @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { InputStream is = req.getInputStream(); DataInputStream dis = new DataInputStream(is); String replicableIdAsString = dis.readUTF(); diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/UnsentOperationsSenderJob.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/UnsentOperationsSenderJob.java index f88915947ce..92c8bdf5cc5 100755 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/UnsentOperationsSenderJob.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/UnsentOperationsSenderJob.java @@ -11,7 +11,7 @@ import com.sap.sse.common.Duration; import com.sap.sse.common.Util.Pair; import com.sap.sse.replication.OperationWithResult; import com.sap.sse.replication.OperationsToMasterSender; -import com.sap.sse.replication.UnsentOperationsToMasterSender; +import com.sap.sse.replication.OperationsToMasterSendingQueue; import com.sap.sse.util.ThreadPoolUtil; /** @@ -29,7 +29,7 @@ import com.sap.sse.util.ThreadPoolUtil; * @author Axel Uhl (d043530) * */ -public class UnsentOperationsSenderJob implements UnsentOperationsToMasterSender, Runnable { +public class UnsentOperationsSenderJob implements OperationsToMasterSendingQueue, Runnable { private static final Logger logger = Logger.getLogger(UnsentOperationsSenderJob.class.getName()); private final static Duration MAX_WAIT_TIME_BETWEEN_ATTEMPTS = Duration.ONE_MINUTE; @@ -49,11 +49,11 @@ public class UnsentOperationsSenderJob implements UnsentOperationsToMasterSender } private void resetWaitDuration() { - nextWaitDuration = Duration.ONE_SECOND; + nextWaitDuration = Duration.ONE_MILLISECOND; } @Override - public synchronized , T> void retrySendingLater( + public synchronized , T> void scheduleForSending( OperationWithResult operationWithResult, OperationsToMasterSender sender) { queue.addLast(new Pair<>(operationWithResult, sender)); @@ -74,14 +74,30 @@ public class UnsentOperationsSenderJob implements UnsentOperationsToMasterSender @Override public void run() { - synchronized (this) { // FIXME synchronization too coarse-grained - while (!queue.isEmpty() && tryToSend(queue.getFirst())) { - queue.removeFirst(); - resetWaitDuration(); + boolean empty = false; + Pair, OperationsToMasterSender> first = null; + boolean sendOk = true; + while (!empty && sendOk) { + first = queue.peekFirst(); + if (first != null) { + sendOk = tryToSend(first); + } else { + sendOk = false; } - scheduled = false; - if (!queue.isEmpty()) { - ensureScheduled(); + synchronized (this) { // FIXME synchronization too coarse-grained + if (sendOk) { + resetWaitDuration(); + queue.removeFirst(); + } else { + incrementWaitDuration(); + // stop this loop (because sendOk==false) and re-schedule + scheduled = false; + ensureScheduled(); + } + empty = queue.isEmpty(); + if (empty) { + scheduled = false; + } } } } @@ -104,8 +120,9 @@ public class UnsentOperationsSenderJob implements UnsentOperationsToMasterSender result = true; } catch (IOException e) { result = false; - incrementWaitTime(); - logger.log(Level.INFO, "Error re-sending operation "+operation+" to master "+sender.getMasterDescriptor()+ + // remove the operation that failed to arrive on the master server from those marked as sent to master for now: + sender.hasSentOperationToMaster(operation); + logger.log(Level.INFO, "Error (re-)sending operation "+operation+" to master "+sender.getMasterDescriptor()+ ". Will try again in "+nextWaitDuration); } return result; @@ -114,7 +131,7 @@ public class UnsentOperationsSenderJob implements UnsentOperationsToMasterSender /** * Doubles the wait duration, capping at {@link #MAX_WAIT_TIME_BETWEEN_ATTEMPTS}. */ - private void incrementWaitTime() { + private void incrementWaitDuration() { if (nextWaitDuration.compareTo(MAX_WAIT_TIME_BETWEEN_ATTEMPTS) < 0) { nextWaitDuration = nextWaitDuration.times(2); if (nextWaitDuration.compareTo(MAX_WAIT_TIME_BETWEEN_ATTEMPTS) > 0) { diff --git a/java/com.sap.sse.security.persistence/META-INF/MANIFEST.MF b/java/com.sap.sse.security.persistence/META-INF/MANIFEST.MF index fe52fab37ad..04124beb5ec 100755 --- a/java/com.sap.sse.security.persistence/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.security.persistence/META-INF/MANIFEST.MF @@ -13,6 +13,6 @@ Import-Package: org.apache.shiro.session;version="1.2.2", org.osgi.framework;version="1.3.0" Bundle-ActivationPolicy: lazy Require-Bundle: com.sap.sse.mongodb, - org.eclipse.orbit.mongodb;bundle-version="3.2.2", - com.sap.sse.common + com.sap.sse.common, + org.mongodb.mongo-java-driver;bundle-version="3.6.4" Export-Package: com.sap.sse.security.persistence diff --git a/java/com.sap.sse.security.replication.test/META-INF/MANIFEST.MF b/java/com.sap.sse.security.replication.test/META-INF/MANIFEST.MF index 5a9352a6de5..a9edaa96566 100644 --- a/java/com.sap.sse.security.replication.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.security.replication.test/META-INF/MANIFEST.MF @@ -15,7 +15,7 @@ Require-Bundle: org.hamcrest;bundle-version="1.1.0", com.sap.sse.mail.replication.testsupport, org.objenesis;bundle-version="2.1.0", org.mockito.mockito-core;bundle-version="1.10.14", - org.eclipse.orbit.mongodb;bundle-version="3.2.2", - com.sap.sse.replication;bundle-version="1.0.0" + com.sap.sse.replication;bundle-version="1.0.0", + org.mongodb.mongo-java-driver;bundle-version="3.6.4" Import-Package: org.osgi.util.tracker;version="1.5.1" Automatic-Module-Name: com.sap.sse.security.replication.test diff --git a/java/com.sap.sse.security.test/META-INF/MANIFEST.MF b/java/com.sap.sse.security.test/META-INF/MANIFEST.MF index 17ba5a44c9d..2257e9f8a42 100755 --- a/java/com.sap.sse.security.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.security.test/META-INF/MANIFEST.MF @@ -10,7 +10,7 @@ Require-Bundle: org.junit;bundle-version="4.8.2", com.sap.sse.security.userstore.mongodb, com.sap.sse.mongodb, com.sap.sse.mail, - org.eclipse.orbit.mongodb;bundle-version="3.2.2", - jcl.over.slf4j;bundle-version="1.6.4" + jcl.over.slf4j;bundle-version="1.6.4", + org.mongodb.mongo-java-driver;bundle-version="3.6.4" Import-Package: org.apache.shiro.session.mgt Automatic-Module-Name: com.sap.sse.security.test diff --git a/java/com.sap.sse.security.userstore.mongodb/META-INF/MANIFEST.MF b/java/com.sap.sse.security.userstore.mongodb/META-INF/MANIFEST.MF index abdb322ae55..879ceea5c28 100644 --- a/java/com.sap.sse.security.userstore.mongodb/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.security.userstore.mongodb/META-INF/MANIFEST.MF @@ -15,7 +15,7 @@ Require-Bundle: com.sap.sse.security, com.sap.sse.common, com.sap.sse.security.common, com.sap.sse.shared.android, - org.eclipse.orbit.mongodb;bundle-version="3.2.2" + org.mongodb.mongo-java-driver;bundle-version="3.6.4" Export-Package: com.sap.sse.security.userstore.mongodb, com.sap.sse.security.userstore.mongodb.impl;x-friends:="com.sap.sse.security.test" Automatic-Module-Name: com.sap.sse.security.userstore.mongodb diff --git a/java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java b/java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java index edd0821c0ac..4fb6af1e822 100644 --- a/java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java +++ b/java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java @@ -75,7 +75,7 @@ import com.sap.sse.replication.OperationWithResultWithIdWrapper; import com.sap.sse.replication.OperationsToMasterSender; import com.sap.sse.replication.ReplicationMasterDescriptor; import com.sap.sse.replication.ReplicationService; -import com.sap.sse.replication.UnsentOperationsToMasterSender; +import com.sap.sse.replication.OperationsToMasterSendingQueue; import com.sap.sse.security.BearerAuthenticationToken; import com.sap.sse.security.ClientUtils; import com.sap.sse.security.Credential; @@ -128,10 +128,10 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat /** * This field is expected to be set by the {@link ReplicationService} once it has "adopted" this replicable. * The {@link ReplicationService} "injects" this service so it can be used here as a delegate for the - * {@link UnsentOperationsToMasterSender#retrySendingLater(OperationWithResult, OperationsToMasterSender)} + * {@link OperationsToMasterSendingQueue#scheduleForSending(OperationWithResult, OperationsToMasterSender)} * method. */ - private UnsentOperationsToMasterSender unsentOperationsToMasterSender; + private OperationsToMasterSendingQueue unsentOperationsToMasterSender; private static Ini shiroConfiguration; static { @@ -1160,15 +1160,15 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat } @Override - public void setUnsentOperationToMasterSender(UnsentOperationsToMasterSender service) { + public void setUnsentOperationToMasterSender(OperationsToMasterSendingQueue service) { this.unsentOperationsToMasterSender = service; } @Override - public , T> void retrySendingLater( + public , T> void scheduleForSending( OperationWithResult operationWithResult, OperationsToMasterSender sender) { if (unsentOperationsToMasterSender != null) { - unsentOperationsToMasterSender.retrySendingLater(operationWithResult, sender); + unsentOperationsToMasterSender.scheduleForSending(operationWithResult, sender); } } } diff --git a/java/target/refreshInstance.sh b/java/target/refreshInstance.sh index d7a7827c154..abae11a982f 100755 --- a/java/target/refreshInstance.sh +++ b/java/target/refreshInstance.sh @@ -288,8 +288,8 @@ if [[ $OPERATION == "auto-install" ]]; then elif [[ $OPERATION == "install-release" ]]; then INSTALL_FROM_RELEASE=$PARAM if [[ $INSTALL_FROM_RELEASE == "" ]]; then - echo "You need to provide the name of a release from http://releases.sapsailing.com/" - exit 1 + INSTALL_FROM_RELEASE=$(wget -O - http://releases.sapsailing.com/ 2>/dev/null | grep build- | tail -1 | sed -e 's/^.*\(build-[0-9]*\).*$/\1/') + echo "You didn't provide a release. Picking latest master build from http://releases.sapsailing.com/$INSTALL_FROM_RELEASE" fi # Honor the no-overrite setting if there is one diff --git a/java/target/start b/java/target/start index 33bdd8e486a..dd8e2771e05 100755 --- a/java/target/start +++ b/java/target/start @@ -143,7 +143,7 @@ if [[ $PARAM == "" ]] || [[ $PARAM == "no-update" ]]; then if [[ $SERVER_PORT == "" ]] || [[ $SERVER_ACTIVE == "" ]]; then echo "Something went wrong with startup. Please check the logs..." if [[ $SERVER_STARTUP_NOTIFY != "" ]]; then - echo "Startup of java server failed - check the logs ($SERVER_ACTIVE)\n\n$NETSTAT\n$PROCESS_INFO" | mail -r simon.marcel.pamies@sap.com -s "Server $INSTANCE_ID startup failed!" $SERVER_STARTUP_NOTIFY + echo "Startup of java server $SERVER_NAME on $INSTANCE_ID failed - check the logs ($SERVER_ACTIVE)\n\n$NETSTAT\n$PROCESS_INFO" | mail -r simon.marcel.pamies@sap.com -s "Server $INSTANCE_ID startup failed!" $SERVER_STARTUP_NOTIFY fi exit fi @@ -153,9 +153,9 @@ if [[ $PARAM == "" ]] || [[ $PARAM == "no-update" ]]; then if [[ $SERVER_STARTUP_NOTIFY != "" ]]; then if [[ ! -z "$ON_AMAZON" ]]; then - echo "Go to http://$INSTANCE_DNS:$SERVER_PORT to reach your instance!" | mail -s "Server $INSTANCE_ID started up!" -a env.sh $SERVER_STARTUP_NOTIFY + echo "Go to http://$INSTANCE_DNS:$SERVER_PORT to reach your instance!" | mail -s "Server $SERVER_NAME on $INSTANCE_ID started up!" -a env.sh $SERVER_STARTUP_NOTIFY else - echo "OK" | mail -s "Server $INSTANCE_ID started up!" -a env.sh $SERVER_STARTUP_NOTIFY + echo "OK" | mail -s "Server $SERVER_NAME on $INSTANCE_ID started up!" -a env.sh $SERVER_STARTUP_NOTIFY fi fi elif [[ $PARAM == "debug" ]]; then diff --git a/java/target/status b/java/target/status index d52eff58c4e..bb2d572dee8 100755 --- a/java/target/status +++ b/java/target/status @@ -12,6 +12,7 @@ echo "TELNET_PORT: $TELNET_PORT" echo "MONGODB_HOST: $MONGODB_HOST" echo "MONGODB_PORT: $MONGODB_PORT" echo "MONGODB_NAME: $MONGODB_NAME" +echo "MONGODB_URI: $MONGODB_URI" echo "EXPEDITION_PORT: $EXPEDITION_PORT" echo "REPLICATION_HOST: $REPLICATION_HOST" echo "REPLICATION_CHANNEL: $REPLICATION_CHANNEL" diff --git a/wiki/info/landscape/creating-ec2-mongodb-image-from-scratch.md b/wiki/info/landscape/creating-ec2-mongodb-image-from-scratch.md new file mode 100644 index 00000000000..b91319d4aac --- /dev/null +++ b/wiki/info/landscape/creating-ec2-mongodb-image-from-scratch.md @@ -0,0 +1,102 @@ +Setting up an EC2 Image for a MongoDB Replica Set from Scratch + +## Image and Basic Packages + +Start with an Amazon Linux 2 AMI (HVM), SSD Volume Type - ami-0fad7378adf284ce0 (64-bit x86) image. + +Add the yum repository for MongoDB 3.6 by creating ``/etc/yum.repos.d/mongodb-org-3.6.repo`` as follow: + +``` +[mongodb-org-3.6] +name=MongoDB Repository +baseurl=https://repo.mongodb.org/yum/amazon/2013.03/mongodb-org/3.6/x86_64/ +gpgcheck=1 +enabled=1 +gpgkey=https://www.mongodb.org/static/pgp/server-3.6.asc +``` + +Then: + +``` +yum install -y mongodb-org-server mongodb-org-mongos mongodb-org-shell mongodb-org-tools +mkfs.xfs /dev/nvme0n1 +mount /dev/nvme0n1 /var/lib/mongo +``` + +## MongoDB Set-Up + +In file ``/etc/mongod.conf`` comment the line +``` +# bindIp: 127.0.0.1 # Listen to local interface only, comment to listen on all interfaces. +``` +and instead add +``` + bindIp: 0.0.0.0 +``` + +so that MongoDB listens on all interfaces, not only localhost. Furthermore, set the ``directoryPerDB`` property to ``true`` and provide a replication set name using the ``replication.replSetName`` property. + +Here is the full ``/etc/mongod.conf`` file: + +``` +# mongod.conf + +# for documentation of all options, see: +# http://docs.mongodb.org/manual/reference/configuration-options/ + +# where to write logging data. +systemLog: + destination: file + logAppend: true + path: /var/log/mongodb/mongod.log + +# Where and how to store data. +storage: + dbPath: /var/lib/mongo + journal: + enabled: true + directoryPerDB: true +# engine: +# mmapv1: +# wiredTiger: + +# how the process runs +processManagement: + fork: true # fork and run in background + pidFilePath: /var/run/mongodb/mongod.pid # location of pidfile + timeZoneInfo: /usr/share/zoneinfo + +# network interfaces +net: + port: 27017 +# bindIp: 127.0.0.1 # Listen to local interface only, comment to listen on all interfaces. + bindIp: 0.0.0.0 + +#security: + +#operationProfiling: + +replication: + replSetName: live + +#sharding: + +## Enterprise-Only Options + +#auditLog: + +#snmp: +``` + +To deal with the set-up of the ephemeral volume, there are a number of ``systemd`` units that can be found in the git repository at ``configuration/mongo_instance_setup``. The ``ephemeralvolume.service`` ensures that the volume exists and is formatted with the XFS file system. This service uses the ``/usr/local/bin/ephemeralvolume`` script to do so. After that, the volume is mounted to ``/var/lib/mongo`` using the ``var-lib-mongo.mount`` unit. The ownerships are adjusted after that by the ``chownvarlibmongo.service`` unit to the ``mongod`` user. This is then also the last unit required to run before the ``mongod.service``. + +Then execute ``systemctl enable mongod.service`` to launch the MongoDB process and ensure it is always launched when the instance (re-)boots. + +## MongoDB Replica Set Configuration + +Connect to the MongoDB on that instance, then issue the command ``rs.initiate()`` in order to turn the instance into the "seed" of a replica set. You can then, for the time being, ``quit()`` the mongo shell. Re-connecting, e.g., with ``mongo "mongodb://localhost:27017/?replicaSet=live"``, will show the ``PRIMARY`` of the new replica set. + +Connected to the PRIMARY using the ``mongo`` shell, a replica can be added using ``rs.add({host: "hostname:port"})``. For additional options see [here](https://docs.mongodb.com/manual/reference/method/rs.add/). To add a hidden replica on ``dbserver.internal.sapsailing.com:10203``, use ``rs.add({host: "dbserver.internal.sapsailing.com:10203", buildIndexes: false, hidden: true, priority: 0})``. + + +TODO: automate the initialization and replica set extension using "Addition Details" in the instance; create a MongoDB script that is executed during start-up; if no replica set exists and no user detail specifies where the primary is, run ``rs.initiate()``. If a replica set already exists, leave things unchanged. If no replica set exists and in a user detail something like ``REPLICA_SET_NAME=...`` and ``REPLICA_SET_PRIMARY=...`` is provided, add the local node as a secondary to the primary / replica set specified.