Merge branch 'master' into bug2823

This commit is contained in:
Axel Uhl
2021-08-23 23:44:29 +02:00
45 changed files with 962 additions and 371 deletions
+2 -1
View File
@@ -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
+4 -3
View File
@@ -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
@@ -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
@@ -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);
@@ -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);
}
@@ -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
@@ -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
@@ -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
@@ -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
@@ -542,7 +542,7 @@ widthInPx=幅 (ピクセル)
heightInPx=高さ (ピクセル)
tags=タグ
size=サイズ
duration=時間 (h:mm:ss.ms)
duration=時間 (h:mm:ss)
addMediaTrack=音声/動画追加
addYoutubeTrack=YouTube 追加
youtubeId=YouTube ID
@@ -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
@@ -542,7 +542,7 @@ widthInPx=Ширина (в пикселях)
heightInPx=Высота (в пикселях)
tags=Теги
size=Размер
duration=Длительность (ч:мм:сс.мс)
duration=Длительность (ч:мм:сс)
addMediaTrack=Добавить аудио/видео
addYoutubeTrack=Добавить Youtube
youtubeId=Ид. Youtube
@@ -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
@@ -542,7 +542,7 @@ widthInPx=宽度(像素)
heightInPx=高度(像素)
tags=标记
size=大小
duration=持续时间 (h:mm:ss.ms)
duration=持续时间 (h:mm:ss)
addMediaTrack=添加音频/视频
addYoutubeTrack=添加 Youtube
youtubeId=Youtube 编号
@@ -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;
@@ -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,
@@ -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<String> dbEndpoint, Optional<String> dbUser, Optional<String> dbPassword) throws SQLException;
}
@@ -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<String> dbEndpoint, Optional<String> dbUser, Optional<String> 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 {
@@ -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<T> implements PreparedInsertStatement<T> {
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();
}
}
@@ -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<Event> events;
private final Set<Competitor> 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<String> leaderboardGroupIds,
@QueryParam("eventid") List<String> 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<Regatta> 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<Regatta> getRegattasToExport(RacingEventService racingEventService, List<String> leaderboardGroupIds, List<String> eventIds) {
final Set<LeaderboardGroup> leaderboardGroups = new HashSet<>();
final Iterable<Regatta> 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<Regatta> 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, "<null>");
} 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<Regatta> 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<TimePoint, Maneuver> 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<Maneuver> 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<MarkPassing> 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<TimePoint> 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);
}
}
}
@@ -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<BoatClass> {
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, "<null>");
} 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());
}
}
@@ -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<Competitor> {
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);
}
}
@@ -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<Event> {
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);
}
}
@@ -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<MaxPointsReason> {
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());
}
}
@@ -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<TrackedLegAndNow> {
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);
}
}
}
@@ -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<TrackedLegOfCompetitorRankingInfoCacheAndNow> {
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())));
}
}
@@ -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<ManeuverTrackedRaceAndCompetitor> {
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());
}
}
@@ -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<RaceResult> {
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());
}
}
@@ -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<TrackedRaceWithRaceColumnAndFleet> {
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<TimePoint> averageWind = trackedRace.getAverageWindSpeedWithConfidenceWithNumberOfSamples(/* number of samples */ 5);
if (averageWind != null) {
setDouble(9, averageWind.getObject().getKnots());
} else {
setDouble(9, 0.0);
}
}
}
@@ -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<TrackedRaceWithCompetitorAndStartWaypoint> {
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<Maneuver> 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<MarkPassing> 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);
}
}
}
@@ -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<RegattaAndEvent> {
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());
}
}
@@ -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<ScoringScheme> {
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());
}
}
@@ -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 <T>
*/
public interface PreparedInsertStatement<T> {
PreparedStatement getPreparedStatement();
void parameterizeStatement(T objectToInsert) throws SQLException;
default void insert(T objectToInsert) throws SQLException {
parameterizeStatement(objectToInsert);
getPreparedStatement().execute();
}
default void insertBatch(Iterable<T> 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();
}
}
@@ -141,7 +141,7 @@ public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRem
/**
* The timeout for a host to come up
*/
private static final Optional<Duration> WAIT_FOR_HOST_TIMEOUT = Optional.of(Duration.ONE_MINUTE.times(10));
private static final Optional<Duration> WAIT_FOR_HOST_TIMEOUT = Optional.of(Duration.ONE_MINUTE.times(30));
private static final String SAILING_TARGET_GROUP_NAME_PREFIX = "S-";
@@ -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<ShardingKey>
extends SailingAnalyticsApplicationConfiguration<ShardingKey> {
@@ -24,6 +26,8 @@ extends SailingAnalyticsApplicationConfiguration<ShardingKey> {
* {@code "live-replica-server"}.</li>
* <li>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.</li>
* <li>If no {@link #setDatabaseConfiguration(Database) database configuration is set} explicitly, it defaults
* to the "replica" replica set on "localhost:27017" on the replica node.</li>
* </ul>
*
* TODO we could default the set of replicables to replicate, competing with the live-replica-server environment
@@ -40,6 +44,8 @@ extends SailingAnalyticsApplicationConfiguration<ShardingKey> {
protected static class BuilderImpl<BuilderT extends Builder<BuilderT, ShardingKey>, ShardingKey>
extends SailingAnalyticsApplicationConfiguration.BuilderImpl<BuilderT, SailingAnalyticsReplicaConfiguration<ShardingKey>, ShardingKey>
implements Builder<BuilderT, ShardingKey> {
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<ShardingKey> {
}
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<InboundReplicationConfiguration> getInboundReplicationConfiguration() {
@@ -3,6 +3,7 @@ source.. = src/,\
output.. = bin/
bin.includes = META-INF/,\
.,\
./.well-known/,\
WEB-INF/web.xml,\
index.html,\
release_notes.css,\
@@ -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
@@ -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);
}
}
@@ -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<Database> optionalDb, Optional<Duration> timeoutEmptyMeaningForever) throws URISyntaxException;
default URI getURI(Optional<Database> optionalDb, String hostname, int port, Optional<String> 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
*/
@@ -11,25 +11,14 @@ import com.sap.sse.landscape.RotatingFileBasedLog;
public interface MongoProcess extends Process<RotatingFileBasedLog, MongoMetrics>, MongoEndpoint {
@Override
default URI getURI(Optional<Database> optionalDb) throws URISyntaxException {
return getURI(optionalDb, getHostname());
return getURI(optionalDb, getHostname(), getPort(), /* no replica set name */ Optional.empty());
}
@Override
default URI getURI(Optional<Database> optionalDb, Optional<Duration> timeoutEmptyMeaningForever) throws URISyntaxException {
return getURI(optionalDb, getHostname(timeoutEmptyMeaningForever));
return getURI(optionalDb, getHostname(timeoutEmptyMeaningForever), getPort(), /* no replica set name */ Optional.empty());
}
default URI getURI(Optional<Database> 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;
/**
@@ -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());
}
@@ -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 + "]";
@@ -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<String> 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<Database> optionalDb) throws URISyntaxException {
return getURI(optionalDb, hostname, port, replicaSetName);
}
@Override
public URI getURI(Optional<Database> optionalDb, Optional<Duration> timeoutEmptyMeaningForever) throws URISyntaxException {
return getURI(optionalDb);
}
}
@@ -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<UserDTO, S, StringMessages, TR> {
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;
}
+1 -1
View File
@@ -536,7 +536,7 @@
<!-- GWT version detected from dependencyManagement -->
<execution>
<configuration>
<extraJvmArgs>-Dgwt.persistentunitcache=false -Xmx2560m -Djava.io.tmpdir=${env.TMP} -Dgwt.watchFileChanges=false</extraJvmArgs>
<extraJvmArgs>-Dgwt.persistentunitcache=false -Xmx4g -Djava.io.tmpdir=${env.TMP} -Dgwt.watchFileChanges=false</extraJvmArgs>
<fragmentCount>4</fragmentCount>
<!--
<userAgents>chrome</userAgents>