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.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.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..11c55022a73 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, 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..6c392df3933 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,127 +131,92 @@ 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(); + alreadyInsertedContent.ensureEventIsInserted(event); + insertRegattas.insert(new RegattaAndEvent(regatta, event)); if (leaderboard != null) { for (final RaceColumn raceColumn : leaderboard.getRaceColumns()) { for (final Competitor competitor : raceColumn.getAllCompetitors()) { - parameterizeInsertRaceResult(insertRaceResults, now, competitor, leaderboard, raceColumn, regatta); - insertRaceResults.addBatch(); + alreadyInsertedContent.ensureCompetitorIsInserted(competitor); + insertRaceResults.insertBatch(new InsertRaceResultStatement.RaceResult(regatta, leaderboard, competitor, raceColumn, now)); } insertRaceResults.executeBatch(); for (final Fleet fleet : raceColumn.getFleets()) { @@ -230,22 +224,18 @@ public class HanaCloudSacExportResource extends SharedAbstractSailingServerResou if (trackedRace != null) { final RankingInfo rankingInfo = trackedRace.getRankingMetric().getRankingInfo(now); final WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache = new LeaderboardDTOCalculationReuseCache(now); - parameterizeInsertRacesStatement(insertRaces, regatta, raceColumn, fleet, trackedRace); - insertRaces.addBatch(); + insertRaces.insertBatch(new InsertRaceStatement.TrackedRaceWithRaceColumnAndFleet(trackedRace, raceColumn, fleet)); for (final TrackedLeg trackedLeg : trackedRace.getTrackedLegs()) { - parameterizeInsertLegsStatement(insertLegs, now, regatta, trackedRace, trackedLeg); - insertLegs.addBatch(); + insertLegs.insertBatch(new InsertLegStatement.TrackedLegAndNow(now, trackedLeg)); for (final Competitor competitor : trackedRace.getRace().getCompetitors()) { - parameterizeInsertLegStatsStatement(insertLegStats, now, trackedLeg.getTrackedLeg(competitor), rankingInfo, cache); - insertLegStats.addBatch(); + insertLegStats.insertBatch(new InsertLegStatsStatement.TrackedLegOfCompetitorRankingInfoCacheAndNow(now, rankingInfo, cache, trackedLeg.getTrackedLeg(competitor))); } } } 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(); + 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) { @@ -253,8 +243,7 @@ public class HanaCloudSacExportResource extends SharedAbstractSailingServerResou } } for (final Maneuver maneuver : timepointUniqueManeuvers.values()) { - parameterizeInsertManeuvers(insertManeuvers, competitor, maneuver, trackedRace); - insertManeuvers.addBatch(); + insertManeuvers.insertBatch(new InsertManeuverStatement.ManeuverTrackedRaceAndCompetitor(maneuver, trackedRace, competitor)); } } } @@ -270,188 +259,18 @@ public class HanaCloudSacExportResource extends SharedAbstractSailingServerResou } } - 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 +308,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.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.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/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