diff --git a/docker/Dockerfile.tpl b/docker/Dockerfile.tpl index 928e7f13cc5..325eb680513 100755 --- a/docker/Dockerfile.tpl +++ b/docker/Dockerfile.tpl @@ -1,4 +1,4 @@ -FROM donaldduck70/sapjvm8:8.1.073 +FROM donaldduck70/sapjvm8:8.1.078 LABEL maintainer=axel.uhl@sap.com # Download and extract the release WORKDIR /home/sailing/servers/server @@ -12,6 +12,7 @@ RUN wget -O /tmp/RELEASE.tar.gz http://releases.sapsailing.com/RELEASE/RELEASE.t && tar xzvpf /tmp/RELEASE.tar.gz \ && rm /tmp/RELEASE.tar.gz COPY env.sh . +RUN cat env-default-rules.sh >>env.sh COPY start . COPY JavaSE-11.profile . EXPOSE 8888 14888 8000 7091 6666 diff --git a/docker/Dockerfile_sapjvm b/docker/Dockerfile_sapjvm index ee4d0840fd5..0689219527c 100644 --- a/docker/Dockerfile_sapjvm +++ b/docker/Dockerfile_sapjvm @@ -1,12 +1,13 @@ -FROM buildpack-deps:stretch-scm +FROM buildpack-deps:latest LABEL maintainer=axel.uhl@sap.com # Download and extract the SAP JVM 8 ENV PATH=${PATH}:/opt/sapjvm_8/bin ENV JAVA_HOME=/opt/sapjvm_8 WORKDIR /opt RUN apt-get update \ - && apt-get install -y wget unzip \ - && curl --cookie eula_3_1_agreed=tools.hana.ondemand.com/developer-license-3_1.txt "https://tools.hana.ondemand.com/additional/sapjvm-7.1.073-linux-x64.zip" --output sapjvm8-linux-x64.zip \ + && apt-get install -y wget apt-utils unzip \ + && apt-get -y upgrade \ + && curl --cookie eula_3_1_agreed=tools.hana.ondemand.com/developer-license-3_1.txt "https://tools.hana.ondemand.com/additional/sapjvm-8.1.078-linux-x64.zip" --output sapjvm8-linux-x64.zip \ && unzip sapjvm8-linux-x64.zip \ && rm sapjvm8-linux-x64.zip \ && echo "export JAVA_HOME=/opt/sapjvm_8; export PATH=\${PATH}:/opt/sapjvm_8/bin" > /etc/profile.d/javahome.sh diff --git a/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/websocket/WebSocketTest.java b/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/websocket/WebSocketTest.java index 61d16c2e709..7372ea7be41 100755 --- a/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/websocket/WebSocketTest.java +++ b/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/websocket/WebSocketTest.java @@ -31,6 +31,7 @@ import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; import org.eclipse.jetty.websocket.client.WebSocketClient; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.Timeout; @@ -131,6 +132,7 @@ public class WebSocketTest { Activator.getInstance().setSecurityService(null); } + @Ignore("echo.websocket.org is (2021-08-19) down; http://www.websocket.org/index.html says ''Service no longer available''") @Test public void simpleWebSocketEchoTest() throws Exception { String destUri = "ws://echo.websocket.org"; // wss currently doesn't seem to work with Jetty 9.0.4 WebSocket implementation diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/MongoSensorFixStoreFactory.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/MongoSensorFixStoreFactory.java index 1c5ba98c4de..182a89f56cf 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/MongoSensorFixStoreFactory.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/MongoSensorFixStoreFactory.java @@ -3,6 +3,7 @@ package com.sap.sailing.domain.persistence.racelog.tracking; import java.net.UnknownHostException; import com.mongodb.MongoException; +import com.mongodb.ReadConcern; import com.mongodb.WriteConcern; import com.sap.sailing.domain.persistence.DomainObjectFactory; import com.sap.sailing.domain.persistence.MongoObjectFactory; @@ -24,9 +25,10 @@ public interface MongoSensorFixStoreFactory { * Like {@link #getMongoGPSFixStore(MongoObjectFactory, DomainObjectFactory, TypeBasedServiceFinderFactory)}, but * additionally the write concern for the MongoDB writes can be controlled which otherwise usually defaults * to {@link WriteConcern#UNACKNOWLEDGED} for the fixes as well as the metadata. + * @param readConcern TODO */ MongoSensorFixStore getMongoGPSFixStore(MongoObjectFactory mongoObjectFactory, DomainObjectFactory domainObjectFactory, - TypeBasedServiceFinderFactory serviceFinderFactory, WriteConcern writeConcern) + TypeBasedServiceFinderFactory serviceFinderFactory, ReadConcern readConcern, WriteConcern writeConcern) throws UnknownHostException, MongoException; } \ No newline at end of file diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MetadataCollection.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MetadataCollection.java index 6a2390fb6da..73f0f9dc0dc 100755 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MetadataCollection.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MetadataCollection.java @@ -48,13 +48,15 @@ public class MetadataCollection extends MongoFixHandler { private final MongoCollection metadataCollection; private final ConcurrentHashMap metadataUpdaters; private final WriteConcern writeConcern; + private final ReadConcern readConcern; public MetadataCollection(MongoObjectFactoryImpl mongoOF, TypeBasedServiceFinder> fixServiceFinder, - TypeBasedServiceFinder deviceServiceFinder, WriteConcern writeConcern) { + TypeBasedServiceFinder deviceServiceFinder, ReadConcern readConcern, WriteConcern writeConcern) { super(fixServiceFinder, deviceServiceFinder); this.metadataCollection = mongoOF.getGPSFixMetadataCollection(); this.metadataUpdaters = new ConcurrentHashMap<>(); + this.readConcern = readConcern; this.writeConcern = writeConcern; } @@ -74,7 +76,7 @@ public class MetadataCollection extends MongoFixHandler { */ private Document findMetadataObjectInternal(DeviceIdentifier device) throws TransformationException { Bson query = getDeviceQuery(device); - Document result = metadataCollection.withReadConcern(ReadConcern.MAJORITY).find(query).first(); + Document result = metadataCollection.withReadConcern(readConcern).find(query).first(); return result; } diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MongoSensorFixStoreFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MongoSensorFixStoreFactoryImpl.java index 3d107366420..6920b3b7820 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MongoSensorFixStoreFactoryImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MongoSensorFixStoreFactoryImpl.java @@ -3,6 +3,7 @@ package com.sap.sailing.domain.persistence.racelog.tracking.impl; import java.net.UnknownHostException; import com.mongodb.MongoException; +import com.mongodb.ReadConcern; import com.mongodb.WriteConcern; import com.sap.sailing.domain.persistence.DomainObjectFactory; import com.sap.sailing.domain.persistence.MongoObjectFactory; @@ -21,7 +22,7 @@ public class MongoSensorFixStoreFactoryImpl implements MongoSensorFixStoreFactor @Override public MongoSensorFixStore getMongoGPSFixStore(MongoObjectFactory mongoObjectFactory, DomainObjectFactory domainObjectFactory, TypeBasedServiceFinderFactory serviceFinderFactory, - WriteConcern writeConcern) throws UnknownHostException, MongoException { - return new MongoSensorFixStoreImpl(mongoObjectFactory, domainObjectFactory, serviceFinderFactory, writeConcern); + ReadConcern readConcern, WriteConcern writeConcern) throws UnknownHostException, MongoException { + return new MongoSensorFixStoreImpl(mongoObjectFactory, domainObjectFactory, serviceFinderFactory, readConcern, writeConcern); } } diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MongoSensorFixStoreImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MongoSensorFixStoreImpl.java index 08f906f5f8b..a3e80f70f48 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MongoSensorFixStoreImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MongoSensorFixStoreImpl.java @@ -17,6 +17,7 @@ import java.util.logging.Logger; import org.bson.Document; import org.bson.conversions.Bson; +import com.mongodb.ReadConcern; import com.mongodb.WriteConcern; import com.mongodb.client.FindIterable; import com.mongodb.client.MongoCollection; @@ -72,16 +73,16 @@ public class MongoSensorFixStoreImpl extends MongoFixHandler implements MongoSen public MongoSensorFixStoreImpl(MongoObjectFactory mongoObjectFactory, DomainObjectFactory domainObjectFactory, TypeBasedServiceFinderFactory serviceFinderFactory) { - this(mongoObjectFactory, domainObjectFactory, serviceFinderFactory, WriteConcern.UNACKNOWLEDGED); + this(mongoObjectFactory, domainObjectFactory, serviceFinderFactory, ReadConcern.DEFAULT, WriteConcern.UNACKNOWLEDGED); } public MongoSensorFixStoreImpl(MongoObjectFactory mongoObjectFactory, DomainObjectFactory domainObjectFactory, - TypeBasedServiceFinderFactory serviceFinderFactory, WriteConcern writeConcern) { + TypeBasedServiceFinderFactory serviceFinderFactory, ReadConcern readConcern, WriteConcern writeConcern) { super(serviceFinderFactory != null ? createFixServiceFinder(serviceFinderFactory) : null, serviceFinderFactory != null ? serviceFinderFactory.createServiceFinder(DeviceIdentifierMongoHandler.class) : null); mongoOF = (MongoObjectFactoryImpl) mongoObjectFactory; fixesCollection = mongoOF.getGPSFixCollection(); - metadataCollection = new MetadataCollection(mongoOF, fixServiceFinder, deviceServiceFinder, writeConcern); + metadataCollection = new MetadataCollection(mongoOF, fixServiceFinder, deviceServiceFinder, readConcern, writeConcern); this.writeConcern = writeConcern; } diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/AbstractGPSFixStoreTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/AbstractGPSFixStoreTest.java index 54953ccee2e..6e076cc0d08 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/AbstractGPSFixStoreTest.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/AbstractGPSFixStoreTest.java @@ -10,6 +10,7 @@ import java.util.UUID; import org.junit.After; import org.junit.Before; +import com.mongodb.ReadConcern; import com.mongodb.WriteConcern; import com.sap.sailing.domain.abstractlog.race.impl.RaceLogImpl; import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; @@ -84,7 +85,7 @@ public class AbstractGPSFixStoreTest extends RaceLogTrackingTestHelper { regattaLog = new RegattaLogImpl("regattalog"); dropPersistedData(); store = new MongoSensorFixStoreImpl(service.getMongoObjectFactory(), service.getDomainObjectFactory(), - serviceFinderFactory, WriteConcern.MAJORITY); + serviceFinderFactory, ReadConcern.MAJORITY, WriteConcern.MAJORITY); } @After diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/RaceLogFixTrackerManagerTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/RaceLogFixTrackerManagerTest.java index 72b961dff3a..c01fef85c57 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/RaceLogFixTrackerManagerTest.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/RaceLogFixTrackerManagerTest.java @@ -14,6 +14,7 @@ import org.junit.Before; import org.junit.Test; import com.mongodb.MongoException; +import com.mongodb.ReadConcern; import com.mongodb.WriteConcern; import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl; @@ -93,7 +94,7 @@ public class RaceLogFixTrackerManagerTest { raceLog2 = new RaceLogImpl("racelog2"); regattaLog = new RegattaLogImpl("regattalog"); store = new MongoSensorFixStoreImpl(PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(), - PersistenceFactory.INSTANCE.getDefaultDomainObjectFactory(), serviceFinderFactory, WriteConcern.MAJORITY); + PersistenceFactory.INSTANCE.getDefaultDomainObjectFactory(), serviceFinderFactory, ReadConcern.MAJORITY, WriteConcern.MAJORITY); Course course = new CourseImpl("course", Arrays.asList(new Waypoint[] { new WaypointImpl(mark), new WaypointImpl(mark2) })); Map competitorsAndBoats = new HashMap<>(); diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java index 233eb215926..2541d56e3a9 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java @@ -24,6 +24,7 @@ import org.junit.Before; import org.junit.Test; import com.mongodb.MongoException; +import com.mongodb.ReadConcern; import com.mongodb.WriteConcern; import com.mongodb.client.MongoDatabase; import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; @@ -153,7 +154,7 @@ public class SensorFixStoreAndLoadTest { raceLog.add(new RaceLogEndOfTrackingEventImpl(new MillisecondsTimePoint(END_OF_TRACKING), author, 0)); regattaLog = new RegattaLogImpl("regattalog"); store = new MongoSensorFixStoreImpl(PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(), - PersistenceFactory.INSTANCE.getDefaultDomainObjectFactory(), serviceFinderFactory, WriteConcern.MAJORITY); + PersistenceFactory.INSTANCE.getDefaultDomainObjectFactory(), serviceFinderFactory, ReadConcern.MAJORITY, WriteConcern.MAJORITY); regattaLog.add(new RegattaLogDefineMarkEventImpl(new MillisecondsTimePoint(1), author, new MillisecondsTimePoint(1), 0, mark)); regattaLog.add(new RegattaLogDefineMarkEventImpl(new MillisecondsTimePoint(2), author, diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreTest.java index e2dd2e47b0c..941312782e9 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreTest.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreTest.java @@ -8,11 +8,13 @@ import java.util.List; import java.util.function.Consumer; import org.junit.After; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import com.mongodb.MongoException; +import com.mongodb.ReadConcern; import com.mongodb.WriteConcern; import com.mongodb.client.MongoDatabase; import com.sap.sailing.domain.common.DeviceIdentifier; @@ -37,8 +39,6 @@ import com.sap.sse.common.impl.DegreeBearingImpl; import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.TimeRangeImpl; -import org.junit.Assert; - public class SensorFixStoreTest { private static final long FIX_TIMESTAMP = 110; private static final long FIX_TIMESTAMP2 = 120; @@ -63,7 +63,7 @@ public class SensorFixStoreTest { private void newStore() { store = new MongoSensorFixStoreImpl(PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(), - PersistenceFactory.INSTANCE.getDefaultDomainObjectFactory(), serviceFinderFactory, WriteConcern.MAJORITY); + PersistenceFactory.INSTANCE.getDefaultDomainObjectFactory(), serviceFinderFactory, ReadConcern.MAJORITY, WriteConcern.MAJORITY); } @After diff --git a/java/com.sap.sailing.domain.tractracadapter.persistence/src/com/sap/sailing/domain/tractracadapter/persistence/impl/TracTracConnectivityParamsHandler.java b/java/com.sap.sailing.domain.tractracadapter.persistence/src/com/sap/sailing/domain/tractracadapter/persistence/impl/TracTracConnectivityParamsHandler.java index 818af621af4..96946811d70 100644 --- a/java/com.sap.sailing.domain.tractracadapter.persistence/src/com/sap/sailing/domain/tractracadapter/persistence/impl/TracTracConnectivityParamsHandler.java +++ b/java/com.sap.sailing.domain.tractracadapter.persistence/src/com/sap/sailing/domain/tractracadapter/persistence/impl/TracTracConnectivityParamsHandler.java @@ -149,8 +149,7 @@ public class TracTracConnectivityParamsHandler extends AbstractRaceTrackingConne final String creatorName = SessionUtils.getPrincipal().toString(); final TracTracConfigurationImpl tracTracConfiguration = new TracTracConfigurationImpl(creatorName, tractracRace.getEvent().getName(), jsonURL, (params.getLiveURI() == null ? null : params.getLiveURI().toString()), - /* stored URI */ params.isPreferReplayIfAvailable() && params.isReplayRace(tractracRace) - ? null // we mainly want to enable the user to list the event's races again in case they are removed; + /* stored URI */ params.isReplayRace(tractracRace) ? null // we mainly want to enable the user to list the event's races again in case they are removed; : (params.getStoredURI() == null ? null : params.getStoredURI().toString()), // live/stored stuff comes from the tracking params params.getCourseDesignUpdateURI()==null?null:params.getCourseDesignUpdateURI().toString(), params.getTracTracUsername(), params.getTracTracPassword()); tractracMongoObjectFactory.updateTracTracConfiguration(tracTracConfiguration); diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/TracTracConfiguration.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/TracTracConfiguration.java index 26cf45a021a..78fc26746da 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/TracTracConfiguration.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/TracTracConfiguration.java @@ -56,7 +56,6 @@ public interface TracTracConfiguration extends WithQualifiedObjectIdentifier { } public static TypeRelativeObjectIdentifier getTypeRelativeObjectIdentifier(String jsonUrl, String username) { - return username == null ? new TypeRelativeObjectIdentifier(jsonUrl) : new TypeRelativeObjectIdentifier(jsonUrl, username); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/CourseChangeBasedTrackApproximation.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/CourseChangeBasedTrackApproximation.java index 0ef5af65b11..076532312a2 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/CourseChangeBasedTrackApproximation.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/CourseChangeBasedTrackApproximation.java @@ -1,5 +1,7 @@ package com.sap.sailing.domain.tracking.impl; +import java.io.IOException; +import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; @@ -7,6 +9,7 @@ import java.util.Comparator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.NavigableSet; import java.util.TreeSet; import com.sap.sailing.domain.base.BoatClass; @@ -49,7 +52,12 @@ public class CourseChangeBasedTrackApproximation implements Serializable, GPSTra private final GPSFixTrack track; private final BoatClass boatClass; private final FixWindow fixWindow; - private final TreeSet maneuverCandidates; + + /** + * The following set is a {@code synchronized} navigable set; all simple access methods synchronize trivially on it. + * All iterations, including serializing this object, must {@code synchronize} on it. + */ + private final NavigableSet maneuverCandidates; /** * The fix window consists of the list of fixes, a corresponding list with the course @@ -309,11 +317,17 @@ public class CourseChangeBasedTrackApproximation implements Serializable, GPSTra this.track = track; this.boatClass = boatClass; this.fixWindow = new FixWindow(); - this.maneuverCandidates = new TreeSet<>(TimedComparator.INSTANCE); + this.maneuverCandidates = Collections.synchronizedNavigableSet(new TreeSet<>(TimedComparator.INSTANCE)); track.addListener(this); addAllFixesOfTrack(); } + private void writeObject(ObjectOutputStream oos) throws IOException { + synchronized (maneuverCandidates) { + oos.defaultWriteObject(); + } + } + private synchronized void addAllFixesOfTrack() { track.lockForRead(); try { @@ -424,7 +438,9 @@ public class CourseChangeBasedTrackApproximation implements Serializable, GPSTra } } if (from.before(to)) { - result = new ArrayList<>(maneuverCandidates.subSet(createDummyFix(from), /* fromInclusive */ true, createDummyFix(to), /* toInclusive */ true)); + synchronized (maneuverCandidates) { + result = new ArrayList<>(maneuverCandidates.subSet(createDummyFix(from), /* fromInclusive */ true, createDummyFix(to), /* toInclusive */ true)); + } } else { result = Collections.emptySet(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractRegattaWithSeriesAndFleetsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractRegattaWithSeriesAndFleetsDialog.java index 1f9eee58489..85d571e5e73 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractRegattaWithSeriesAndFleetsDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractRegattaWithSeriesAndFleetsDialog.java @@ -283,7 +283,7 @@ public abstract class AbstractRegattaWithSeriesAndFleetsDialog extends DataEn sailingEventsListBox.addItem(stringMessages.selectSailingEvent(), stringMessages.selectSailingEvent()); final Iterable eventsSortedByName = Util.stream(existingEvents) .filter(event->userService.hasPermission(event, DefaultActions.UPDATE)) - .sorted(Util.naturalNamedComparator())::iterator; + .sorted(Util.naturalNamedComparator(/* caseSensitive */ true))::iterator; for (EventDTO event : eventsSortedByName) { sailingEventsListBox.addItem(event.getName(), event.getId().toString()); if (defaultEvent != null) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_da.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_da.properties index daed1a05691..a18fa357793 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_da.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_da.properties @@ -542,7 +542,7 @@ widthInPx=Bredde (pixel) heightInPx=Højde (pixel) tags=Tags size=Størrelse -duration=Varighed (t:mm:ss.ms) +duration=Varighed (t:mm:ss) addMediaTrack=Tilføj lyd/video addYoutubeTrack=Tilføj Youtube youtubeId=Youtube-id 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 5a59b7f966d..8cf12ee44fd 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 @@ -542,7 +542,7 @@ widthInPx=Ancho (píxeles) heightInPx=Alto (píxeles) tags=Etiquetas size=Tamaño -duration=Duración (h:mm:ss.ms) +duration=Duración (h:mm:ss) addMediaTrack=Añadir audio/vídeo addYoutubeTrack=Añadir Youtube youtubeId=ID de Youtube diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties index aef7eb8ad49..678c8d668d9 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties @@ -542,7 +542,7 @@ widthInPx=Largeur (pixel) heightInPx=Hauteur (pixel) tags=Balises size=Taille -duration=Durée (h:mm:ss.ms) +duration=Durée (h:mm:ss) addMediaTrack=Ajouter audio/vidéo addYoutubeTrack=Ajouter Youtube youtubeId=ID Youtube diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_it.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_it.properties index 4834e27498b..edbef4020cc 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_it.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_it.properties @@ -542,7 +542,7 @@ widthInPx=Larghezza (pixel) heightInPx=Altezza (pixel) tags=Tags size=Dimensione -duration=Durata (h:mm:ss.ms) +duration=Durata (h:mm:ss) addMediaTrack=Aggiungi audio/video addYoutubeTrack=Aggiungi Youtube youtubeId=ID Youtube 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 c85fd119a76..1a9eb3359fc 100644 --- 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 @@ -542,7 +542,7 @@ widthInPx=幅 (ピクセル) heightInPx=高さ (ピクセル) tags=タグ size=サイズ -duration=時間 (h:mm:ss.ms) +duration=時間 (h:mm:ss) addMediaTrack=音声/動画追加 addYoutubeTrack=YouTube 追加 youtubeId=YouTube ID diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties index 66c3ec93fd8..b76b2c6dc42 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties @@ -542,7 +542,7 @@ widthInPx=Largura (pixels) heightInPx=Altura (pixels) tags=Etiquetas size=Tamanho -duration=Duração (h:mm:ss.ms) +duration=Duração (h:mm:ss) addMediaTrack=Adicionar áudio/vídeo addYoutubeTrack=Adicionar Youtube youtubeId=ID do Youtube diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties index c0ebbe4dce5..f9eab9c9762 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -542,7 +542,7 @@ widthInPx=Ширина (в пикселях) heightInPx=Высота (в пикселях) tags=Теги size=Размер -duration=Длительность (ч:мм:сс.мс) +duration=Длительность (ч:мм:сс) addMediaTrack=Добавить аудио/видео addYoutubeTrack=Добавить Youtube youtubeId=Ид. Youtube diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_sl.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_sl.properties index 31cb92907c3..0fa4e05a9a7 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_sl.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_sl.properties @@ -542,7 +542,7 @@ widthInPx=Širina (slikovne pike) heightInPx=Višina (slikovne pike) tags=Oznake size=Velikost -duration=Trajanje (h:mm:ss.ms) +duration=Trajanje (h:mm:ss) addMediaTrack=Dodaj zvok/video addYoutubeTrack=Dodaj Youtube youtubeId=ID za Youtube diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties index e5c294fd938..08c2c4f858e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties @@ -542,7 +542,7 @@ widthInPx=宽度(像素) heightInPx=高度(像素) tags=标记 size=大小 -duration=持续时间 (h:mm:ss.ms) +duration=持续时间 (h:mm:ss) addMediaTrack=添加音频/视频 addYoutubeTrack=添加 Youtube youtubeId=Youtube 编号 diff --git a/java/com.sap.sailing.hanaexport/resources/cleartables.sql b/java/com.sap.sailing.hanaexport/resources/cleartables.sql index b4b1a033526..5e66acbc5d8 100755 --- a/java/com.sap.sailing.hanaexport/resources/cleartables.sql +++ b/java/com.sap.sailing.hanaexport/resources/cleartables.sql @@ -1,5 +1,4 @@ DELETE FROM SAILING."CompetitorRace" CASCADE; -DELETE FROM SAILING."Event" CASCADE; DELETE FROM SAILING."RaceResult" CASCADE; DELETE FROM SAILING."RaceStats" CASCADE; DELETE FROM SAILING."Leg" CASCADE; @@ -7,6 +6,7 @@ DELETE FROM SAILING."LegStats" CASCADE; DELETE FROM SAILING."Maneuver" CASCADE; DELETE FROM SAILING."Race" CASCADE; DELETE FROM SAILING."Regatta" CASCADE; +DELETE FROM SAILING."Event" CASCADE; DELETE FROM SAILING."Competitor" CASCADE; DELETE FROM SAILING."BoatClass" CASCADE; DELETE FROM SAILING."IRM" CASCADE; diff --git a/java/com.sap.sailing.hanaexport/resources/createtables.sql b/java/com.sap.sailing.hanaexport/resources/createtables.sql index 0f6c7dd18ea..ee1c1d31605 100755 --- a/java/com.sap.sailing.hanaexport/resources/createtables.sql +++ b/java/com.sap.sailing.hanaexport/resources/createtables.sql @@ -6,7 +6,7 @@ CREATE COLUMN TABLE SAILING."Event" ( "venue" NVARCHAR(255), "isListed" BOOLEAN, "description" NVARCHAR(5000), - "location" ST_POINT + "location" ST_POINT(3857) ); CREATE ROW TABLE SAILING."ScoringScheme" ( "id" NVARCHAR(255) PRIMARY KEY, @@ -49,14 +49,14 @@ CREATE COLUMN TABLE SAILING."Race" ( FOREIGN KEY ("regatta") REFERENCES SAILING."Regatta" ("name") ); CREATE TABLE SAILING."Competitor" ( - "id" NVARCHAR(36) PRIMARY KEY, + "id" NVARCHAR(255) PRIMARY KEY, "name" NVARCHAR(255) NOT NULL, "shortName" NVARCHAR(20), "nationality" NVARCHAR(3) NOT NULL, "sailNumber" NVARCHAR(255) ); CREATE TABLE SAILING."CompetitorRace" ( - "competitorId" NVARCHAR(36), + "competitorId" NVARCHAR(255), "race" NVARCHAR(255), "regatta" NVARCHAR(255), PRIMARY KEY ("competitorId", "race"), @@ -66,7 +66,7 @@ CREATE TABLE SAILING."CompetitorRace" ( CREATE TABLE SAILING."RaceResult" ( "regatta" NVARCHAR(255) NOT NULL, "raceColumn" NVARCHAR(255) NOT NULL, - "competitorId" NVARCHAR(36) NOT NULL, + "competitorId" NVARCHAR(255) NOT NULL, "points" DOUBLE, "discarded" BOOLEAN, "irm" NVARCHAR(4), @@ -76,7 +76,7 @@ CREATE TABLE SAILING."RaceResult" ( CREATE TABLE SAILING."RaceStats" ( "race" NVARCHAR(255) NOT NULL, "regatta" NVARCHAR(255) NOT NULL, - "competitorId" NVARCHAR(36) NOT NULL, + "competitorId" NVARCHAR(255) NOT NULL, "rankOneBased" INTEGER, "distanceSailedInMeters" DOUBLE, "elapsedTimeInSeconds" DOUBLE, @@ -105,7 +105,7 @@ CREATE TABLE SAILING."LegStats" ( "race" NVARCHAR(255) NOT NULL, "regatta" NVARCHAR(255) NOT NULL, "number" INTEGER NOT NULL, - "competitorId" NVARCHAR(36) NOT NULL, + "competitorId" NVARCHAR(255) NOT NULL, "rankOneBased" INTEGER, "distanceSailedInMeters" DOUBLE, "elapsedTimeInSeconds" DOUBLE, @@ -123,7 +123,7 @@ CREATE TABLE SAILING."LegStats" ( CREATE TABLE SAILING."Maneuver" ( "race" NVARCHAR(255) NOT NULL, "regatta" NVARCHAR(255) NOT NULL, - "competitorId" NVARCHAR(36) NOT NULL, + "competitorId" NVARCHAR(255) NOT NULL, "timepoint" TIMESTAMP NOT NULL, "type" NVARCHAR(20) NOT NULL, "newTack" NVARCHAR(20), diff --git a/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/HanaConnectionFactory.java b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/HanaConnectionFactory.java index 82ecb5de43b..4db76070c89 100755 --- a/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/HanaConnectionFactory.java +++ b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/HanaConnectionFactory.java @@ -2,11 +2,12 @@ package com.sap.sailing.hanaexport; import java.sql.Connection; import java.sql.SQLException; +import java.util.Optional; import com.sap.sailing.hanaexport.impl.HanaConnectionFactoryImpl; public interface HanaConnectionFactory { static HanaConnectionFactory INSTANCE = new HanaConnectionFactoryImpl(); - Connection getConnection() throws SQLException; + Connection getConnection(Optional dbEndpoint, Optional dbUser, Optional dbPassword) throws SQLException; } diff --git a/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/impl/HanaConnectionFactoryImpl.java b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/impl/HanaConnectionFactoryImpl.java index 002c18963fb..e2178806137 100755 --- a/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/impl/HanaConnectionFactoryImpl.java +++ b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/impl/HanaConnectionFactoryImpl.java @@ -6,6 +6,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.util.Optional; import java.util.logging.Logger; import com.sap.sailing.domain.common.BoatClassMasterdata; @@ -20,7 +21,7 @@ public class HanaConnectionFactoryImpl implements HanaConnectionFactory { public void test() throws SQLException { System.out.println("Java version: " + com.sap.db.jdbc.Driver.getJavaVersion()); System.out.println("Minimum supported Java version and SAP driver version number: " + com.sap.db.jdbc.Driver.getVersionInfo()); - final Connection connection = getConnection(); + final Connection connection = getConnection(Optional.empty(), Optional.empty(), Optional.empty()); if (connection == null) { logger.warning("Couldn't get database connection for end point "+System.getProperty(HANADB_ENDPOINT_PROPERTY_NAME)); } else { @@ -42,11 +43,33 @@ public class HanaConnectionFactoryImpl implements HanaConnectionFactory { } @Override - public Connection getConnection() throws SQLException { + public Connection getConnection(Optional dbEndpoint, Optional dbUser, Optional dbPassword) throws SQLException { + final String dbEndpointFromSystemProperty = System.getProperty(HANADB_ENDPOINT_PROPERTY_NAME); + final String dbUserFromSystemProperty = System.getProperty(HANADB_USERNAME_SYSTEM_PROPERTY_NAME); + final String dbPasswordFromSystemProperty = System.getProperty(HANADB_PASSWORD_SYSTEM_PROPERTY_NAME); + final String dbUrl = "jdbc:sap://"+dbEndpoint.orElseGet(()->{ + if (dbEndpointFromSystemProperty != null) { + return dbEndpointFromSystemProperty; + } else { + throw new IllegalStateException("Missing DB URL, either as parameter or from system property "+HANADB_ENDPOINT_PROPERTY_NAME); + } + }); return DriverManager.getConnection( - "jdbc:sap://"+System.getProperty(HANADB_ENDPOINT_PROPERTY_NAME)+"/?encrypt=true&validateCertificate=false", - System.getProperty(HANADB_USERNAME_SYSTEM_PROPERTY_NAME), - System.getProperty(HANADB_PASSWORD_SYSTEM_PROPERTY_NAME)); + dbUrl, + dbUser.orElseGet(()->{ + if (dbUserFromSystemProperty != null) { + return dbUserFromSystemProperty; + } else { + throw new IllegalStateException("Missing DB user name, either as parameter or from system property "+HANADB_USERNAME_SYSTEM_PROPERTY_NAME); + } + }), + dbPassword.orElseGet(()->{ + if (dbPasswordFromSystemProperty != null) { + return dbPasswordFromSystemProperty; + } else { + throw new IllegalStateException("Missing DB password, either as parameter or from system property "+HANADB_PASSWORD_SYSTEM_PROPERTY_NAME); + } + })); } public static void main(String[] args) throws SQLException { diff --git a/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/AbstractPreparedInsertStatement.java b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/AbstractPreparedInsertStatement.java new file mode 100755 index 00000000000..ed02472a1a4 --- /dev/null +++ b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/AbstractPreparedInsertStatement.java @@ -0,0 +1,40 @@ +package com.sap.sailing.hanaexport.jaxrs.api; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import com.sap.sse.common.Distance; +import com.sap.sse.common.Duration; + +public abstract class AbstractPreparedInsertStatement implements PreparedInsertStatement { + private final PreparedStatement preparedStatement; + + protected AbstractPreparedInsertStatement(PreparedStatement preparedStatement) { + this.preparedStatement = preparedStatement; + } + + @Override + public PreparedStatement getPreparedStatement() { + return preparedStatement; + } + + protected void setDouble(int parameterIndex, double value) throws SQLException { + if (Double.isNaN(value) || Double.isInfinite(value)) { + setDouble(parameterIndex, 0.0); + } else { + getPreparedStatement().setDouble(parameterIndex, value); + } + } + + protected int intOr0ForNull(Integer i) { + return i==null?0:i; + } + + protected double metersOr0ForNull(final Distance distance) { + return distance == null ? 0 : distance.getMeters(); + } + + protected double secondsOr0ForNull(final Duration duration) { + return duration == null ? 0 : duration.asSeconds(); + } +} diff --git a/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/HanaCloudSacExportResource.java b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/HanaCloudSacExportResource.java index a9d2583dde4..a70b91e7f50 100755 --- a/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/HanaCloudSacExportResource.java +++ b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/HanaCloudSacExportResource.java @@ -3,14 +3,18 @@ package com.sap.sailing.hanaexport.jaxrs.api; import java.io.IOException; import java.io.StringWriter; import java.sql.Connection; -import java.sql.Date; -import java.sql.PreparedStatement; import java.sql.SQLException; +import java.util.Arrays; +import java.util.HashSet; import java.util.LinkedHashMap; -import java.util.NavigableSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; +import javax.ws.rs.FormParam; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; @@ -20,40 +24,30 @@ import javax.ws.rs.core.Response; import org.apache.commons.io.IOUtils; import org.apache.shiro.SecurityUtils; -import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; -import com.sap.sailing.domain.base.CompetitorWithBoat; import com.sap.sailing.domain.base.Event; import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.base.Regatta; -import com.sap.sailing.domain.base.SpeedWithConfidence; import com.sap.sailing.domain.base.Waypoint; -import com.sap.sailing.domain.common.LegType; import com.sap.sailing.domain.common.ManeuverType; import com.sap.sailing.domain.common.MaxPointsReason; -import com.sap.sailing.domain.common.NoWindException; -import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.ScoringSchemeType; -import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.leaderboard.Leaderboard; +import com.sap.sailing.domain.leaderboard.LeaderboardGroup; +import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCalculationReuseCache; import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sailing.domain.tracking.Maneuver; -import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.TrackedLeg; -import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache; -import com.sap.sailing.domain.tracking.WindPositionMode; import com.sap.sailing.hanaexport.HanaConnectionFactory; +import com.sap.sailing.hanaexport.jaxrs.api.InsertRegattaStatement.RegattaAndEvent; import com.sap.sailing.server.interfaces.RacingEventService; import com.sap.sailing.shared.server.gateway.jaxrs.SharedAbstractSailingServerResource; import com.sap.sse.ServerInfo; -import com.sap.sse.common.Distance; -import com.sap.sse.common.Duration; -import com.sap.sse.common.Speed; import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util; import com.sap.sse.security.shared.TypeRelativeObjectIdentifier; @@ -63,37 +57,72 @@ import com.sap.sse.security.shared.impl.SecuredSecurityTypes; public class HanaCloudSacExportResource extends SharedAbstractSailingServerResource { private static final Logger logger = Logger.getLogger(HanaCloudSacExportResource.class.getName()); + private class AlreadyInsertedContent { + private final Set events; + private final Set competitors; + private final InsertCompetitorStatement insertCompetitors; + private final InsertEventStatement insertEvents; + public AlreadyInsertedContent(InsertEventStatement insertEvents, InsertCompetitorStatement insertCompetitors) { + events = new HashSet<>(); + competitors = new HashSet<>(); + this.insertEvents = insertEvents; + this.insertCompetitors = insertCompetitors; + } + + public void ensureEventIsInserted(Event event) throws SQLException { + if (!events.contains(event)) { + events.add(event); + insertEvents.insert(event); + } + } + + public void ensureCompetitorIsInserted(Competitor competitor) throws SQLException { + if (!competitors.contains(competitor)) { + competitors.add(competitor); + insertCompetitors.insert(competitor); + } + } +} + @POST @Produces("application/json;charset=UTF-8") @Path("clear") - public Response clear() throws SQLException, IOException { + public Response clear( + @FormParam("dbendpoint") String dbEndpoint, + @FormParam("dbuser") String dbUser, + @FormParam("dbpassword") String dbPassword) throws SQLException, IOException { SecurityUtils.getSubject().checkPermission(SecuredSecurityTypes.SERVER.getStringPermissionForTypeRelativeIdentifier( SecuredSecurityTypes.ServerActions.CAN_EXPORT_MASTERDATA, new TypeRelativeObjectIdentifier(ServerInfo.getName()))); logger.info("Clearing HANA Cloud SAILING DB on behalf of user "+SecurityUtils.getSubject().getPrincipal()); - final Connection connection = HanaConnectionFactory.INSTANCE.getConnection(); + final Connection connection = HanaConnectionFactory.INSTANCE.getConnection(Optional.ofNullable(dbEndpoint), + Optional.ofNullable(dbUser), Optional.ofNullable(dbPassword)); tryExecutingQueriesFromSqlResource("/cleartables.sql", connection); - logger.info("Done learing HANA Cloud SAILING DB on behalf of user "+SecurityUtils.getSubject().getPrincipal()); + logger.info("Done clearing HANA Cloud SAILING DB on behalf of user "+SecurityUtils.getSubject().getPrincipal()); return Response.ok().build(); } @POST @Produces("application/json;charset=UTF-8") @Path("export") - public Response export() throws SQLException { + public Response export(@QueryParam("leaderboardgroupid") List leaderboardGroupIds, + @QueryParam("eventid") List eventIds, + @FormParam("dbendpoint") String dbEndpoint, + @FormParam("dbuser") String dbUser, + @FormParam("dbpassword") String dbPassword) throws SQLException { SecurityUtils.getSubject().checkPermission(SecuredSecurityTypes.SERVER.getStringPermissionForTypeRelativeIdentifier( SecuredSecurityTypes.ServerActions.CAN_EXPORT_MASTERDATA, new TypeRelativeObjectIdentifier(ServerInfo.getName()))); logger.info("Exporting HANA Cloud SAILING DB content on behalf of user "+SecurityUtils.getSubject().getPrincipal()); final RacingEventService racingEventService = getService(); try { - final Connection connection = HanaConnectionFactory.INSTANCE.getConnection(); + final Connection connection = HanaConnectionFactory.INSTANCE.getConnection(Optional.ofNullable(dbEndpoint), + Optional.ofNullable(dbUser), Optional.ofNullable(dbPassword)); exportBoatClasses(racingEventService, connection); exportIrms(racingEventService, connection); exportScoringSchemes(racingEventService, connection); - exportCompetitors(racingEventService, connection); - exportEvents(racingEventService, connection); - exportRaces(racingEventService, connection); + final Iterable regattasToExport = getRegattasToExport(racingEventService, leaderboardGroupIds, eventIds); + exportRaces(racingEventService, regattasToExport, connection); } catch (Exception e) { logger.log(Level.SEVERE, "Problem exporting data to HANA", e); throw e; @@ -102,356 +131,148 @@ public class HanaCloudSacExportResource extends SharedAbstractSailingServerResou return Response.ok().build(); } - private void exportCompetitors(RacingEventService racingEventService, Connection connection) throws SQLException { - final PreparedStatement insertCompetitors = connection.prepareStatement( - "INSERT INTO SAILING.\"Competitor\" (\"id\", \"name\", \"shortName\", \"nationality\", \"sailNumber\") VALUES (?, ?, ?, ?, ?);"); - for (final Competitor competitor : racingEventService.getCompetitorAndBoatStore().getAllCompetitors()) { - insertCompetitors.setString(1, competitor.getId().toString()); - insertCompetitors.setString(2, competitor.getName()); - insertCompetitors.setString(3, competitor.getShortName()); - insertCompetitors.setString(4, competitor.getNationality() == null ? " " : competitor.getNationality().getThreeLetterIOCAcronym()); - insertCompetitors.setString(5, competitor.hasBoat() ? ((CompetitorWithBoat) competitor).getBoat().getSailID() : null); - insertCompetitors.execute(); + private Iterable getRegattasToExport(RacingEventService racingEventService, List leaderboardGroupIds, List eventIds) { + final Set leaderboardGroups = new HashSet<>(); + final Iterable result; + boolean atLeastOneIdProvided = false; + if (leaderboardGroupIds != null) { + for (final String leaderboardGroupIdAsString : leaderboardGroupIds) { + atLeastOneIdProvided = true; + try { + leaderboardGroups.add(racingEventService.getLeaderboardGroupByID(UUID.fromString(leaderboardGroupIdAsString))); + } catch (IllegalArgumentException e) { + logger.log(Level.WARNING, "Couldn't parse "+leaderboardGroupIdAsString+" as a leaderboard group's UUID. Ignoring."); + } + } } + if (eventIds != null) { + for (final String eventIdAsString : eventIds) { + atLeastOneIdProvided = true; + try { + final Event event = racingEventService.getEvent(UUID.fromString(eventIdAsString)); + Util.addAll(event.getLeaderboardGroups(), leaderboardGroups); + } catch (IllegalArgumentException e) { + logger.log(Level.WARNING, "Couldn't parse "+eventIdAsString+" as an event's UUID. Ignoring."); + } + } + } + if (atLeastOneIdProvided) { + final Set resultSet = new HashSet<>(); + for (final LeaderboardGroup leaderboardGroup : leaderboardGroups) { + for (final Leaderboard leaderboard : leaderboardGroup.getLeaderboards()) { + if (leaderboard.getLeaderboardType().isRegattaLeaderboard()) { + resultSet.add(((RegattaLeaderboard) leaderboard).getRegatta()); + } + } + } + result = resultSet; + } else { + result = racingEventService.getAllRegattas(); + } + return result; } private void exportIrms(RacingEventService racingEventService, Connection connection) throws SQLException { - final PreparedStatement insertBoatClasses = connection.prepareStatement( - "INSERT INTO SAILING.\"IRM\" (\"name\", \"discardable\", \"advanceCompetitorsTrackedWorse\", \"appliesAtStartOfRace\") VALUES (?, ?, ?, ?);"); - for (final MaxPointsReason irm : MaxPointsReason.values()) { - insertBoatClasses.setString(1, irm.name()); - insertBoatClasses.setBoolean(2, irm.isDiscardable()); - insertBoatClasses.setBoolean(3, irm.isAdvanceCompetitorsTrackedWorse()); - insertBoatClasses.setBoolean(4, irm.isAppliesAtStartOfRace()); - insertBoatClasses.execute(); - } + final InsertIrmStatement insertIrms = new InsertIrmStatement(connection); + insertIrms.insertBatch(Arrays.asList(MaxPointsReason.values())); + insertIrms.executeBatch(); } private void exportScoringSchemes(RacingEventService racingEventService, Connection connection) throws SQLException { - final PreparedStatement insertBoatClasses = connection.prepareStatement( - "INSERT INTO SAILING.\"ScoringScheme\" (\"id\", \"higherIsBetter\") VALUES (?, ?);"); + final InsertScoringSchemeStatement insertScoringScheme = new InsertScoringSchemeStatement(connection); for (final ScoringSchemeType scoringSchemeType : ScoringSchemeType.values()) { final ScoringScheme scoringScheme = racingEventService.getBaseDomainFactory().createScoringScheme(scoringSchemeType); - insertBoatClasses.setString(1, scoringScheme.getType().name()); - insertBoatClasses.setBoolean(2, scoringScheme.isHigherBetter()); - insertBoatClasses.execute(); + insertScoringScheme.insertBatch(scoringScheme); } + insertScoringScheme.executeBatch(); } private void exportBoatClasses(final RacingEventService racingEventService, final Connection connection) throws SQLException { - final PreparedStatement insertBoatClasses = connection.prepareStatement( - "INSERT INTO SAILING.\"BoatClass\" (\"id\", \"description\", \"hullLengthInMeters\", \"hullBeamInMeters\", \"hullType\") VALUES (?, ?, ?, ?, ?);"); - for (final BoatClass boatClass : racingEventService.getBaseDomainFactory().getBoatClasses()) { - if (boatClass.getName() == null) { - insertBoatClasses.setString(1, ""); - } else { - insertBoatClasses.setString(1, boatClass.getName().substring(0, Math.min(boatClass.getName().length(), 255))); - } - insertBoatClasses.setString(2, "Type "+boatClass.getHullType().name()+", length "+ - boatClass.getHullLength().getMeters()+"m, beam "+boatClass.getHullBeam().getMeters()+"m"); - setDouble(insertBoatClasses, 3, boatClass.getHullLength().getMeters()); - setDouble(insertBoatClasses, 4, boatClass.getHullBeam().getMeters()); - insertBoatClasses.setString(5, boatClass.getHullType().name()); - insertBoatClasses.execute(); - } + final InsertBoatClassStatement insertBoatClasses = new InsertBoatClassStatement(connection); + insertBoatClasses.insertBatch(racingEventService.getBaseDomainFactory().getBoatClasses()); + insertBoatClasses.executeBatch(); } - private void exportEvents(RacingEventService racingEventService, Connection connection) throws SQLException { - final PreparedStatement insertEvents = connection.prepareStatement( - "INSERT INTO SAILING.\"Event\" (\"id\", \"name\", \"startDate\", \"endDate\", \"venue\", \"isListed\", \"description\", \"location\") "+ - "VALUES (?, ?, ?, ?, ?, ?, ?, NEW ST_POINT(?));"); - for (final Event event : racingEventService.getAllEvents()) { - insertEvents.setString(1, event.getId().toString()); - insertEvents.setString(2, event.getName()); - insertEvents.setDate(3, event.getStartDate()==null?null:new Date(event.getStartDate().asMillis())); - insertEvents.setDate(4, event.getEndDate()==null?null:new Date(event.getEndDate().asMillis())); - insertEvents.setString(5, event.getVenue().getName()); - insertEvents.setBoolean(6, event.isPublic()); - insertEvents.setString(7, event.getDescription()); - final Position location = event.getLocation(); - insertEvents.setString(8, location != null ? String.format("POINT(%1.5f %1.5f)", location.getLatDeg(), location.getLngDeg()) : null); - insertEvents.execute(); - } - } - - private void exportRaces(RacingEventService racingEventService, Connection connection) throws SQLException { + private void exportRaces(RacingEventService racingEventService, Iterable regattasToExport, Connection connection) throws SQLException { final TimePoint now = TimePoint.now(); - final PreparedStatement insertRegattas = connection.prepareStatement( - "INSERT INTO SAILING.\"Regatta\" (\"name\", \"boatClass\", \"scoringScheme\", \"rankingMetric\", \"eventId\") "+ - "VALUES (?, ?, ?, ?, ?);"); - final PreparedStatement insertRaces = connection.prepareStatement( - "INSERT INTO SAILING.\"Race\" (\"name\", \"regatta\", \"raceColumn\", \"fleet\", \"startOfTracking\", "+ - "\"startOfRace\", \"endOfTracking\", \"endOfRace\", \"avgWindSpeedInKnots\") "+ - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);"); - final PreparedStatement insertRaceResults = connection.prepareStatement( - "INSERT INTO SAILING.\"RaceResult\" (\"regatta\", \"raceColumn\", \"competitorId\", \"points\", "+ - "\"discarded\", \"irm\") "+ - "VALUES (?, ?, ?, ?, ?, ?);"); - final PreparedStatement insertRaceStats = connection.prepareStatement( - "INSERT INTO SAILING.\"RaceStats\" (\"race\", \"regatta\", \"competitorId\", \"rankOneBased\", \"distanceSailedInMeters\", \"elapsedTimeInSeconds\", "+ - "\"avgCrossTrackErrorInMeters\", \"absoluteAvgCrossTrackErrorInMeters\", \"numberOfTacks\", "+ - "\"numberOfGybes\", \"numberOfPenaltyCircles\", \"startDelayInSeconds\", \"distanceFromStartLineInMetersAtStart\", "+ - "\"speedWhenCrossingStartLineInKnots\", \"startTack\") "+ - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); - final PreparedStatement insertLegs = connection.prepareStatement( - "INSERT INTO SAILING.\"Leg\" (\"race\", \"regatta\", \"number\", \"type\") "+ - "VALUES (?, ?, ?, ?);"); - final PreparedStatement insertLegStats = connection.prepareStatement( - "INSERT INTO SAILING.\"LegStats\" (\"race\", \"regatta\", \"number\", \"competitorId\", \"rankOneBased\", \"distanceSailedInMeters\", \"elapsedTimeInSeconds\", "+ - "\"avgCrossTrackErrorInMeters\", \"absoluteAvgCrossTrackErrorInMeters\", \"numberOfTacks\", "+ - "\"numberOfGybes\", \"numberOfPenaltyCircles\", \"avgVelocityMadeGoodInKnots\", \"gapToLeaderInSeconds\") "+ - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); - final PreparedStatement insertManeuvers = connection.prepareStatement( - "INSERT INTO SAILING.\"Maneuver\" (\"race\", \"regatta\", \"competitorId\", \"timepoint\", \"type\", \"newTack\", "+ - "\"lossInMeters\", \"speedBeforeInKnots\", \"speedAfterInKnots\", "+ - "\"courseBeforeInTrueDegrees\", \"courseAfterInTrueDegrees\", \"directionChangeInDegrees\", \"maximumTurningRateInDegreesPerSecond\", "+ - "\"lowestSpeedInKnots\", \"toSide\") "+ - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); - for (final Regatta regatta : racingEventService.getAllRegattas()) { + final InsertEventStatement insertEvents = new InsertEventStatement(connection); + final InsertCompetitorStatement insertCompetitors = new InsertCompetitorStatement(connection); + final AlreadyInsertedContent alreadyInsertedContent = new AlreadyInsertedContent(insertEvents, insertCompetitors); + final InsertRegattaStatement insertRegattas = new InsertRegattaStatement(connection); + final InsertRaceStatement insertRaces = new InsertRaceStatement(connection); + final InsertRaceResultStatement insertRaceResults = new InsertRaceResultStatement(connection); + final InsertRaceStatsStatement insertRaceStats = new InsertRaceStatsStatement(connection); + final InsertLegStatement insertLegs = new InsertLegStatement(connection); + final InsertLegStatsStatement insertLegStats = new InsertLegStatsStatement(connection); + final InsertManeuverStatement insertManeuvers = new InsertManeuverStatement(connection); + for (final Regatta regatta : regattasToExport) { final Leaderboard leaderboard = racingEventService.getLeaderboardByName(regatta.getName()); if (leaderboard != null) { final Event event = racingEventService.findEventContainingLeaderboardAndMatchingAtLeastOneCourseArea(leaderboard); - insertRegattas.setString(1, regatta.getName()); - insertRegattas.setString(2, regatta.getBoatClass().getName()); - insertRegattas.setString(3, regatta.getScoringScheme().getType().name()); - insertRegattas.setString(4, regatta.getRankingMetricType().name()); - insertRegattas.setString(5, event == null ? null : event.getId().toString()); - insertRegattas.execute(); - if (leaderboard != null) { - for (final RaceColumn raceColumn : leaderboard.getRaceColumns()) { - for (final Competitor competitor : raceColumn.getAllCompetitors()) { - parameterizeInsertRaceResult(insertRaceResults, now, competitor, leaderboard, raceColumn, regatta); - insertRaceResults.addBatch(); - } - insertRaceResults.executeBatch(); - for (final Fleet fleet : raceColumn.getFleets()) { - final TrackedRace trackedRace = raceColumn.getTrackedRace(fleet); - if (trackedRace != null) { - final RankingInfo rankingInfo = trackedRace.getRankingMetric().getRankingInfo(now); - final WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache = new LeaderboardDTOCalculationReuseCache(now); - parameterizeInsertRacesStatement(insertRaces, regatta, raceColumn, fleet, trackedRace); - insertRaces.addBatch(); - for (final TrackedLeg trackedLeg : trackedRace.getTrackedLegs()) { - parameterizeInsertLegsStatement(insertLegs, now, regatta, trackedRace, trackedLeg); - insertLegs.addBatch(); - for (final Competitor competitor : trackedRace.getRace().getCompetitors()) { - parameterizeInsertLegStatsStatement(insertLegStats, now, trackedLeg.getTrackedLeg(competitor), rankingInfo, cache); - insertLegStats.addBatch(); - } - } + if (event != null) { + alreadyInsertedContent.ensureEventIsInserted(event); + insertRegattas.insert(new RegattaAndEvent(regatta, event)); + if (leaderboard != null) { + for (final RaceColumn raceColumn : leaderboard.getRaceColumns()) { + for (final Competitor competitor : raceColumn.getAllCompetitors()) { + alreadyInsertedContent.ensureCompetitorIsInserted(competitor); + insertRaceResults.insertBatch(new InsertRaceResultStatement.RaceResult(regatta, leaderboard, competitor, raceColumn, now)); } - final Waypoint startWaypoint = trackedRace == null ? null : trackedRace.getRace().getCourse().getFirstWaypoint(); - if (trackedRace != null) { - for (final Competitor competitor : trackedRace.getRace().getCompetitors()) { - parameterizeInsertRaceStats(insertRaceStats, now, startWaypoint, competitor, trackedRace); - insertRaceStats.addBatch(); - final LinkedHashMap timepointUniqueManeuvers = new LinkedHashMap<>(); - for (final Maneuver maneuver : trackedRace.getManeuvers(competitor, /* waitForLatest */ false)) { - if (maneuver.getType() == ManeuverType.TACK || maneuver.getType() == ManeuverType.JIBE || maneuver.getType() == ManeuverType.PENALTY_CIRCLE) { - timepointUniqueManeuvers.put(maneuver.getTimePoint(), maneuver); + insertRaceResults.executeBatch(); + for (final Fleet fleet : raceColumn.getFleets()) { + final TrackedRace trackedRace = raceColumn.getTrackedRace(fleet); + if (trackedRace != null) { + final RankingInfo rankingInfo = trackedRace.getRankingMetric().getRankingInfo(now); + final WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache = new LeaderboardDTOCalculationReuseCache(now); + insertRaces.insertBatch(new InsertRaceStatement.TrackedRaceWithRaceColumnAndFleet(trackedRace, raceColumn, fleet)); + for (final TrackedLeg trackedLeg : trackedRace.getTrackedLegs()) { + insertLegs.insertBatch(new InsertLegStatement.TrackedLegAndNow(now, trackedLeg)); + for (final Competitor competitor : trackedRace.getRace().getCompetitors()) { + insertLegStats.insertBatch(new InsertLegStatsStatement.TrackedLegOfCompetitorRankingInfoCacheAndNow(now, rankingInfo, cache, trackedLeg.getTrackedLeg(competitor))); } } - for (final Maneuver maneuver : timepointUniqueManeuvers.values()) { - parameterizeInsertManeuvers(insertManeuvers, competitor, maneuver, trackedRace); - insertManeuvers.addBatch(); + } + final Waypoint startWaypoint = trackedRace == null ? null : trackedRace.getRace().getCourse().getFirstWaypoint(); + if (trackedRace != null) { + for (final Competitor competitor : trackedRace.getRace().getCompetitors()) { + insertRaceStats.insertBatch(new InsertRaceStatsStatement.TrackedRaceWithCompetitorAndStartWaypoint(now, startWaypoint, competitor, trackedRace)); + final LinkedHashMap timepointUniqueManeuvers = new LinkedHashMap<>(); + for (final Maneuver maneuver : trackedRace.getManeuvers(competitor, /* waitForLatest */ false)) { + if (maneuver.getType() == ManeuverType.TACK || maneuver.getType() == ManeuverType.JIBE || maneuver.getType() == ManeuverType.PENALTY_CIRCLE) { + timepointUniqueManeuvers.put(maneuver.getTimePoint(), maneuver); + } + } + for (final Maneuver maneuver : timepointUniqueManeuvers.values()) { + insertManeuvers.insertBatch(new InsertManeuverStatement.ManeuverTrackedRaceAndCompetitor(maneuver, trackedRace, competitor)); + } } } } + insertRaces.executeBatch(); + insertLegs.executeBatch(); + insertLegStats.executeBatch(); + insertRaceStats.executeBatch(); + insertManeuvers.executeBatch(); } - insertRaces.executeBatch(); - insertLegs.executeBatch(); - insertLegStats.executeBatch(); - insertRaceStats.executeBatch(); - insertManeuvers.executeBatch(); } } } } } - private void parameterizeInsertManeuvers(PreparedStatement insertManeuvers, Competitor competitor, Maneuver maneuver, TrackedRace trackedRace) throws SQLException { - insertManeuvers.setString(1, trackedRace.getRace().getName()); - insertManeuvers.setString(2, trackedRace.getTrackedRegatta().getRegatta().getName()); - insertManeuvers.setString(3, competitor.getId().toString()); - insertManeuvers.setDate(4, new Date(maneuver.getTimePoint().asMillis())); - insertManeuvers.setString(5, maneuver.getType().name()); - insertManeuvers.setString(6, maneuver.getNewTack().name()); - if (maneuver.getManeuverLoss() != null) { - setDouble(insertManeuvers, 7, - maneuver.getManeuverLoss().getDistanceSailedIfNotManeuveringProjectedOnMiddleManeuverAngle().getMeters() - -maneuver.getManeuverLoss().getDistanceSailedProjectedOnMiddleManeuverAngle().getMeters()); - } else { - setDouble(insertManeuvers, 7, 0); - } - setDouble(insertManeuvers, 8, maneuver.getSpeedWithBearingBefore().getKnots()); - setDouble(insertManeuvers, 9, maneuver.getSpeedWithBearingAfter().getKnots()); - setDouble(insertManeuvers, 10, maneuver.getSpeedWithBearingBefore().getBearing().getDegrees()); - setDouble(insertManeuvers, 11, maneuver.getSpeedWithBearingAfter().getBearing().getDegrees()); - setDouble(insertManeuvers, 12, maneuver.getDirectionChangeInDegrees()); - setDouble(insertManeuvers, 13, maneuver.getMaxTurningRateInDegreesPerSecond()); - setDouble(insertManeuvers, 14, maneuver.getLowestSpeed().getKnots()); - insertManeuvers.setString(15, maneuver.getToSide().name()); - } - - private void parameterizeInsertLegStatsStatement(PreparedStatement insertLegStats, TimePoint now, - TrackedLegOfCompetitor trackedLegOfCompetitor, RankingInfo rankingInfo, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) throws SQLException { - insertLegStats.setString(1, trackedLegOfCompetitor.getTrackedLeg().getTrackedRace().getRace().getName()); - insertLegStats.setString(2, trackedLegOfCompetitor.getTrackedLeg().getTrackedRace().getTrackedRegatta().getRegatta().getName()); - insertLegStats.setInt(3, trackedLegOfCompetitor.getTrackedLeg().getLeg().getZeroBasedIndexOfStartWaypoint()); - insertLegStats.setString(4, trackedLegOfCompetitor.getCompetitor().getId().toString()); - insertLegStats.setInt(5, trackedLegOfCompetitor.getRank(now)); - setDouble(insertLegStats, 6, metersOr0ForNull(trackedLegOfCompetitor.getDistanceTraveled(now))); - setDouble(insertLegStats, 7, secondsOr0ForNull(trackedLegOfCompetitor.getTime(now))); - setDouble(insertLegStats, 8, metersOr0ForNull(trackedLegOfCompetitor.getAverageSignedCrossTrackError(now, /* waitForLatest */ false))); - setDouble(insertLegStats, 9, metersOr0ForNull(trackedLegOfCompetitor.getAverageAbsoluteCrossTrackError(now, /* waitForLatest */ false))); - try { - insertLegStats.setInt(10, intOr0ForNull(trackedLegOfCompetitor.getNumberOfTacks(now, /* waitForLatest */ false))); - insertLegStats.setInt(11, intOr0ForNull(trackedLegOfCompetitor.getNumberOfJibes(now, /* waitForLatest */ false))); - insertLegStats.setInt(12, intOr0ForNull(trackedLegOfCompetitor.getNumberOfPenaltyCircles(now, /* waitForLatest */ false))); - } catch (NoWindException nwe) { - insertLegStats.setInt(10, 0); - insertLegStats.setInt(11, 0); - insertLegStats.setInt(12, 0); - } - final Speed vmg = trackedLegOfCompetitor.getAverageVelocityMadeGood(now); - setDouble(insertLegStats, 13, vmg==null?0:vmg.getKnots()); - setDouble(insertLegStats, 14, secondsOr0ForNull(trackedLegOfCompetitor.getGapToLeader(now, WindPositionMode.LEG_MIDDLE, rankingInfo, cache))); - } - - private int intOr0ForNull(Integer i) { - return i==null?0:i; - } - - private void parameterizeInsertLegsStatement(PreparedStatement insertLegs, TimePoint now, Regatta regatta, TrackedRace trackedRace, TrackedLeg trackedLeg) throws SQLException { - insertLegs.setString(1, trackedRace.getRace().getName()); - insertLegs.setString(2, trackedRace.getTrackedRegatta().getRegatta().getName()); - insertLegs.setInt(3, trackedLeg.getLeg().getZeroBasedIndexOfStartWaypoint()); - final LegType legType; - try { - legType = trackedLeg.getLegType(now); - insertLegs.setString(4, legType.name()); - } catch (NoWindException nwe) { - insertLegs.setString(4, null); - } - } - - private void parameterizeInsertRaceStats(PreparedStatement insertRaceStats, TimePoint now, Waypoint startWaypoint, - Competitor competitor, TrackedRace trackedRace) throws SQLException { - insertRaceStats.setString(1, trackedRace.getRace().getName()); - insertRaceStats.setString(2, trackedRace.getTrackedRegatta().getRegatta().getName()); - insertRaceStats.setString(3, competitor.getId().toString()); - insertRaceStats.setInt(4, trackedRace.getRank(competitor, now)); - setDouble(insertRaceStats, 5, metersOr0ForNull(trackedRace.getDistanceTraveled(competitor, now))); - setDouble(insertRaceStats, 6, secondsOr0ForNull(trackedRace.getTimeSailedSinceRaceStart(competitor, now))); - setDouble(insertRaceStats, 7, metersOr0ForNull(trackedRace.getAverageSignedCrossTrackError(competitor, now, /* waitForLatest */ false))); - setDouble(insertRaceStats, 8, metersOr0ForNull(trackedRace.getAverageAbsoluteCrossTrackError(competitor, now, /* waitForLatest */ false))); - final Iterable maneuvers = trackedRace.getManeuvers(competitor, /* waitForLatest */ false); - insertRaceStats.setInt(9, Util.size(Util.filter(maneuvers, m->m.getType() == ManeuverType.TACK))); - insertRaceStats.setInt(10, Util.size(Util.filter(maneuvers, m->m.getType() == ManeuverType.JIBE))); - insertRaceStats.setInt(11, Util.size(Util.filter(maneuvers, m->m.getType() == ManeuverType.PENALTY_CIRCLE))); - final TimePoint startOfRace = trackedRace.getStartOfRace(); - final double startDelay; - Tack startTack; - if (startWaypoint != null && startOfRace != null) { - NavigableSet competitorMarkPassings = trackedRace.getMarkPassings(competitor); - trackedRace.lockForRead(competitorMarkPassings); - try { - if (!Util.isEmpty(competitorMarkPassings)) { - final MarkPassing competitorStartMarkPassing = competitorMarkPassings.iterator().next(); - final TimePoint competitorStartTime = competitorStartMarkPassing.getTimePoint(); - startDelay = secondsOr0ForNull(startOfRace.until(competitorStartTime)); - try { - startTack = trackedRace.getTack(competitor, competitorStartTime); - } catch (NoWindException e) { - startTack = null; - } - } else { - startDelay = 0; - startTack = null; - } - } finally { - trackedRace.unlockAfterRead(competitorMarkPassings); - } - } else { - startDelay = 0; - startTack = null; - } - setDouble(insertRaceStats, 12, startDelay); - if (startOfRace != null) { - setDouble(insertRaceStats, 13, metersOr0ForNull(trackedRace.getDistanceToStartLine(competitor, startOfRace))); - final Speed speedWhenCrossingStartLine = trackedRace.getSpeedWhenCrossingStartLine(competitor); - setDouble(insertRaceStats, 14, speedWhenCrossingStartLine==null?0:speedWhenCrossingStartLine.getKnots()); - insertRaceStats.setString(15, startTack==null?null:startTack.name()); - } else { - setDouble(insertRaceStats, 13, 0); - setDouble(insertRaceStats, 14, 0); - } - } - - private double metersOr0ForNull(final Distance distance) { - return distance == null ? 0 : distance.getMeters(); - } - - private double secondsOr0ForNull(final Duration duration) { - return duration == null ? 0 : duration.asSeconds(); - } - - private void parameterizeInsertRaceResult(PreparedStatement insertRaceResults, TimePoint now, - Competitor competitor, Leaderboard leaderboard, RaceColumn raceColumn, Regatta regatta) throws SQLException { - insertRaceResults.setString(1, regatta.getName()); - insertRaceResults.setString(2, raceColumn.getName()); - insertRaceResults.setString(3, competitor.getId().toString()); - final Double totalPoints = leaderboard.getTotalPoints(competitor, raceColumn, now); - setDouble(insertRaceResults, 4, totalPoints == null ? 0 : totalPoints); - insertRaceResults.setBoolean(5, leaderboard.isDiscarded(competitor, raceColumn, now)); - final MaxPointsReason maxPointsReason = leaderboard.getMaxPointsReason(competitor, raceColumn, now); - insertRaceResults.setString(6, (maxPointsReason == null ? MaxPointsReason.NONE : maxPointsReason).name()); - } - - private void parameterizeInsertRacesStatement(final PreparedStatement insertRaces, final Regatta regatta, - final RaceColumn raceColumn, final Fleet fleet, final TrackedRace trackedRace) throws SQLException { - insertRaces.setString(1, trackedRace.getRace().getName()); - assert trackedRace.getTrackedRegatta().getRegatta() == regatta; - insertRaces.setString(2, trackedRace.getTrackedRegatta().getRegatta().getName()); - insertRaces.setString(3, raceColumn.getName()); - insertRaces.setString(4, fleet.getName()); - if (trackedRace.getStartOfTracking() != null) { - insertRaces.setDate(5, new Date(trackedRace.getStartOfTracking().asMillis())); - } else { - insertRaces.setDate(5, null); - } - if (trackedRace.getStartOfRace() != null) { - insertRaces.setDate(6, new Date(trackedRace.getStartOfRace().asMillis())); - } else { - insertRaces.setDate(6, null); - } - if (trackedRace.getEndOfTracking() != null) { - insertRaces.setDate(7, new Date(trackedRace.getEndOfTracking().asMillis())); - } else { - insertRaces.setDate(7, null); - } - if (trackedRace.getEndOfRace() != null) { - insertRaces.setDate(8, new Date(trackedRace.getEndOfRace().asMillis())); - } else { - insertRaces.setDate(8, null); - } - final SpeedWithConfidence averageWind = trackedRace.getAverageWindSpeedWithConfidenceWithNumberOfSamples(/* number of samples */ 5); - if (averageWind != null) { - setDouble(insertRaces, 9, averageWind.getObject().getKnots()); - } else { - setDouble(insertRaces, 9, 0.0); - } - } - @POST @Produces("application/json;charset=UTF-8") @Path("createtables") - public Response createTables(@QueryParam("drop") boolean drop) throws SQLException, IOException { + public Response createTables(@QueryParam("drop") boolean drop, + @FormParam("dbendpoint") String dbEndpoint, + @FormParam("dbuser") String dbUser, + @FormParam("dbpassword") String dbPassword) throws SQLException, IOException { SecurityUtils.getSubject().checkPermission(SecuredSecurityTypes.SERVER.getStringPermissionForTypeRelativeIdentifier( SecuredSecurityTypes.ServerActions.CAN_EXPORT_MASTERDATA, new TypeRelativeObjectIdentifier(ServerInfo.getName()))); - final Connection connection = HanaConnectionFactory.INSTANCE.getConnection(); + final Connection connection = HanaConnectionFactory.INSTANCE.getConnection(Optional.ofNullable(dbEndpoint), + Optional.ofNullable(dbUser), Optional.ofNullable(dbPassword)); if (drop) { logger.info("Dropping HANA Cloud SAILING DB tables on behalf of user "+SecurityUtils.getSubject().getPrincipal()); tryExecutingQueriesFromSqlResource("/droptables.sql", connection); @@ -489,12 +310,4 @@ public class HanaCloudSacExportResource extends SharedAbstractSailingServerResou connection.createStatement().execute(statementAsString); } } - - private void setDouble(PreparedStatement statement, int parameterIndex, double value) throws SQLException { - if (Double.isNaN(value) || Double.isInfinite(value)) { - setDouble(statement, parameterIndex, 0.0); - } else { - statement.setDouble(parameterIndex, value); - } - } } diff --git a/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertBoatClassStatement.java b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertBoatClassStatement.java new file mode 100755 index 00000000000..879924ea427 --- /dev/null +++ b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertBoatClassStatement.java @@ -0,0 +1,27 @@ +package com.sap.sailing.hanaexport.jaxrs.api; + +import java.sql.Connection; +import java.sql.SQLException; + +import com.sap.sailing.domain.base.BoatClass; + +public class InsertBoatClassStatement extends AbstractPreparedInsertStatement { + protected InsertBoatClassStatement(Connection connection) throws SQLException { + super(connection.prepareStatement( + "INSERT INTO SAILING.\"BoatClass\" (\"id\", \"description\", \"hullLengthInMeters\", \"hullBeamInMeters\", \"hullType\") VALUES (?, ?, ?, ?, ?);")); + } + + @Override + public void parameterizeStatement(BoatClass boatClass) throws SQLException { + if (boatClass.getName() == null) { + getPreparedStatement().setString(1, ""); + } else { + getPreparedStatement().setString(1, boatClass.getName().substring(0, Math.min(boatClass.getName().length(), 255))); + } + getPreparedStatement().setString(2, "Type "+boatClass.getHullType().name()+", length "+ + boatClass.getHullLength().getMeters()+"m, beam "+boatClass.getHullBeam().getMeters()+"m"); + setDouble(3, boatClass.getHullLength().getMeters()); + setDouble(4, boatClass.getHullBeam().getMeters()); + getPreparedStatement().setString(5, boatClass.getHullType().name()); + } +} diff --git a/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertCompetitorStatement.java b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertCompetitorStatement.java new file mode 100755 index 00000000000..a5a019c12c6 --- /dev/null +++ b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertCompetitorStatement.java @@ -0,0 +1,23 @@ +package com.sap.sailing.hanaexport.jaxrs.api; + +import java.sql.Connection; +import java.sql.SQLException; + +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.CompetitorWithBoat; + +public class InsertCompetitorStatement extends AbstractPreparedInsertStatement { + protected InsertCompetitorStatement(Connection connection) throws SQLException { + super(connection.prepareStatement( + "INSERT INTO SAILING.\"Competitor\" (\"id\", \"name\", \"shortName\", \"nationality\", \"sailNumber\") VALUES (?, ?, ?, ?, ?);")); + } + + @Override + public void parameterizeStatement(Competitor competitor) throws SQLException { + getPreparedStatement().setString(1, competitor.getId().toString()); + getPreparedStatement().setString(2, competitor.getName()); + getPreparedStatement().setString(3, competitor.getShortName()); + getPreparedStatement().setString(4, competitor.getNationality() == null ? " " : competitor.getNationality().getThreeLetterIOCAcronym()); + getPreparedStatement().setString(5, competitor.hasBoat() ? ((CompetitorWithBoat) competitor).getBoat().getSailID() : null); + } +} diff --git a/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertEventStatement.java b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertEventStatement.java new file mode 100755 index 00000000000..f840b3d3903 --- /dev/null +++ b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertEventStatement.java @@ -0,0 +1,29 @@ +package com.sap.sailing.hanaexport.jaxrs.api; + +import java.sql.Connection; +import java.sql.Date; +import java.sql.SQLException; + +import com.sap.sailing.domain.base.Event; +import com.sap.sailing.domain.common.Position; + +public class InsertEventStatement extends AbstractPreparedInsertStatement { + protected InsertEventStatement(Connection connection) throws SQLException { + super(connection.prepareStatement( + "INSERT INTO SAILING.\"Event\" (\"id\", \"name\", \"startDate\", \"endDate\", \"venue\", \"isListed\", \"description\", \"location\") "+ + "VALUES (?, ?, ?, ?, ?, ?, ?, NEW ST_POINT(?, 3857));")); + } + + @Override + public void parameterizeStatement(Event event) throws SQLException { + getPreparedStatement().setString(1, event.getId().toString()); + getPreparedStatement().setString(2, event.getName()); + getPreparedStatement().setDate(3, event.getStartDate()==null?null:new Date(event.getStartDate().asMillis())); + getPreparedStatement().setDate(4, event.getEndDate()==null?null:new Date(event.getEndDate().asMillis())); + getPreparedStatement().setString(5, event.getVenue().getName()); + getPreparedStatement().setBoolean(6, event.isPublic()); + getPreparedStatement().setString(7, event.getDescription()); + final Position location = event.getLocation(); + getPreparedStatement().setString(8, location != null ? String.format("POINT(%1.8f %1.8f)", location.getLngDeg(), location.getLatDeg()) : null); + } +} diff --git a/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertIrmStatement.java b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertIrmStatement.java new file mode 100755 index 00000000000..42967391212 --- /dev/null +++ b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertIrmStatement.java @@ -0,0 +1,21 @@ +package com.sap.sailing.hanaexport.jaxrs.api; + +import java.sql.Connection; +import java.sql.SQLException; + +import com.sap.sailing.domain.common.MaxPointsReason; + +public class InsertIrmStatement extends AbstractPreparedInsertStatement { + protected InsertIrmStatement(Connection connection) throws SQLException { + super(connection.prepareStatement( + "INSERT INTO SAILING.\"IRM\" (\"name\", \"discardable\", \"advanceCompetitorsTrackedWorse\", \"appliesAtStartOfRace\") VALUES (?, ?, ?, ?);")); + } + + @Override + public void parameterizeStatement(MaxPointsReason irm) throws SQLException { + getPreparedStatement().setString(1, irm.name()); + getPreparedStatement().setBoolean(2, irm.isDiscardable()); + getPreparedStatement().setBoolean(3, irm.isAdvanceCompetitorsTrackedWorse()); + getPreparedStatement().setBoolean(4, irm.isAppliesAtStartOfRace()); + } +} diff --git a/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertLegStatement.java b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertLegStatement.java new file mode 100755 index 00000000000..233b21fe4e5 --- /dev/null +++ b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertLegStatement.java @@ -0,0 +1,49 @@ +package com.sap.sailing.hanaexport.jaxrs.api; + +import java.sql.Connection; +import java.sql.SQLException; + +import com.sap.sailing.domain.common.LegType; +import com.sap.sailing.domain.common.NoWindException; +import com.sap.sailing.domain.tracking.TrackedLeg; +import com.sap.sailing.hanaexport.jaxrs.api.InsertLegStatement.TrackedLegAndNow; +import com.sap.sse.common.TimePoint; + +public class InsertLegStatement extends AbstractPreparedInsertStatement { + static class TrackedLegAndNow { + private final TimePoint now; + private final TrackedLeg trackedLeg; + public TrackedLegAndNow(TimePoint now, TrackedLeg trackedLeg) { + super(); + this.now = now; + this.trackedLeg = trackedLeg; + } + public TimePoint getNow() { + return now; + } + public TrackedLeg getTrackedLeg() { + return trackedLeg; + } + } + + protected InsertLegStatement(Connection connection) throws SQLException { + super(connection.prepareStatement( + "INSERT INTO SAILING.\"Leg\" (\"race\", \"regatta\", \"number\", \"type\") "+ + "VALUES (?, ?, ?, ?);")); + } + + @Override + public void parameterizeStatement(TrackedLegAndNow trackedLegAndNow) throws SQLException { + getPreparedStatement().setString(1, trackedLegAndNow.getTrackedLeg().getTrackedRace().getRace().getName()); + getPreparedStatement().setString(2, trackedLegAndNow.getTrackedLeg().getTrackedRace().getTrackedRegatta().getRegatta().getName()); + getPreparedStatement().setInt(3, trackedLegAndNow.getTrackedLeg().getLeg().getZeroBasedIndexOfStartWaypoint()); + final LegType legType; + try { + legType = trackedLegAndNow.getTrackedLeg().getLegType(trackedLegAndNow.getNow()); + getPreparedStatement().setString(4, legType.name()); + } catch (NoWindException nwe) { + getPreparedStatement().setString(4, null); + } + + } +} diff --git a/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertLegStatsStatement.java b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertLegStatsStatement.java new file mode 100755 index 00000000000..610c8925154 --- /dev/null +++ b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertLegStatsStatement.java @@ -0,0 +1,77 @@ +package com.sap.sailing.hanaexport.jaxrs.api; + +import java.sql.Connection; +import java.sql.SQLException; + +import com.sap.sailing.domain.common.NoWindException; +import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; +import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; +import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache; +import com.sap.sailing.domain.tracking.WindPositionMode; +import com.sap.sailing.hanaexport.jaxrs.api.InsertLegStatsStatement.TrackedLegOfCompetitorRankingInfoCacheAndNow; +import com.sap.sse.common.Speed; +import com.sap.sse.common.TimePoint; + +public class InsertLegStatsStatement extends AbstractPreparedInsertStatement { + static class TrackedLegOfCompetitorRankingInfoCacheAndNow { + private final TimePoint now; + private final RankingInfo rankingInfo; + private final WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache; + private final TrackedLegOfCompetitor trackedLegOfCompetitor; + public TrackedLegOfCompetitorRankingInfoCacheAndNow(TimePoint now, RankingInfo rankingInfo, + WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache, + TrackedLegOfCompetitor trackedLegOfCompetitor) { + super(); + this.now = now; + this.rankingInfo = rankingInfo; + this.cache = cache; + this.trackedLegOfCompetitor = trackedLegOfCompetitor; + } + public TimePoint getNow() { + return now; + } + public RankingInfo getRankingInfo() { + return rankingInfo; + } + public WindLegTypeAndLegBearingAndORCPerformanceCurveCache getCache() { + return cache; + } + public TrackedLegOfCompetitor getTrackedLegOfCompetitor() { + return trackedLegOfCompetitor; + } + } + + protected InsertLegStatsStatement(Connection connection) throws SQLException { + super(connection.prepareStatement( + "INSERT INTO SAILING.\"LegStats\" (\"race\", \"regatta\", \"number\", \"competitorId\", \"rankOneBased\", \"distanceSailedInMeters\", \"elapsedTimeInSeconds\", "+ + "\"avgCrossTrackErrorInMeters\", \"absoluteAvgCrossTrackErrorInMeters\", \"numberOfTacks\", "+ + "\"numberOfGybes\", \"numberOfPenaltyCircles\", \"avgVelocityMadeGoodInKnots\", \"gapToLeaderInSeconds\") "+ + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + } + + @Override + public void parameterizeStatement(TrackedLegOfCompetitorRankingInfoCacheAndNow trackedLegOfCompetitorAndNow) throws SQLException { + getPreparedStatement().setString(1, trackedLegOfCompetitorAndNow.getTrackedLegOfCompetitor().getTrackedLeg().getTrackedRace().getRace().getName()); + getPreparedStatement().setString(2, trackedLegOfCompetitorAndNow.getTrackedLegOfCompetitor().getTrackedLeg().getTrackedRace().getTrackedRegatta().getRegatta().getName()); + getPreparedStatement().setInt(3, trackedLegOfCompetitorAndNow.getTrackedLegOfCompetitor().getTrackedLeg().getLeg().getZeroBasedIndexOfStartWaypoint()); + getPreparedStatement().setString(4, trackedLegOfCompetitorAndNow.getTrackedLegOfCompetitor().getCompetitor().getId().toString()); + getPreparedStatement().setInt(5, trackedLegOfCompetitorAndNow.getTrackedLegOfCompetitor().getRank(trackedLegOfCompetitorAndNow.getNow())); + setDouble(6, metersOr0ForNull(trackedLegOfCompetitorAndNow.getTrackedLegOfCompetitor().getDistanceTraveled(trackedLegOfCompetitorAndNow.getNow()))); + setDouble(7, secondsOr0ForNull(trackedLegOfCompetitorAndNow.getTrackedLegOfCompetitor().getTime(trackedLegOfCompetitorAndNow.getNow()))); + setDouble(8, metersOr0ForNull(trackedLegOfCompetitorAndNow.getTrackedLegOfCompetitor().getAverageSignedCrossTrackError(trackedLegOfCompetitorAndNow.getNow(), /* waitForLatest */ false))); + setDouble(9, metersOr0ForNull(trackedLegOfCompetitorAndNow.getTrackedLegOfCompetitor().getAverageAbsoluteCrossTrackError(trackedLegOfCompetitorAndNow.getNow(), /* waitForLatest */ false))); + try { + getPreparedStatement().setInt(10, intOr0ForNull(trackedLegOfCompetitorAndNow.getTrackedLegOfCompetitor().getNumberOfTacks(trackedLegOfCompetitorAndNow.getNow(), /* waitForLatest */ false))); + getPreparedStatement().setInt(11, intOr0ForNull(trackedLegOfCompetitorAndNow.getTrackedLegOfCompetitor().getNumberOfJibes(trackedLegOfCompetitorAndNow.getNow(), /* waitForLatest */ false))); + getPreparedStatement().setInt(12, intOr0ForNull(trackedLegOfCompetitorAndNow.getTrackedLegOfCompetitor().getNumberOfPenaltyCircles(trackedLegOfCompetitorAndNow.getNow(), /* waitForLatest */ false))); + } catch (NoWindException nwe) { + getPreparedStatement().setInt(10, 0); + getPreparedStatement().setInt(11, 0); + getPreparedStatement().setInt(12, 0); + } + final Speed vmg = trackedLegOfCompetitorAndNow.getTrackedLegOfCompetitor().getAverageVelocityMadeGood(trackedLegOfCompetitorAndNow.getNow()); + setDouble(13, vmg==null?0:vmg.getKnots()); + setDouble(14, secondsOr0ForNull(trackedLegOfCompetitorAndNow.getTrackedLegOfCompetitor().getGapToLeader(trackedLegOfCompetitorAndNow.getNow(), WindPositionMode.LEG_MIDDLE, + trackedLegOfCompetitorAndNow.getRankingInfo(), trackedLegOfCompetitorAndNow.getCache()))); + } +} diff --git a/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertManeuverStatement.java b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertManeuverStatement.java new file mode 100755 index 00000000000..dd8957ddef6 --- /dev/null +++ b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertManeuverStatement.java @@ -0,0 +1,67 @@ +package com.sap.sailing.hanaexport.jaxrs.api; + +import java.sql.Connection; +import java.sql.Date; +import java.sql.SQLException; + +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.tracking.Maneuver; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.hanaexport.jaxrs.api.InsertManeuverStatement.ManeuverTrackedRaceAndCompetitor; + +public class InsertManeuverStatement extends AbstractPreparedInsertStatement { + static class ManeuverTrackedRaceAndCompetitor { + private final Maneuver maneuver; + private final TrackedRace trackedRace; + private final Competitor competitor; + public ManeuverTrackedRaceAndCompetitor(Maneuver maneuver, TrackedRace trackedRace, Competitor competitor) { + super(); + this.maneuver = maneuver; + this.trackedRace = trackedRace; + this.competitor = competitor; + } + public Maneuver getManeuver() { + return maneuver; + } + public TrackedRace getTrackedRace() { + return trackedRace; + } + public Competitor getCompetitor() { + return competitor; + } + } + + protected InsertManeuverStatement(Connection connection) throws SQLException { + super(connection.prepareStatement( + "INSERT INTO SAILING.\"Maneuver\" (\"race\", \"regatta\", \"competitorId\", \"timepoint\", \"type\", \"newTack\", "+ + "\"lossInMeters\", \"speedBeforeInKnots\", \"speedAfterInKnots\", "+ + "\"courseBeforeInTrueDegrees\", \"courseAfterInTrueDegrees\", \"directionChangeInDegrees\", \"maximumTurningRateInDegreesPerSecond\", "+ + "\"lowestSpeedInKnots\", \"toSide\") "+ + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + } + + @Override + public void parameterizeStatement(ManeuverTrackedRaceAndCompetitor maneuverTrackedRaceAndCompetitor) throws SQLException { + getPreparedStatement().setString(1, maneuverTrackedRaceAndCompetitor.getTrackedRace().getRace().getName()); + getPreparedStatement().setString(2, maneuverTrackedRaceAndCompetitor.getTrackedRace().getTrackedRegatta().getRegatta().getName()); + getPreparedStatement().setString(3, maneuverTrackedRaceAndCompetitor.getCompetitor().getId().toString()); + getPreparedStatement().setDate(4, new Date(maneuverTrackedRaceAndCompetitor.getManeuver().getTimePoint().asMillis())); + getPreparedStatement().setString(5, maneuverTrackedRaceAndCompetitor.getManeuver().getType().name()); + getPreparedStatement().setString(6, maneuverTrackedRaceAndCompetitor.getManeuver().getNewTack().name()); + if (maneuverTrackedRaceAndCompetitor.getManeuver().getManeuverLoss() != null) { + setDouble(7, + maneuverTrackedRaceAndCompetitor.getManeuver().getManeuverLoss().getDistanceSailedIfNotManeuveringProjectedOnMiddleManeuverAngle().getMeters() + -maneuverTrackedRaceAndCompetitor.getManeuver().getManeuverLoss().getDistanceSailedProjectedOnMiddleManeuverAngle().getMeters()); + } else { + setDouble(7, 0); + } + setDouble(8, maneuverTrackedRaceAndCompetitor.getManeuver().getSpeedWithBearingBefore().getKnots()); + setDouble(9, maneuverTrackedRaceAndCompetitor.getManeuver().getSpeedWithBearingAfter().getKnots()); + setDouble(10, maneuverTrackedRaceAndCompetitor.getManeuver().getSpeedWithBearingBefore().getBearing().getDegrees()); + setDouble(11, maneuverTrackedRaceAndCompetitor.getManeuver().getSpeedWithBearingAfter().getBearing().getDegrees()); + setDouble(12, maneuverTrackedRaceAndCompetitor.getManeuver().getDirectionChangeInDegrees()); + setDouble(13, maneuverTrackedRaceAndCompetitor.getManeuver().getMaxTurningRateInDegreesPerSecond()); + setDouble(14, maneuverTrackedRaceAndCompetitor.getManeuver().getLowestSpeed().getKnots()); + getPreparedStatement().setString(15, maneuverTrackedRaceAndCompetitor.getManeuver().getToSide().name()); + } +} diff --git a/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertRaceResultStatement.java b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertRaceResultStatement.java new file mode 100755 index 00000000000..af513c49076 --- /dev/null +++ b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertRaceResultStatement.java @@ -0,0 +1,65 @@ +package com.sap.sailing.hanaexport.jaxrs.api; + +import java.sql.Connection; +import java.sql.SQLException; + +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.RaceColumn; +import com.sap.sailing.domain.base.Regatta; +import com.sap.sailing.domain.common.MaxPointsReason; +import com.sap.sailing.domain.leaderboard.Leaderboard; +import com.sap.sailing.hanaexport.jaxrs.api.InsertRaceResultStatement.RaceResult; +import com.sap.sse.common.TimePoint; + +public class InsertRaceResultStatement extends AbstractPreparedInsertStatement { + static class RaceResult { + private final Regatta regatta; + private final Leaderboard leaderboard; + private final Competitor competitor; + private final RaceColumn raceColumn; + private final TimePoint now; + public RaceResult(Regatta regatta, Leaderboard leaderboard, Competitor competitor, RaceColumn raceColumn, + TimePoint now) { + super(); + this.regatta = regatta; + this.leaderboard = leaderboard; + this.competitor = competitor; + this.raceColumn = raceColumn; + this.now = now; + } + public Regatta getRegatta() { + return regatta; + } + public Leaderboard getLeaderboard() { + return leaderboard; + } + public Competitor getCompetitor() { + return competitor; + } + public RaceColumn getRaceColumn() { + return raceColumn; + } + public TimePoint getNow() { + return now; + } + } + + protected InsertRaceResultStatement(Connection connection) throws SQLException { + super(connection.prepareStatement( + "INSERT INTO SAILING.\"RaceResult\" (\"regatta\", \"raceColumn\", \"competitorId\", \"points\", "+ + "\"discarded\", \"irm\") "+ + "VALUES (?, ?, ?, ?, ?, ?);")); + } + + @Override + public void parameterizeStatement(RaceResult raceResult) throws SQLException { + getPreparedStatement().setString(1, raceResult.getRegatta().getName()); + getPreparedStatement().setString(2, raceResult.getRaceColumn().getName()); + getPreparedStatement().setString(3, raceResult.getCompetitor().getId().toString()); + final Double totalPoints = raceResult.getLeaderboard().getTotalPoints(raceResult.getCompetitor(), raceResult.getRaceColumn(), raceResult.getNow()); + setDouble(4, totalPoints == null ? 0 : totalPoints); + getPreparedStatement().setBoolean(5, raceResult.getLeaderboard().isDiscarded(raceResult.getCompetitor(), raceResult.getRaceColumn(), raceResult.getNow())); + final MaxPointsReason maxPointsReason = raceResult.getLeaderboard().getMaxPointsReason(raceResult.getCompetitor(), raceResult.getRaceColumn(), raceResult.getNow()); + getPreparedStatement().setString(6, (maxPointsReason == null ? MaxPointsReason.NONE : maxPointsReason).name()); + } +} diff --git a/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertRaceStatement.java b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertRaceStatement.java new file mode 100755 index 00000000000..8272940e3b1 --- /dev/null +++ b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertRaceStatement.java @@ -0,0 +1,77 @@ +package com.sap.sailing.hanaexport.jaxrs.api; + +import java.sql.Connection; +import java.sql.Date; +import java.sql.SQLException; + +import com.sap.sailing.domain.base.Fleet; +import com.sap.sailing.domain.base.RaceColumn; +import com.sap.sailing.domain.base.SpeedWithConfidence; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.hanaexport.jaxrs.api.InsertRaceStatement.TrackedRaceWithRaceColumnAndFleet; +import com.sap.sse.common.TimePoint; + +public class InsertRaceStatement extends AbstractPreparedInsertStatement { + static class TrackedRaceWithRaceColumnAndFleet { + private final TrackedRace trackedRace; + private final RaceColumn raceColumn; + private final Fleet fleet; + public TrackedRaceWithRaceColumnAndFleet(TrackedRace trackedRace, RaceColumn raceColumn, Fleet fleet) { + super(); + this.trackedRace = trackedRace; + this.raceColumn = raceColumn; + this.fleet = fleet; + } + public TrackedRace getTrackedRace() { + return trackedRace; + } + public RaceColumn getRaceColumn() { + return raceColumn; + } + public Fleet getFleet() { + return fleet; + } + } + + protected InsertRaceStatement(Connection connection) throws SQLException { + super(connection.prepareStatement( + "INSERT INTO SAILING.\"Race\" (\"name\", \"regatta\", \"raceColumn\", \"fleet\", \"startOfTracking\", "+ + "\"startOfRace\", \"endOfTracking\", \"endOfRace\", \"avgWindSpeedInKnots\") "+ + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);")); + } + + @Override + public void parameterizeStatement(TrackedRaceWithRaceColumnAndFleet raceColumnFleetAndTrackedRace) throws SQLException { + final TrackedRace trackedRace = raceColumnFleetAndTrackedRace.getTrackedRace(); + getPreparedStatement().setString(1, trackedRace.getRace().getName()); + getPreparedStatement().setString(2, trackedRace.getTrackedRegatta().getRegatta().getName()); + getPreparedStatement().setString(3, raceColumnFleetAndTrackedRace.getRaceColumn().getName()); + getPreparedStatement().setString(4, raceColumnFleetAndTrackedRace.getFleet().getName()); + if (trackedRace.getStartOfTracking() != null) { + getPreparedStatement().setDate(5, new Date(trackedRace.getStartOfTracking().asMillis())); + } else { + getPreparedStatement().setDate(5, null); + } + if (trackedRace.getStartOfRace() != null) { + getPreparedStatement().setDate(6, new Date(trackedRace.getStartOfRace().asMillis())); + } else { + getPreparedStatement().setDate(6, null); + } + if (trackedRace.getEndOfTracking() != null) { + getPreparedStatement().setDate(7, new Date(trackedRace.getEndOfTracking().asMillis())); + } else { + getPreparedStatement().setDate(7, null); + } + if (trackedRace.getEndOfRace() != null) { + getPreparedStatement().setDate(8, new Date(trackedRace.getEndOfRace().asMillis())); + } else { + getPreparedStatement().setDate(8, null); + } + final SpeedWithConfidence averageWind = trackedRace.getAverageWindSpeedWithConfidenceWithNumberOfSamples(/* number of samples */ 5); + if (averageWind != null) { + setDouble(9, averageWind.getObject().getKnots()); + } else { + setDouble(9, 0.0); + } + } +} diff --git a/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertRaceStatsStatement.java b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertRaceStatsStatement.java new file mode 100755 index 00000000000..9e2256437ff --- /dev/null +++ b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertRaceStatsStatement.java @@ -0,0 +1,112 @@ +package com.sap.sailing.hanaexport.jaxrs.api; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.NavigableSet; + +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.Waypoint; +import com.sap.sailing.domain.common.ManeuverType; +import com.sap.sailing.domain.common.NoWindException; +import com.sap.sailing.domain.common.Tack; +import com.sap.sailing.domain.tracking.Maneuver; +import com.sap.sailing.domain.tracking.MarkPassing; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.hanaexport.jaxrs.api.InsertRaceStatsStatement.TrackedRaceWithCompetitorAndStartWaypoint; +import com.sap.sse.common.Speed; +import com.sap.sse.common.TimePoint; +import com.sap.sse.common.Util; + +public class InsertRaceStatsStatement extends AbstractPreparedInsertStatement { + static class TrackedRaceWithCompetitorAndStartWaypoint { + private final TimePoint now; + private final Waypoint startWaypoint; + private final Competitor competitor; + private final TrackedRace trackedRace; + public TrackedRaceWithCompetitorAndStartWaypoint(TimePoint now, Waypoint startWaypoint, Competitor competitor, + TrackedRace trackedRace) { + super(); + this.now = now; + this.startWaypoint = startWaypoint; + this.competitor = competitor; + this.trackedRace = trackedRace; + } + public TimePoint getNow() { + return now; + } + public Waypoint getStartWaypoint() { + return startWaypoint; + } + public Competitor getCompetitor() { + return competitor; + } + public TrackedRace getTrackedRace() { + return trackedRace; + } + } + + protected InsertRaceStatsStatement(Connection connection) throws SQLException { + super(connection.prepareStatement( + "INSERT INTO SAILING.\"RaceStats\" (\"race\", \"regatta\", \"competitorId\", \"rankOneBased\", \"distanceSailedInMeters\", \"elapsedTimeInSeconds\", "+ + "\"avgCrossTrackErrorInMeters\", \"absoluteAvgCrossTrackErrorInMeters\", \"numberOfTacks\", "+ + "\"numberOfGybes\", \"numberOfPenaltyCircles\", \"startDelayInSeconds\", \"distanceFromStartLineInMetersAtStart\", "+ + "\"speedWhenCrossingStartLineInKnots\", \"startTack\") "+ + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + } + + @Override + public void parameterizeStatement(TrackedRaceWithCompetitorAndStartWaypoint raceColumnFleetAndTrackedRace) throws SQLException { + final TrackedRace trackedRace = raceColumnFleetAndTrackedRace.getTrackedRace(); + final Competitor competitor = raceColumnFleetAndTrackedRace.getCompetitor(); + final TimePoint now = raceColumnFleetAndTrackedRace.getNow(); + getPreparedStatement().setString(1, trackedRace.getRace().getName()); + getPreparedStatement().setString(2, trackedRace.getTrackedRegatta().getRegatta().getName()); + getPreparedStatement().setString(3, competitor.getId().toString()); + getPreparedStatement().setInt(4, trackedRace.getRank(competitor, now)); + setDouble(5, metersOr0ForNull(trackedRace.getDistanceTraveled(competitor, now))); + setDouble(6, secondsOr0ForNull(trackedRace.getTimeSailedSinceRaceStart(competitor, now))); + setDouble(7, metersOr0ForNull(trackedRace.getAverageSignedCrossTrackError(competitor, now, /* waitForLatest */ false))); + setDouble(8, metersOr0ForNull(trackedRace.getAverageAbsoluteCrossTrackError(competitor, now, /* waitForLatest */ false))); + final Iterable maneuvers = trackedRace.getManeuvers(competitor, /* waitForLatest */ false); + getPreparedStatement().setInt(9, Util.size(Util.filter(maneuvers, m->m.getType() == ManeuverType.TACK))); + getPreparedStatement().setInt(10, Util.size(Util.filter(maneuvers, m->m.getType() == ManeuverType.JIBE))); + getPreparedStatement().setInt(11, Util.size(Util.filter(maneuvers, m->m.getType() == ManeuverType.PENALTY_CIRCLE))); + final TimePoint startOfRace = trackedRace.getStartOfRace(); + final double startDelay; + Tack startTack; + if (raceColumnFleetAndTrackedRace.getStartWaypoint() != null && startOfRace != null) { + NavigableSet competitorMarkPassings = trackedRace.getMarkPassings(competitor); + trackedRace.lockForRead(competitorMarkPassings); + try { + if (!Util.isEmpty(competitorMarkPassings)) { + final MarkPassing competitorStartMarkPassing = competitorMarkPassings.iterator().next(); + final TimePoint competitorStartTime = competitorStartMarkPassing.getTimePoint(); + startDelay = secondsOr0ForNull(startOfRace.until(competitorStartTime)); + try { + startTack = trackedRace.getTack(competitor, competitorStartTime); + } catch (NoWindException e) { + startTack = null; + } + } else { + startDelay = 0; + startTack = null; + } + } finally { + trackedRace.unlockAfterRead(competitorMarkPassings); + } + } else { + startDelay = 0; + startTack = null; + } + setDouble(12, startDelay); + if (startOfRace != null) { + setDouble(13, metersOr0ForNull(trackedRace.getDistanceToStartLine(competitor, startOfRace))); + final Speed speedWhenCrossingStartLine = trackedRace.getSpeedWhenCrossingStartLine(competitor); + setDouble(14, speedWhenCrossingStartLine==null?0:speedWhenCrossingStartLine.getKnots()); + getPreparedStatement().setString(15, startTack==null?null:startTack.name()); + } else { + setDouble(13, 0); + setDouble(14, 0); + } + } +} diff --git a/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertRegattaStatement.java b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertRegattaStatement.java new file mode 100755 index 00000000000..d5eb88f99a3 --- /dev/null +++ b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertRegattaStatement.java @@ -0,0 +1,43 @@ +package com.sap.sailing.hanaexport.jaxrs.api; + +import java.sql.Connection; +import java.sql.SQLException; + +import com.sap.sailing.domain.base.Event; +import com.sap.sailing.domain.base.Regatta; +import com.sap.sailing.hanaexport.jaxrs.api.InsertRegattaStatement.RegattaAndEvent; + +public class InsertRegattaStatement extends AbstractPreparedInsertStatement { + static class RegattaAndEvent { + private final Regatta regatta; + private final Event event; + public RegattaAndEvent(Regatta regatta, Event event) { + super(); + this.regatta = regatta; + this.event = event; + } + public Regatta getRegatta() { + return regatta; + } + public Event getEvent() { + return event; + } + } + + protected InsertRegattaStatement(Connection connection) throws SQLException { + super(connection.prepareStatement( + "INSERT INTO SAILING.\"Regatta\" (\"name\", \"boatClass\", \"scoringScheme\", \"rankingMetric\", \"eventId\") "+ + "VALUES (?, ?, ?, ?, ?);")); + } + + @Override + public void parameterizeStatement(RegattaAndEvent regattaAndEvent) throws SQLException { + final Regatta regatta = regattaAndEvent.getRegatta(); + final Event event = regattaAndEvent.getEvent(); + getPreparedStatement().setString(1, regatta.getName()); + getPreparedStatement().setString(2, regatta.getBoatClass().getName()); + getPreparedStatement().setString(3, regatta.getScoringScheme().getType().name()); + getPreparedStatement().setString(4, regatta.getRankingMetricType().name()); + getPreparedStatement().setString(5, event == null ? null : event.getId().toString()); + } +} diff --git a/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertScoringSchemeStatement.java b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertScoringSchemeStatement.java new file mode 100755 index 00000000000..009b4150535 --- /dev/null +++ b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/InsertScoringSchemeStatement.java @@ -0,0 +1,19 @@ +package com.sap.sailing.hanaexport.jaxrs.api; + +import java.sql.Connection; +import java.sql.SQLException; + +import com.sap.sailing.domain.leaderboard.ScoringScheme; + +public class InsertScoringSchemeStatement extends AbstractPreparedInsertStatement { + protected InsertScoringSchemeStatement(Connection connection) throws SQLException { + super(connection.prepareStatement( + "INSERT INTO SAILING.\"ScoringScheme\" (\"id\", \"higherIsBetter\") VALUES (?, ?);")); + } + + @Override + public void parameterizeStatement(ScoringScheme scoringScheme) throws SQLException { + getPreparedStatement().setString(1, scoringScheme.getType().name()); + getPreparedStatement().setBoolean(2, scoringScheme.isHigherBetter()); + } +} diff --git a/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/PreparedInsertStatement.java b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/PreparedInsertStatement.java new file mode 100755 index 00000000000..f92e64ee014 --- /dev/null +++ b/java/com.sap.sailing.hanaexport/src/com/sap/sailing/hanaexport/jaxrs/api/PreparedInsertStatement.java @@ -0,0 +1,37 @@ +package com.sap.sailing.hanaexport.jaxrs.api; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +/** + * Can insert objects of type {@code T} using a prepared statement, in batch mode. + * + * @author Axel Uhl (D043530) + * + * @param + */ +public interface PreparedInsertStatement { + PreparedStatement getPreparedStatement(); + + void parameterizeStatement(T objectToInsert) throws SQLException; + + default void insert(T objectToInsert) throws SQLException { + parameterizeStatement(objectToInsert); + getPreparedStatement().execute(); + } + + default void insertBatch(Iterable objectsToInsert) throws SQLException { + for (final T objectToInsert : objectsToInsert) { + insertBatch(objectToInsert); + } + } + + default void insertBatch(final T objectToInsert) throws SQLException { + parameterizeStatement(objectToInsert); + getPreparedStatement().addBatch(); + } + + default int[] executeBatch() throws SQLException { + return getPreparedStatement().executeBatch(); + } +} diff --git a/java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/server/LandscapeManagementWriteServiceImpl.java b/java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/server/LandscapeManagementWriteServiceImpl.java index 466b0ee3b9d..84f9ebe23df 100755 --- a/java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/server/LandscapeManagementWriteServiceImpl.java +++ b/java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/server/LandscapeManagementWriteServiceImpl.java @@ -141,7 +141,7 @@ public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRem /** * The timeout for a host to come up */ - private static final Optional WAIT_FOR_HOST_TIMEOUT = Optional.of(Duration.ONE_MINUTE.times(10)); + private static final Optional WAIT_FOR_HOST_TIMEOUT = Optional.of(Duration.ONE_MINUTE.times(30)); private static final String SAILING_TARGET_GROUP_NAME_PREFIX = "S-"; diff --git a/java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/procedures/SailingAnalyticsReplicaConfiguration.java b/java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/procedures/SailingAnalyticsReplicaConfiguration.java index eca6ecf044f..87b3405b25c 100755 --- a/java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/procedures/SailingAnalyticsReplicaConfiguration.java +++ b/java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/procedures/SailingAnalyticsReplicaConfiguration.java @@ -8,6 +8,8 @@ import com.sap.sse.landscape.DefaultProcessConfigurationVariables; import com.sap.sse.landscape.InboundReplicationConfiguration; import com.sap.sse.landscape.OutboundReplicationConfiguration; import com.sap.sse.landscape.ProcessConfigurationVariable; +import com.sap.sse.landscape.mongodb.Database; +import com.sap.sse.landscape.mongodb.MongoEndpoint; public class SailingAnalyticsReplicaConfiguration extends SailingAnalyticsApplicationConfiguration { @@ -24,6 +26,8 @@ extends SailingAnalyticsApplicationConfiguration { * {@code "live-replica-server"}. *
  • If no {@link #setDatabaseName(String) database name is set} explicitly, it defaults to the * {@link #getServerName() server name} with the suffix {@code -replica} appended to it.
  • + *
  • If no {@link #setDatabaseConfiguration(Database) database configuration is set} explicitly, it defaults + * to the "replica" replica set on "localhost:27017" on the replica node.
  • * * * TODO we could default the set of replicables to replicate, competing with the live-replica-server environment @@ -40,6 +44,8 @@ extends SailingAnalyticsApplicationConfiguration { protected static class BuilderImpl, ShardingKey> extends SailingAnalyticsApplicationConfiguration.BuilderImpl, ShardingKey> implements Builder { + private static final String DEFAULT_MONGO_REPLICA_SET_NAME_FOR_REPLICA = "replica"; + @Override public OutboundReplicationConfiguration getOutboundReplicationConfiguration() { final OutboundReplicationConfiguration.Builder resultBuilder; @@ -56,6 +62,12 @@ extends SailingAnalyticsApplicationConfiguration { } return resultBuilder.build(); } + + @Override + protected Database getDatabaseConfiguration() { + return isDatabaseConfigurationSet() ? super.getDatabaseConfiguration() : + Database.of(MongoEndpoint.of("localhost", 27017, DEFAULT_MONGO_REPLICA_SET_NAME_FOR_REPLICA), getDatabaseName()); + } @Override public Optional getInboundReplicationConfiguration() { diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java index e322dbaa99a..3e82f634a59 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java @@ -43,6 +43,7 @@ import org.mockito.Matchers; import org.mockito.Mockito; import com.mongodb.MongoException; +import com.mongodb.ReadConcern; import com.mongodb.WriteConcern; import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl; @@ -264,7 +265,7 @@ public class MasterDataImportTest { .spy(new RacingEventServiceImpl(null, MongoSensorFixStoreFactory.INSTANCE.getMongoGPSFixStore( PersistenceFactory.INSTANCE .getDefaultMongoObjectFactory(serviceFinderFactory), PersistenceFactory.INSTANCE - .getDefaultDomainObjectFactory(), serviceFinderFactory, WriteConcern.MAJORITY), serviceFinderFactory)); + .getDefaultDomainObjectFactory(), serviceFinderFactory, ReadConcern.MAJORITY, WriteConcern.MAJORITY), serviceFinderFactory)); Mockito.doReturn(securityService).when(sourceService).getSecurityService(); Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */null, eventStartDate, eventEndDate, "testVenue", false, eventUUID); diff --git a/java/com.sap.sailing.server.testsupport/src/com/sap/sailing/server/testsupport/RacingEventServiceImplMock.java b/java/com.sap.sailing.server.testsupport/src/com/sap/sailing/server/testsupport/RacingEventServiceImplMock.java index d40b27f7142..707f8d3b167 100644 --- a/java/com.sap.sailing.server.testsupport/src/com/sap/sailing/server/testsupport/RacingEventServiceImplMock.java +++ b/java/com.sap.sailing.server.testsupport/src/com/sap/sailing/server/testsupport/RacingEventServiceImplMock.java @@ -5,6 +5,7 @@ import java.util.Map; import java.util.Set; import com.mongodb.MongoException; +import com.mongodb.ReadConcern; import com.mongodb.WriteConcern; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.common.impl.DataImportProgressImpl; @@ -36,7 +37,7 @@ public abstract class RacingEventServiceImplMock extends RacingEventServiceImpl super(null, MongoSensorFixStoreFactory.INSTANCE.getMongoGPSFixStore( PersistenceFactory.INSTANCE .getDefaultMongoObjectFactory(serviceFinderFactory), PersistenceFactory.INSTANCE - .getDefaultDomainObjectFactory(), serviceFinderFactory, WriteConcern.MAJORITY), serviceFinderFactory); + .getDefaultDomainObjectFactory(), serviceFinderFactory, ReadConcern.MAJORITY, WriteConcern.MAJORITY), serviceFinderFactory); lock = new DataImportLockWithProgress(); lock.addProgress(dataImportProgressImpl.getOperationId(), dataImportProgressImpl); } diff --git a/java/com.sap.sailing.www/.well-known/security.txt b/java/com.sap.sailing.www/.well-known/security.txt new file mode 100755 index 00000000000..55103ea6e09 --- /dev/null +++ b/java/com.sap.sailing.www/.well-known/security.txt @@ -0,0 +1,5 @@ +Contact: https://www.sap.com/report-a-vulnerability +Encryption: https://www.sap.com/pgp-keyblock +Policy: https://wiki.scn.sap.com/wiki/x/1s-iGg +Acknowledgments: https://wiki.scn.sap.com/wiki/x/rc-iGg +Expires: Mon, 31 Jan 2022 12:00 +0100 \ No newline at end of file diff --git a/java/com.sap.sailing.www/build.properties b/java/com.sap.sailing.www/build.properties index 2ce035d38bc..74bcce9ed8f 100644 --- a/java/com.sap.sailing.www/build.properties +++ b/java/com.sap.sailing.www/build.properties @@ -3,6 +3,7 @@ source.. = src/,\ output.. = bin/ bin.includes = META-INF/,\ .,\ + ./.well-known/,\ WEB-INF/web.xml,\ index.html,\ release_notes.css,\ diff --git a/java/com.sap.sse.common/src/com/sap/sse/common/Util.java b/java/com.sap.sse.common/src/com/sap/sse/common/Util.java index 9d6bbad99c4..de3a0fe6c99 100755 --- a/java/com.sap.sse.common/src/com/sap/sse/common/Util.java +++ b/java/com.sap.sse.common/src/com/sap/sse/common/Util.java @@ -789,16 +789,20 @@ public class Util { } public static List sortNamedCollection(Collection collection) { + return sortNamedCollection(collection, /* caseSensitive */ true); + } + + public static List sortNamedCollection(Collection collection, boolean caseSensitive) { List sortedCollection = new ArrayList<>(collection); - Collections.sort(sortedCollection, naturalNamedComparator()); + Collections.sort(sortedCollection, naturalNamedComparator(caseSensitive)); return sortedCollection; } - - public static Comparator naturalNamedComparator() { + + public static Comparator naturalNamedComparator(boolean caseSensitive) { return new Comparator() { @Override public int compare(T o1, T o2) { - return new NaturalComparator().compare(o1.getName(), o2.getName()); + return new NaturalComparator(caseSensitive).compare(o1.getName(), o2.getName()); } }; } diff --git a/java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/orchestration/AwsApplicationConfiguration.java b/java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/orchestration/AwsApplicationConfiguration.java index c07523fee00..f5c585da474 100755 --- a/java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/orchestration/AwsApplicationConfiguration.java +++ b/java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/orchestration/AwsApplicationConfiguration.java @@ -190,7 +190,11 @@ implements UserDataProvider { } protected Database getDatabaseConfiguration() { - return databaseConfiguration == null ? getLandscape().getDatabase(getRegion(), getDatabaseName()) : databaseConfiguration; + return isDatabaseConfigurationSet() ? databaseConfiguration : getLandscape().getDatabase(getRegion(), getDatabaseName()); + } + + protected boolean isDatabaseConfigurationSet() { + return databaseConfiguration != null; } @Override diff --git a/java/com.sap.sse.landscape/src/com/sap/sse/landscape/mongodb/Database.java b/java/com.sap.sse.landscape/src/com/sap/sse/landscape/mongodb/Database.java index 92f63b7fec5..394f17bdd5f 100755 --- a/java/com.sap.sse.landscape/src/com/sap/sse/landscape/mongodb/Database.java +++ b/java/com.sap.sse.landscape/src/com/sap/sse/landscape/mongodb/Database.java @@ -5,14 +5,18 @@ import java.net.URISyntaxException; import java.util.HashMap; import java.util.Map; import java.util.Optional; +import java.util.logging.Logger; import com.mongodb.client.MongoDatabase; import com.sap.sse.common.Named; import com.sap.sse.landscape.DefaultProcessConfigurationVariables; import com.sap.sse.landscape.ProcessConfigurationVariable; import com.sap.sse.landscape.UserDataProvider; +import com.sap.sse.landscape.mongodb.impl.DatabaseImpl; public interface Database extends UserDataProvider, Named { + static final Logger logger = Logger.getLogger(Database.class.getName()); + /** * Obtains the database-specific connection URI that clients can use to connect to the replica set hosting this * database. See also {@link MongoReplicaSet#getConnectionURI()}. @@ -48,5 +52,12 @@ public interface Database extends UserDataProvider, Named { /** * Drops the database. */ - void drop() throws URISyntaxException; + default void drop() throws URISyntaxException { + logger.info("Dropping database "+this); + getMongoDatabase().drop(); + } + + static Database of(MongoEndpoint mongoEndpoint, String databaseName) { + return new DatabaseImpl(mongoEndpoint, databaseName); + } } diff --git a/java/com.sap.sse.landscape/src/com/sap/sse/landscape/mongodb/MongoEndpoint.java b/java/com.sap.sse.landscape/src/com/sap/sse/landscape/mongodb/MongoEndpoint.java index 87a16cdc59a..ca0d960eb1a 100755 --- a/java/com.sap.sse.landscape/src/com/sap/sse/landscape/mongodb/MongoEndpoint.java +++ b/java/com.sap.sse.landscape/src/com/sap/sse/landscape/mongodb/MongoEndpoint.java @@ -9,6 +9,7 @@ import com.mongodb.MongoClientURI; import com.mongodb.client.MongoDatabase; import com.mongodb.session.ClientSession; import com.sap.sse.common.Duration; +import com.sap.sse.landscape.mongodb.impl.SimpleMongoEndpointImpl; /** * A MongoDB endpoint that an application can connect to. It can produce a {@link URI} the client can use to connect to, @@ -19,6 +20,14 @@ import com.sap.sse.common.Duration; * */ public interface MongoEndpoint { + static MongoEndpoint of(String hostname, int port) { + return new SimpleMongoEndpointImpl(hostname, port); + } + + static MongoEndpoint of(String hostname, int port, String replicaSetName) { + return new SimpleMongoEndpointImpl(hostname, port, replicaSetName); + } + /** * When invoked on a {@link MongoProcess} that is not currently equipped with a public IP address, a * {@link NullPointerException} will result. Consider using {@link #getURI(Optional, Optional)} to wait for @@ -28,6 +37,24 @@ public interface MongoEndpoint { URI getURI(Optional optionalDb, Optional timeoutEmptyMeaningForever) throws URISyntaxException; + default URI getURI(Optional optionalDb, String hostname, int port, Optional replicaSetName) throws URISyntaxException { + final StringBuilder sb = new StringBuilder(); + sb.append("mongodb://"); + sb.append(hostname); + sb.append(":"); + sb.append(port); + sb.append("/"); + optionalDb.ifPresent(db->sb.append(db.getName())); + replicaSetName.ifPresent(rsName->appendReplicaSetParametersToURI(rsName, sb)); + return new URI(sb.toString()); + } + + default void appendReplicaSetParametersToURI(String replicaSetName, StringBuilder uriStringBuilder) { + uriStringBuilder.append("?replicaSet="); + uriStringBuilder.append(replicaSetName); + uriStringBuilder.append("&retryWrites=true&readPreference=nearest"); + } + /** * Lists all MongoDB databases available in this end point */ diff --git a/java/com.sap.sse.landscape/src/com/sap/sse/landscape/mongodb/MongoProcess.java b/java/com.sap.sse.landscape/src/com/sap/sse/landscape/mongodb/MongoProcess.java index 44d2756d010..409b3c11cbf 100755 --- a/java/com.sap.sse.landscape/src/com/sap/sse/landscape/mongodb/MongoProcess.java +++ b/java/com.sap.sse.landscape/src/com/sap/sse/landscape/mongodb/MongoProcess.java @@ -11,25 +11,14 @@ import com.sap.sse.landscape.RotatingFileBasedLog; public interface MongoProcess extends Process, MongoEndpoint { @Override default URI getURI(Optional optionalDb) throws URISyntaxException { - return getURI(optionalDb, getHostname()); + return getURI(optionalDb, getHostname(), getPort(), /* no replica set name */ Optional.empty()); } @Override default URI getURI(Optional optionalDb, Optional timeoutEmptyMeaningForever) throws URISyntaxException { - return getURI(optionalDb, getHostname(timeoutEmptyMeaningForever)); + return getURI(optionalDb, getHostname(timeoutEmptyMeaningForever), getPort(), /* no replica set name */ Optional.empty()); } - default URI getURI(Optional optionalDb, String hostname) throws URISyntaxException { - final StringBuilder sb = new StringBuilder(); - sb.append("mongodb://"); - sb.append(hostname); - sb.append(":"); - sb.append(getPort()); - sb.append("/"); - optionalDb.ifPresent(db->sb.append(db.getName())); - return new URI(sb.toString()); - } - boolean isInReplicaSet() throws URISyntaxException; /** diff --git a/java/com.sap.sse.landscape/src/com/sap/sse/landscape/mongodb/MongoReplicaSet.java b/java/com.sap.sse.landscape/src/com/sap/sse/landscape/mongodb/MongoReplicaSet.java index 3fce02db376..1aabc59f6da 100755 --- a/java/com.sap.sse.landscape/src/com/sap/sse/landscape/mongodb/MongoReplicaSet.java +++ b/java/com.sap.sse.landscape/src/com/sap/sse/landscape/mongodb/MongoReplicaSet.java @@ -49,9 +49,7 @@ public interface MongoReplicaSet extends Named, MongoEndpoint { result.append(String.join(",", hostSpecs)); result.append("/"); optionalDb.ifPresent(db->result.append(db.getName())); - result.append("?replicaSet="); - result.append(getName()); - result.append("&retryWrites=true&readPreference=nearest"); + appendReplicaSetParametersToURI(getName(), result); return new URI(result.toString()); } diff --git a/java/com.sap.sse.landscape/src/com/sap/sse/landscape/mongodb/impl/DatabaseImpl.java b/java/com.sap.sse.landscape/src/com/sap/sse/landscape/mongodb/impl/DatabaseImpl.java index ff9a119b617..7605b3ca89f 100755 --- a/java/com.sap.sse.landscape/src/com/sap/sse/landscape/mongodb/impl/DatabaseImpl.java +++ b/java/com.sap.sse.landscape/src/com/sap/sse/landscape/mongodb/impl/DatabaseImpl.java @@ -1,14 +1,10 @@ package com.sap.sse.landscape.mongodb.impl; -import java.net.URISyntaxException; -import java.util.logging.Logger; - import com.sap.sse.common.impl.NamedImpl; import com.sap.sse.landscape.mongodb.Database; import com.sap.sse.landscape.mongodb.MongoEndpoint; public class DatabaseImpl extends NamedImpl implements Database { - private static final Logger logger = Logger.getLogger(DatabaseImpl.class.getName()); private static final long serialVersionUID = -3816220771822620058L; private final MongoEndpoint endpoint; @@ -22,12 +18,6 @@ public class DatabaseImpl extends NamedImpl implements Database { return endpoint; } - @Override - public void drop() throws URISyntaxException { - logger.info("Dropping database "+this); - getMongoDatabase().drop(); - } - @Override public String toString() { return "DatabaseImpl [endpoint=" + endpoint + "]"; diff --git a/java/com.sap.sse.landscape/src/com/sap/sse/landscape/mongodb/impl/SimpleMongoEndpointImpl.java b/java/com.sap.sse.landscape/src/com/sap/sse/landscape/mongodb/impl/SimpleMongoEndpointImpl.java new file mode 100755 index 00000000000..7d31bb91126 --- /dev/null +++ b/java/com.sap.sse.landscape/src/com/sap/sse/landscape/mongodb/impl/SimpleMongoEndpointImpl.java @@ -0,0 +1,35 @@ +package com.sap.sse.landscape.mongodb.impl; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Optional; + +import com.sap.sse.common.Duration; +import com.sap.sse.landscape.mongodb.Database; + +public class SimpleMongoEndpointImpl extends MongoEndpointImpl { + private final String hostname; + private final int port; + private final Optional replicaSetName; + + public SimpleMongoEndpointImpl(String hostname, int port) { + this(hostname, port, /* no replica set name */ null); + } + + public SimpleMongoEndpointImpl(String hostname, int port, String replicaSetName) { + super(); + this.hostname = hostname; + this.port = port; + this.replicaSetName = Optional.ofNullable(replicaSetName); + } + + @Override + public URI getURI(Optional optionalDb) throws URISyntaxException { + return getURI(optionalDb, hostname, port, replicaSetName); + } + + @Override + public URI getURI(Optional optionalDb, Optional timeoutEmptyMeaningForever) throws URISyntaxException { + return getURI(optionalDb); + } +} diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/UserTableWrapper.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/UserTableWrapper.java index 93bcc0cf894..8ab55d9d25a 100755 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/UserTableWrapper.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/UserTableWrapper.java @@ -26,6 +26,7 @@ import com.google.gwt.user.cellview.client.TextColumn; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Label; +import com.sap.sse.common.Util; import com.sap.sse.common.util.NaturalComparator; import com.sap.sse.gwt.client.ErrorReporter; import com.sap.sse.gwt.client.celltable.AbstractSortableTextColumn; @@ -200,7 +201,7 @@ extends TableWrapper { string.add(t.getFullName()); string.add(t.getEmail()); string.add(t.getCompany()); - string.add(t.getUserGroups().toString()); + Util.addAll(Util.map(t.getUserGroups(), StrippedUserGroupDTO::getName), string); return string; } diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/userprofile/shared/userdetails/AbstractUserDetails.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/userprofile/shared/userdetails/AbstractUserDetails.java index 3fb73874b3c..705659fed00 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/userprofile/shared/userdetails/AbstractUserDetails.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/userprofile/shared/userdetails/AbstractUserDetails.java @@ -91,7 +91,8 @@ public class AbstractUserDetails extends Composite implements UserDetailsView { StrippedUserGroupDTO defaultTennant = currentUser.getDefaultTenant(); defaultTenantUi.clear(); int i = 0; - for (StrippedUserGroupDTO group : Util.sortNamedCollection(currentUser.getUserGroups())) { + for (StrippedUserGroupDTO group : Util.sortNamedCollection(currentUser.getUserGroups(), + /* caseSensitive */ false)) { defaultTenantUi.addItem(group.getName(), group.getId().toString()); if (Util.equalsWithNull(group, defaultTennant)) { defaultTenantUi.setSelectedIndex(i); diff --git a/java/pom.xml b/java/pom.xml index 6695afb4c3c..743c80d41c2 100755 --- a/java/pom.xml +++ b/java/pom.xml @@ -536,7 +536,7 @@ - -Dgwt.persistentunitcache=false -Xmx2560m -Djava.io.tmpdir=${env.TMP} -Dgwt.watchFileChanges=false + -Dgwt.persistentunitcache=false -Xmx4g -Djava.io.tmpdir=${env.TMP} -Dgwt.watchFileChanges=false 4