Merge branch 'master' of ssh://sapsailing.com/home/trac/git into bug1873

This commit is contained in:
Thomas Stokes
2024-03-18 13:31:51 +01:00
36 changed files with 258 additions and 123 deletions
@@ -216,4 +216,10 @@ myorigin =\$myhostname.sapsailing.com
postmap hash:${password_file_location}
systemctl restart postfix
rm -f "${temp_mail_properties_location}"
}
setup_sshd_resilience() {
echo "ClientAliveInterval 3
ClientAliveCountMax 3
GatewayPorts yes" >> /etc/ssh/sshd_config
}
@@ -34,6 +34,9 @@ setup_cloud_cfg_and_root_login
build_crontab_and_setup_files "${IMAGE_TYPE}" "${GIT_COPY_USER}" "${RELATIVE_PATH_TO_GIT}"
# setup mail
setup_mail_sending
# setup sshd config
setup_sshd_resilience
systemctl reload sshd.service
cd /usr/local/bin
echo $BEARER_TOKEN > /root/ssh-key-reader.token
# add basic test page which won't cause redirect error code if used as a health check.
@@ -8,6 +8,8 @@ public interface RaceFetcher {
/**
* Not for execution on the client; on the server, returns a <code>TrackedRace</code> object.
* The method will not block or wait for the tracked race to appear. If it isn't found (see also
* bug 5982) then {@code null} is returned.
*/
Object getTrackedRace(RegattaAndRaceIdentifier regattaNameAndRaceName);
@@ -9,11 +9,6 @@ public interface RaceIdentifier extends Serializable {
Object getRace(RaceFetcher raceFetcher);
/**
* Blocks and waits if the tracked race for this race identifier doesn't exist yet.
*/
Object getTrackedRace(RaceFetcher raceFetcher);
/**
* Immediately returns <code>null</code> if the tracked race for this race identifier doesn't exist yet.
*/
@@ -24,10 +24,6 @@ public class RegattaNameAndRaceName extends RegattaName implements RegattaAndRac
return raceFetcher.getRace(this);
}
@Override
public Object getTrackedRace(RaceFetcher raceFetcher) {
return raceFetcher.getTrackedRace(this);
}
@Override
public Object getExistingTrackedRace(RaceFetcher raceFetcher) {
return raceFetcher.getExistingTrackedRace(this);
}
@@ -154,6 +154,7 @@ public class TracTracConnectivityParamsHandler extends AbstractRaceTrackingConne
} else {
final String creatorName = SessionUtils.getPrincipal().toString();
final TracTracConfigurationImpl tracTracConfiguration = new TracTracConfigurationImpl(creatorName, tractracRace.getEvent().getName(), jsonURL,
// FIXME bug5983: stored/live URIs should be captured in the configuration only if they were specified explicitly when the parameters were created
(params.getLiveURI() == null ? null : params.getLiveURI().toString()),
/* 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
@@ -219,12 +219,16 @@ public abstract class TrackedRegattaImpl implements TrackedRegatta {
public void raceAdded(TrackedRace trackedRace) {
synchronized (mutex) { // TODO possible improvement: only notify if trackedRace.getRace() == race; otherwise it cannot have made a difference for getExistingTrackedRace(race)...
mutex.notifyAll();
} // TODO can't we remove the listener again here?
}
}
};
addRaceListener(listener, Optional.empty(), /* synchronous */ false);
try {
synchronized (mutex) {
if (getRegatta().getRaceByName(race.getName()) == null) {
throw new IllegalStateException("Race "+race.getName()+" not in regatta "+getRegatta().getName()+
"; not blocking for it to appear. It most likely won't");
}
result = getExistingTrackedRace(race);
while (!interrupted && result == null) {
try {
@@ -55,19 +55,19 @@ public class GetSixtyInchStatisticAction implements SailingAction<GetSixtyInchSt
final RaceDefinition race = context.getRacingEventService().getRace(identifier);
int competitors = com.sap.sse.common.Util.size(race.getCompetitors());
legs = race.getCourse().getLegs().size();
final DynamicTrackedRace trace = context.getRacingEventService().getTrackedRace(identifier);
final DynamicTrackedRace trackedRace = context.getRacingEventService().getTrackedRace(identifier);
Duration duration = null;
Distance distance = null;
TimePoint timePoint;
if (trace.getEndOfRace() == null) {
if (trackedRace.getEndOfRace() == null) {
timePoint = MillisecondsTimePoint.now();
duration = estimateDuration(trace, duration, timePoint);
distance = estimateDistance(trace, distance, timePoint);
duration = estimateDuration(trackedRace, duration, timePoint);
distance = estimateDistance(trackedRace, distance, timePoint);
} else {
final Iterator<Competitor> competitorIterator = trace.getCompetitorsFromBestToWorst(trace.getEndOfRace()).iterator();
final Iterator<Competitor> competitorIterator = trackedRace.getCompetitorsFromBestToWorst(trackedRace.getEndOfRace()).iterator();
if (competitorIterator.hasNext()) {
distance = trace.getDistanceTraveled(competitorIterator.next(), trace.getEndOfRace());
duration = trace.getStartOfRace().until(trace.getEndOfRace());
distance = trackedRace.getDistanceTraveled(competitorIterator.next(), trackedRace.getEndOfRace());
duration = trackedRace.getStartOfRace().until(trackedRace.getEndOfRace());
}
}
return new GetSixtyInchStatisticDTO(competitors, legs, duration, distance);
@@ -2486,5 +2486,5 @@ zeroBasedNumberOfWaypointForRepeatablePartEnd=Index koncového trasového bodu o
defaultNumberOfLapsMustNotBeNegative=Výchozí počet kol nesmí být záporný.
eitherNoneOrBothStartAndEndOfRepeatablePartMustBeSpecified=Pokud zadáte začátek opakovatelné části sekvence trasových bodů, musíte zadat i její konec.
endOfRepeatablePartMustBeAtOfAfterStart=Konec opakovatelné části musí být za jejím začátkem.
endOfRepeatablePartIsGreaterThanNumberOfWaypoints=Konec opakovatelné části přesahuje počet trasových bodů. Upravte konfiguraci dráhy.
endOfRepeatablePartIsGreaterThanNumberOfWaypoints=Konec opakovatelné části přesahuje počet trasových bodů.
configureCourse=Konfigurovat dráhu
@@ -2486,5 +2486,5 @@ zeroBasedNumberOfWaypointForRepeatablePartEnd=Índice de punto de ruta de final
defaultNumberOfLapsMustNotBeNegative=El número predeterminado de vueltas no debe ser negativo.
eitherNoneOrBothStartAndEndOfRepeatablePartMustBeSpecified=No se debe indicar ninguna o ambas, inicio y final, de la parte repetible de la secuencia de los puntos de ruta.
endOfRepeatablePartMustBeAtOfAfterStart=El final de la parte repetible debe ser igual o posterior al inicio de la parte repetible.
endOfRepeatablePartIsGreaterThanNumberOfWaypoints=El final de la parte repetible es mayor al número de waypoints.econfigureCourse
endOfRepeatablePartIsGreaterThanNumberOfWaypoints=El final de la parte repetible es mayor al número de waypoints.
configureCourse=Configurar rumbo
@@ -2486,5 +2486,5 @@ zeroBasedNumberOfWaypointForRepeatablePartEnd=Index des points de cheminement -
defaultNumberOfLapsMustNotBeNegative=Le nombre de tours par défaut ne doit pas être négatif.
eitherNoneOrBothStartAndEndOfRepeatablePartMustBeSpecified=Vous devez obligatoirement indiquer le début et la fin de la portion répétée de la séquence de points de cheminement (ou aucun des deux).
endOfRepeatablePartMustBeAtOfAfterStart=La fin de la portion répétée doit se situer au début de la portion répétée ou après.
endOfRepeatablePartIsGreaterThanNumberOfWaypoints=La fin de la portion répétée est supérieure au nombre de waypoints.econfigureCourse
endOfRepeatablePartIsGreaterThanNumberOfWaypoints=La fin de la portion répétée est supérieure au nombre de points de cheminement.
configureCourse=Configurer le parcours
@@ -2486,5 +2486,5 @@ zeroBasedNumberOfWaypointForRepeatablePartEnd=繰り返し可能順序終了変
defaultNumberOfLapsMustNotBeNegative=ラップのデフォルト数はマイナスであってはなりません。
eitherNoneOrBothStartAndEndOfRepeatablePartMustBeSpecified=変針点順序の繰り返し可能部分は、なしで指定するか、開始と終了の両方を指定する必要があります。
endOfRepeatablePartMustBeAtOfAfterStart=繰り返し可能部分の終了は、繰り返し可能部分の開始以降である必要があります。
endOfRepeatablePartIsGreaterThanNumberOfWaypoints=繰り返し可能部分の終了は、waypoints.econfigureCourse の数より大きくなります
endOfRepeatablePartIsGreaterThanNumberOfWaypoints=繰り返し可能部分の終了は、変針点の数より大きくなります
configureCourse=コースの設定
@@ -2486,5 +2486,5 @@ zeroBasedNumberOfWaypointForRepeatablePartEnd=Índice de waypoint de fim de sequ
defaultNumberOfLapsMustNotBeNegative=O número padrão de voltas não pode ser negativo.
eitherNoneOrBothStartAndEndOfRepeatablePartMustBeSpecified=Deve ser indicado o início e o fim da parte repetível da sequência de waypoint, os dois ou nenhum.
endOfRepeatablePartMustBeAtOfAfterStart=O fim da parte repetível deve ser o mesmo que ou após o início da parte repetível.
endOfRepeatablePartIsGreaterThanNumberOfWaypoints=O fim da parte repetível é maior que o número de waypoints.econfigureCourse
endOfRepeatablePartIsGreaterThanNumberOfWaypoints=O fim da parte repetível é maior que o número de waypoints.
configureCourse=Configurar curso
@@ -128,7 +128,7 @@ public class QuickRanksLiveCache {
public QuickRanksDTO get(RegattaAndRaceIdentifier raceIdentifier) {
QuickRanksDTO result = cache.get(raceIdentifier, false);
if (result == null) {
TrackedRace trackedRace = service.getExistingTrackedRace(raceIdentifier);
final TrackedRace trackedRace = service.getExistingTrackedRace(raceIdentifier);
if (trackedRace != null) {
trackedRace.addListener(new Listener(raceIdentifier)); // register for all changes that may affect the quick ranks
cache.triggerUpdate(raceIdentifier, CalculateOrPurge.CALCULATE);
@@ -662,7 +662,6 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
* Stops this service and frees its resources. In particular, caching services and threads owned by this service will be
* notified to stop their jobs.
*/
//UNCLEAR
public void stop() {
quickRanksLiveCache.stop();
}
@@ -1632,7 +1631,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
@Override
public SimulatorResultsDTO getSimulatorResults(LegIdentifier legIdentifier) {
DynamicTrackedRace trackedRace = getService().getTrackedRace(legIdentifier.getRaceIdentifier());
final DynamicTrackedRace trackedRace = getService().getTrackedRace(legIdentifier.getRaceIdentifier());
if (trackedRace == null) {
throw new IllegalArgumentException("Race for leg " + legIdentifier + " not found!");
}
@@ -3346,10 +3345,12 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
@Override
public DynamicTrackedRace getTrackedRace(RegattaAndRaceIdentifier regattaNameAndRaceName) {
Regatta regatta = getService().getRegattaByName(regattaNameAndRaceName.getRegattaName());
RaceDefinition race = getRaceByName(regatta, regattaNameAndRaceName.getRaceName());
DynamicTrackedRace trackedRace = getService().getOrCreateTrackedRegatta(regatta).getTrackedRace(race);
getSecurityService().checkCurrentUserReadPermission(trackedRace);
final Regatta regatta = getService().getRegattaByName(regattaNameAndRaceName.getRegattaName());
final RaceDefinition race = getRaceByName(regatta, regattaNameAndRaceName.getRaceName());
final DynamicTrackedRace trackedRace = getService().getOrCreateTrackedRegatta(regatta).getExistingTrackedRace(race);
if (trackedRace != null) {
getSecurityService().checkCurrentUserReadPermission(trackedRace);
}
return trackedRace;
}
@@ -5637,9 +5638,9 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
public Boolean checkIfRaceIsTracking(RegattaAndRaceIdentifier race) {
getSecurityService().checkCurrentUserReadPermission(race);
boolean result = false;
DynamicTrackedRace trace = getService().getTrackedRace(race);
if (trace != null) {
final TrackedRaceStatusEnum status = trace.getStatus().getStatus();
DynamicTrackedRace trackedRace = getService().getTrackedRace(race);
if (trackedRace != null) {
final TrackedRaceStatusEnum status = trackedRace.getStatus().getStatus();
if (status == TrackedRaceStatusEnum.LOADING || status == TrackedRaceStatusEnum.TRACKING) {
result = true;
}
@@ -6387,22 +6388,25 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
@Override
public boolean canSliceRace(RegattaAndRaceIdentifier raceIdentifier) {
final boolean result;
final Regatta regatta = getService().getRegattaByName(raceIdentifier.getRegattaName());
final Leaderboard regattaLeaderboard = getService().getLeaderboardByName(raceIdentifier.getRegattaName());
final DynamicTrackedRace trackedRace = getService().getTrackedRace(raceIdentifier);
getSecurityService().checkCurrentUserUpdatePermission(raceIdentifier);
getSecurityService().checkCurrentUserUpdatePermission(regattaLeaderboard);
getSecurityService().checkCurrentUserUpdatePermission(regatta);
final boolean result;
if (regatta == null || !(regattaLeaderboard instanceof RegattaLeaderboard) || trackedRace == null
|| trackedRace.getStartOfTracking() == null || !isSmartphoneTrackingEnabled(trackedRace)) {
if (trackedRace == null ) {
result = false;
} else {
final Pair<RaceColumn, Fleet> raceColumnAndFleetOfRaceToSlice = regattaLeaderboard
.getRaceColumnAndFleet(trackedRace);
result = (raceColumnAndFleetOfRaceToSlice != null); // is the TrackedRace associated to the given
// RegattaLeaderboard?
getSecurityService().checkCurrentUserUpdatePermission(raceIdentifier);
getSecurityService().checkCurrentUserUpdatePermission(regattaLeaderboard);
getSecurityService().checkCurrentUserUpdatePermission(regatta);
if (regatta == null || !(regattaLeaderboard instanceof RegattaLeaderboard) || trackedRace == null
|| trackedRace.getStartOfTracking() == null || !isSmartphoneTrackingEnabled(trackedRace)) {
result = false;
} else {
final Pair<RaceColumn, Fleet> raceColumnAndFleetOfRaceToSlice = regattaLeaderboard
.getRaceColumnAndFleet(trackedRace);
result = (raceColumnAndFleetOfRaceToSlice != null); // is the TrackedRace associated to the given
// RegattaLeaderboard?
}
}
return result;
}
@@ -2105,11 +2105,13 @@ public class SailingServiceWriteImpl extends SailingServiceImpl implements Saili
final List<DynamicTrackedRace> trackedRaces = new ArrayList<>();
if (selectedRaces != null && !selectedRaces.isEmpty()) {
for (RaceDTO raceDTO : selectedRaces) {
DynamicTrackedRace trackedRace = getTrackedRace(raceDTO.getRaceIdentifier());
// In case the user selected a distinct set of races, we want the call to completely fail if
// tracking wind isn't allowed for at least one race
getSecurityService().checkCurrentUserUpdatePermission(trackedRace);
trackedRaces.add(trackedRace);
final DynamicTrackedRace trackedRace = getTrackedRace(raceDTO.getRaceIdentifier());
if (trackedRace != null) {
// In case the user selected a distinct set of races, we want the call to completely fail if
// tracking wind isn't allowed for at least one race
getSecurityService().checkCurrentUserUpdatePermission(trackedRace);
trackedRaces.add(trackedRace);
}
}
} else {
for (DynamicTrackedRace race : getAllTrackedRaces()) {
@@ -2601,11 +2603,13 @@ public class SailingServiceWriteImpl extends SailingServiceImpl implements Saili
public RaceDTO setStartTimeReceivedForRace(RaceIdentifier raceIdentifier, Date newStartTimeReceived) {
RegattaNameAndRaceName regattaAndRaceIdentifier = new RegattaNameAndRaceName(raceIdentifier.getRegattaName(),
raceIdentifier.getRaceName());
DynamicTrackedRace trackedRace = getService().getTrackedRace(regattaAndRaceIdentifier);
getSecurityService().checkCurrentUserUpdatePermission(trackedRace);
trackedRace.setStartTimeReceived(
newStartTimeReceived == null ? null : new MillisecondsTimePoint(newStartTimeReceived));
return baseDomainFactory.createRaceDTO(getService(), false, regattaAndRaceIdentifier, trackedRace);
final DynamicTrackedRace trackedRace = getService().getTrackedRace(regattaAndRaceIdentifier);
if (trackedRace != null) {
getSecurityService().checkCurrentUserUpdatePermission(trackedRace);
trackedRace.setStartTimeReceived(
newStartTimeReceived == null ? null : new MillisecondsTimePoint(newStartTimeReceived));
}
return trackedRace == null ? null : baseDomainFactory.createRaceDTO(getService(), false, regattaAndRaceIdentifier, trackedRace);
}
@Override
@@ -2958,6 +2962,8 @@ public class SailingServiceWriteImpl extends SailingServiceImpl implements Saili
throw new ServiceException(serverStringMessages.get(locale, "slicingRaceColumnAlreadyUsedThe"));
}
final DynamicTrackedRace trackedRaceToSlice = getService().getTrackedRace(raceIdentifier);
// If tracked race isn't found, a NullPointerException will be thrown next, and that's okay because
// it's a bit unusual to not find a race that is just about to be sliced. We couldn't continue anyway.
final TimePoint startOfTrackingOfRaceToSlice = trackedRaceToSlice.getStartOfTracking();
final TimePoint endOfTrackingOfRaceToSlice = trackedRaceToSlice.getEndOfTracking();
if (sliceFrom == null || sliceTo == null || startOfTrackingOfRaceToSlice.after(sliceFrom)
@@ -2997,7 +3003,6 @@ public class SailingServiceWriteImpl extends SailingServiceImpl implements Saili
hasFinishingTime = false;
hasFinishedTime = false;
}
// Only wind fixes in the new tracking interval as well as the best fallback fixes are added to the new RaceLog
final LogEventTimeRangeWithFallbackFilter<RaceLogWindFixEvent> windFixEvents = new LogEventTimeRangeWithFallbackFilter<>(
timeRange);
@@ -41,6 +41,7 @@ numberOfMongoInstancesToLaunch=Počet instancí MongoDB, které se mají spustit
priority=Priorita
votes=Hlasy
instanceType=Typ instance
instanceName=Název instance
youHaveToProvideAPositiveNumberOfInstancesToLaunch=Zadali jste kladný počet instancí, které se mají spustit.
youHaveToProvideANonNegativePriority=Zadaná hodnota priority nesmí být záporná.
youHaveToProvideANonNegativeNumberOfVotes=Zadaný počet hlasů nesmí být záporný.
@@ -155,3 +156,13 @@ successfullyMovedAllProcessesAwayFromHost=Všechny procesy byly úspěšně pře
dnsNameAlreadyInUse=Název DNS se již používá
errorArchivingMongoDBTo=Chyba při archivaci MongoDB sady replik k sadě replik Mongo {0}: {1}
optionalSessionToken=Token relace (volitelný)
reverseProxies=Reverzní proxy servery
rotateHttpdLogs=Rotovat logy httpd
successfullyRotatedHttpdLogsOnInstance=Rotace logů Apache Httpd proběhla úspěšně u instance: {0}
invalidOperationForThisProxy=Tuto operaci nelze u této instance provést.
pleaseProvideNonEmptyNameAndAZ=Zadejte neprázdný název (UTF-8) a AZ.
success=úspěch
availabilityZone=Zóna dostupnosti
runOnExisting=Spustit na existující běžící instanci
publicIp=Veřejná IP adresa
privateIp=Soukromá IP adresa
@@ -41,6 +41,7 @@ numberOfMongoInstancesToLaunch=Antal MongoDB-instanser, der skal startes
priority=Prioritet
votes=Stemmer
instanceType=Instanstype
instanceName=Instansnavn
youHaveToProvideAPositiveNumberOfInstancesToLaunch=Du skal angive et positivt antal instanser, der skal startes.
youHaveToProvideANonNegativePriority=Du skal angive en ikke-negativ prioritet.
youHaveToProvideANonNegativeNumberOfVotes=Du skal angive et ikke-negativt antal stemmer.
@@ -155,3 +156,13 @@ successfullyMovedAllProcessesAwayFromHost=Alle processer blev flyttet væk fra v
dnsNameAlreadyInUse=DNS-navn er allerede i brug
errorArchivingMongoDBTo=Fejl ved arkivering af replikasæts MongoDB til Monogo-replikasættet {0}: {1}
optionalSessionToken=Sessionstoken (valgfrit)
reverseProxies=Omvendte proxyer
rotateHttpdLogs=Roter httpd-logge
successfullyRotatedHttpdLogsOnInstance=Roterede Apache-Httpd-logge på instans: {0}
invalidOperationForThisProxy=Du kan ikke udføre denne handling på denne instans
pleaseProvideNonEmptyNameAndAZ=Angiv et ikke-tomt navn (UTF-8) og et AZ.
success=uden fejl
availabilityZone=Tilgængelighedszone
runOnExisting=Kør på en eksisterende, igangværende instans
publicIp=Offentlig IP-adresse
privateIp=Privat IP-adresse
@@ -41,6 +41,7 @@ numberOfMongoInstancesToLaunch=Cantidad de instancias MongoDB para iniciar
priority=Prioridad
votes=Votos
instanceType=Tipo de instancia
instanceName=Nombre de la instancia
youHaveToProvideAPositiveNumberOfInstancesToLaunch=Debe indicar un número positivo de instancias para iniciar.
youHaveToProvideANonNegativePriority=Debe indicar una prioridad no negativa.
youHaveToProvideANonNegativeNumberOfVotes=Debe indicar un número no negativo de votos.
@@ -155,3 +156,13 @@ successfullyMovedAllProcessesAwayFromHost=Todos los procesos movidos correctamen
dnsNameAlreadyInUse=Nombre de DNS ya en uso
errorArchivingMongoDBTo=Error al archivar conjunto de réplica MongoDB en conjunto de réplica Mongo {0}: {1}
optionalSessionToken=Token de sesión (opcional)
reverseProxies=Anular proxies
rotateHttpdLogs=Rotar logs httpd
successfullyRotatedHttpdLogsOnInstance=Se han rotado correctamente los logs de Apache httpd en la instancia: {0}
invalidOperationForThisProxy=No se puede realizar esta operación en esta instancia
pleaseProvideNonEmptyNameAndAZ=Introduzca un nombre no vacío (UTF-8) y una zona de disponibilidad.
success=correcto
availabilityZone=Zona de disponibilidad
runOnExisting=Ejecutar en una instancia existente y en funcionamiento
publicIp=Dirección IP pública
privateIp=Dirección IP privada
@@ -41,6 +41,7 @@ numberOfMongoInstancesToLaunch=Nombre d''instances MongoDB à lancer
priority=Priorité
votes=Votes
instanceType=Type d''instance
instanceName=Nom de l''instance
youHaveToProvideAPositiveNumberOfInstancesToLaunch=Vous devez indiquer un nombre positif d''instances à lancer.
youHaveToProvideANonNegativePriority=Vous devez indiquer une priorité non négative.
youHaveToProvideANonNegativeNumberOfVotes=Vous devez indiquer une nombre de votes non négatif.
@@ -155,3 +156,13 @@ successfullyMovedAllProcessesAwayFromHost=Processus correctement déplacés depu
dnsNameAlreadyInUse=Nom DNS déjà utilisé
errorArchivingMongoDBTo=Erreur lors de l''archivage de l''ensemble de réplicas MongoDB dans l''ensemble de réplicas Mongo {0} : {1}
optionalSessionToken=Jeton de session (facultatif)
reverseProxies=Proxies inverses
rotateHttpdLogs=Effectuer la rotation des journaux httpd
successfullyRotatedHttpdLogsOnInstance=Rotation des journaux httpd Apache correctement effectuée sur l''instance : {0}
invalidOperationForThisProxy=Vous ne pouvez pas effectuer cette opération sur cette instance.
pleaseProvideNonEmptyNameAndAZ=Veuillez fournir un nom (UTF-8) et une zone de disponibilité.
success=réussite
availabilityZone=Zone de disponibilité
runOnExisting=Exécuter sur une instance existante et en cours d''exécution
publicIp=Adresse IP publique
privateIp=Adresse IP privée
@@ -41,6 +41,7 @@ numberOfMongoInstancesToLaunch=Numero di istanze MongoDB da avviare
priority=Priorità
votes=Voti
instanceType=Tipo di istanza
instanceName=Nome istanza
youHaveToProvideAPositiveNumberOfInstancesToLaunch=È necessario fornire un numero positivo di istanze da avviare
youHaveToProvideANonNegativePriority=È necessario fornire una priorità non negativa
youHaveToProvideANonNegativeNumberOfVotes=È necessario fornire un numero di voti non negativo
@@ -155,3 +156,13 @@ successfullyMovedAllProcessesAwayFromHost=Spostamento di tutti i processi dall''
dnsNameAlreadyInUse=Nome DNS già in uso
errorArchivingMongoDBTo=Errore di archiviazione del MongoDB del set di replica nel set di replica Mongo {0}: {1}
optionalSessionToken=Token di sessione (opzionale)
reverseProxies=Proxy inversi
rotateHttpdLogs=Ruota i registri httpd
successfullyRotatedHttpdLogsOnInstance=I registri Httpd Apache sono stati ruotati correttamente nell''istanza: {0}
invalidOperationForThisProxy=Non è possibile eseguire questa operazione in questa istanza
pleaseProvideNonEmptyNameAndAZ=Fornire un nome non vuoto (UTF-8) e un AZ.
success=esito positivo
availabilityZone=Zona di disponibilità
runOnExisting=Esegui in un''istanza esistente in esecuzione
publicIp=Indirizzo IP pubblico
privateIp=Indirizzo IP privato
@@ -41,6 +41,7 @@ numberOfMongoInstancesToLaunch=開始する MongoDB インスタンスの数
priority=priority
votes=votes
instanceType=インスタンスタイプ
instanceName=インスタンス名
youHaveToProvideAPositiveNumberOfInstancesToLaunch=開始するインスタンスはプラスの数で指定する必要があります。
youHaveToProvideANonNegativePriority=マイナスではない priority を指定する必要があります。
youHaveToProvideANonNegativeNumberOfVotes=マイナスでない数の votes を指定する必要があります。
@@ -155,3 +156,13 @@ successfullyMovedAllProcessesAwayFromHost=すべてのプロセスがホスト {
dnsNameAlreadyInUse=DNS 名はすでに使用中です
errorArchivingMongoDBTo=複製セットの MongoDB を Mongo 複製セット {0} にアーカイブする際にエラーが発生: {1}
optionalSessionToken=セッショントークン (オプション)
reverseProxies=リバースプロキシ
rotateHttpdLogs=httpd ログのローテーション
successfullyRotatedHttpdLogsOnInstance=インスタンスでの Apache httpd ログのローテーションに成功しました: {0}
invalidOperationForThisProxy=この処理はこのインスタンスでは実行できません
pleaseProvideNonEmptyNameAndAZ=空でない名前 (UTF-8) と A~Z の 1 文字を指定してください。
success=成功
availabilityZone=可用性ゾーン
runOnExisting=既存の起動済みインスタンスで実行
publicIp=パブリック IP アドレス
privateIp=プライベート IP アドレス
@@ -41,6 +41,7 @@ numberOfMongoInstancesToLaunch=Número de instâncias MongoDB para iniciar
priority=Prioridade
votes=Votos
instanceType=Tipo de instância
instanceName=Nome da instância
youHaveToProvideAPositiveNumberOfInstancesToLaunch=Você tem que fornecer um número positivo de instâncias para iniciar.
youHaveToProvideANonNegativePriority=Você tem que fornecer uma prioridade não negativa.
youHaveToProvideANonNegativeNumberOfVotes=Você tem que fornecer um número não negativo de votos.
@@ -155,3 +156,13 @@ successfullyMovedAllProcessesAwayFromHost=Todos os processos movidos com êxito
dnsNameAlreadyInUse=Nome de DNS já em utilização
errorArchivingMongoDBTo=Erro ao arquivar MongoDB de conjunto de réplicas para o conjunto de réplicas Mongo {0}: {1}
optionalSessionToken=Token de sessão (opcional)
reverseProxies=Estornar proxies
rotateHttpdLogs=Rotacionar logs httpd
successfullyRotatedHttpdLogsOnInstance=Logs Httpd Apache rotacionados com êxito na instância: {0}
invalidOperationForThisProxy=Você não pode efetuar esta operação nesta instância
pleaseProvideNonEmptyNameAndAZ=Forneça um nome que não esteja em branco (UTF-8) e um AZ.
success=êxito
availabilityZone=Zona de disponibilidade
runOnExisting=Executar em uma instância em execução existente
publicIp=Endereço IP público
privateIp=Endereço IP privado
@@ -41,6 +41,7 @@ numberOfMongoInstancesToLaunch=Число инстанций MongoDB для за
priority=Приоритет
votes=Голоса
instanceType=Тип инстанции
instanceName=Имя инстанции
youHaveToProvideAPositiveNumberOfInstancesToLaunch=Введите положительное число инстанций для запуска.
youHaveToProvideANonNegativePriority=Введите неотрицательный приоритет.
youHaveToProvideANonNegativeNumberOfVotes=Введите неотрицательное число голосов.
@@ -155,3 +156,13 @@ successfullyMovedAllProcessesAwayFromHost=Все процессы успешно
dnsNameAlreadyInUse=Имя DNS уже используется
errorArchivingMongoDBTo=Ошибка при архивации MongoDB набора реплик в набор реплик Mongo {0}: {1}
optionalSessionToken=Маркер сеанса (опциональный)
reverseProxies=Обратные прокси
rotateHttpdLogs=Ротировать журналы httpd
successfullyRotatedHttpdLogsOnInstance=Журналы Httpd Apache успешно ротированы в инстанции: {0}
invalidOperationForThisProxy=Вы не можете выполнить эту операцию в данной инстанции
pleaseProvideNonEmptyNameAndAZ=Укажите непустое имя (UTF-8) и AZ.
success=успешно
availabilityZone=Зона доступности
runOnExisting=Выполнить в существующей запущенной инстанции
publicIp=Общедоступный IP-адрес
privateIp=Частный IP-адрес
@@ -41,6 +41,7 @@ numberOfMongoInstancesToLaunch=Število instanc podatkovne baze MongoDB za zagon
priority=Prioriteta
votes=Glasovi
instanceType=Vrsta instance
instanceName=Ime instance
youHaveToProvideAPositiveNumberOfInstancesToLaunch=Za zagon morate navesti pozitivno število instanc.
youHaveToProvideANonNegativePriority=Navesti morate nenegativno prioriteto.
youHaveToProvideANonNegativeNumberOfVotes=Navesti morate nenegativno število glasov.
@@ -155,3 +156,13 @@ successfullyMovedAllProcessesAwayFromHost=Vsi procesi uspešno premaknjeni iz go
dnsNameAlreadyInUse=Ime DNS je že v uporabi
errorArchivingMongoDBTo=Napaka pri arhiviranju MongoDB niza replik v niz replik Mongo {0}: {1}
optionalSessionToken=Žeton seje (izbirno)
reverseProxies=Razveljavitev posredniških strežnikov
rotateHttpdLogs=Zasuk zapisnikov httpd
successfullyRotatedHttpdLogsOnInstance=Uspešno zasukani zapisniki Apache Httpd na instanci: {0}
invalidOperationForThisProxy=Tega postopka ni mogoče izvajati na tej instanci
pleaseProvideNonEmptyNameAndAZ=Navedite ima, ki ni prazno (UTF-8), in AZ.
success=uspeh
availabilityZone=Območje razpoložljivosti
runOnExisting=Izvajanje na obstoječi, izvajajoči se instanci
publicIp=Javni IP-naslov
privateIp=Zasebni IP-naslov
@@ -41,6 +41,7 @@ numberOfMongoInstancesToLaunch=启动的 MongoDB 实例的数量
priority=优先级
votes=投票
instanceType=实例类型
instanceName=实例名称
youHaveToProvideAPositiveNumberOfInstancesToLaunch=对于要启动的实例,您必须提供为正的数量。
youHaveToProvideANonNegativePriority=您必须提供非负优先级。
youHaveToProvideANonNegativeNumberOfVotes=您必须提供非负票数。
@@ -155,3 +156,13 @@ successfullyMovedAllProcessesAwayFromHost=已成功将所有流程移离主机 {
dnsNameAlreadyInUse=DNS 名称已在使用中
errorArchivingMongoDBTo=归档复本集的 MongoDB 到 Mongo 复本集 {0} 出错:{1}
optionalSessionToken=会话令牌(可选)
reverseProxies=反向代理
rotateHttpdLogs=轮换 httpd 日志
successfullyRotatedHttpdLogsOnInstance=已成功轮换实例上的 Apache Httpd 日志:{0}
invalidOperationForThisProxy=无法在该实例上执行此操作
pleaseProvideNonEmptyNameAndAZ=请提供非空名称 (UTF-8) 和 AZ。
success=成功
availabilityZone=可用区
runOnExisting=在现有的运行中实例上运行
publicIp=公共 IP 地址
privateIp=专用 IP 地址
@@ -10,7 +10,6 @@ import org.osgi.util.tracker.ServiceTracker;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tractracadapter.TracTracAdapterFactory;
import com.sap.sailing.server.interfaces.RacingEventService;
import com.sap.sse.InvalidDateException;
@@ -135,16 +134,6 @@ public abstract class SailingServerHttpServlet extends HttpServlet {
return timePoint;
}
protected TrackedRace getTrackedRace(HttpServletRequest req) {
Regatta regatta = getRegatta(req);
RaceDefinition race = getRaceDefinition(req);
TrackedRace trackedRace = null;
if (regatta != null && race != null) {
trackedRace = getService().getOrCreateTrackedRegatta(regatta).getTrackedRace(race);
}
return trackedRace;
}
protected TypeBasedServiceFinderFactory getServiceFinderFactory() {
return serviceFinderFactory;
}
@@ -1429,7 +1429,7 @@ public class RegattasResource extends AbstractSailingServerResource {
if (race == null) {
response = getBadRaceErrorResponse(regattaName, raceName);
} else {
DynamicTrackedRace trackedRace = getService().getTrackedRace(regatta, race);
final DynamicTrackedRace trackedRace = getService().getTrackedRace(regatta, race);
if (trackedRace != null) {
TargetTimeInfo targetTime;
try {
@@ -107,7 +107,10 @@ public class WindResource extends AbstractSailingServerResource {
RegattaNameAndRaceName identifier = new RegattaNameAndRaceName(regattaName, raceName);
// add wind only to those races the subject is permitted to update
if (getSecurityService().hasCurrentUserUpdatePermission(identifier)) {
result.add(new RaceIdentifierAndTrackedRace(identifier, getService().getTrackedRace(identifier)));
final DynamicTrackedRace trackedRace = getService().getTrackedRace(identifier);
if (trackedRace != null) {
result.add(new RaceIdentifierAndTrackedRace(identifier, trackedRace));
}
}
}
} else {
@@ -365,31 +365,34 @@ public class ExpeditionAllInOneImporter {
securityService.checkCurrentUserExplicitPermissions(service.getRegatta(new RegattaName(regattaNameAndleaderboardName)), DefaultActions.UPDATE);
securityService.checkCurrentUserExplicitPermissions(service.getLeaderboardByName(regattaNameAndleaderboardName), DefaultActions.UPDATE);
if (importMode == ImportMode.NEW_COMPETITOR) {
securityService.checkCurrentUserExplicitPermissions(service.getTrackedRace(new RegattaNameAndRaceName(regattaNameAndleaderboardName, trackedRaceName)), DefaultActions.UPDATE);
final TimePointsOfFirstAndLastFix firstAndLastFixAt = importFixes(filenameWithSuffix, fileItem, jsonHolderForGpsFixImport, jsonHolderForSensorFixImport, errors);
ensureEventLongEnough(firstAndLastFixAt.getFirstFixAt(), firstAndLastFixAt.getLastFixAt(), eventId);
final Iterable<RaceColumn> raceColumns = regattaLeaderboard.getRaceColumns();
if (Util.isEmpty(raceColumns)) {
return new ImporterResult(serverStringMessages.get(uiLocale, "allInOneErrorInvalidRace"));
}
try {
for (RaceColumn raceColumn : raceColumns) {
final Iterable<? extends Fleet> fleets = raceColumn.getFleets();
if (Util.size(fleets) != 1) {
return new ImporterResult(serverStringMessages.get(uiLocale, "allInOneErrorSplitFleetNotSupported"));
final DynamicTrackedRace trackedRace = service.getTrackedRace(new RegattaNameAndRaceName(regattaNameAndleaderboardName, trackedRaceName));
if (trackedRace != null) {
securityService.checkCurrentUserExplicitPermissions(trackedRace, DefaultActions.UPDATE);
final TimePointsOfFirstAndLastFix firstAndLastFixAt = importFixes(filenameWithSuffix, fileItem, jsonHolderForGpsFixImport, jsonHolderForSensorFixImport, errors);
ensureEventLongEnough(firstAndLastFixAt.getFirstFixAt(), firstAndLastFixAt.getLastFixAt(), eventId);
final Iterable<RaceColumn> raceColumns = regattaLeaderboard.getRaceColumns();
if (Util.isEmpty(raceColumns)) {
return new ImporterResult(serverStringMessages.get(uiLocale, "allInOneErrorInvalidRace"));
}
try {
for (RaceColumn raceColumn : raceColumns) {
final Iterable<? extends Fleet> fleets = raceColumn.getFleets();
if (Util.size(fleets) != 1) {
return new ImporterResult(serverStringMessages.get(uiLocale, "allInOneErrorSplitFleetNotSupported"));
}
final Fleet fleet = fleets.iterator().next();
DynamicTrackedRace trackedRaceForColumn = (DynamicTrackedRace) raceColumn.getTrackedRace(fleet);
if (trackedRaceForColumn == null) {
trackedRaceForColumn = trackRace(regattaLeaderboard, raceColumn, fleet);
}
trackedRaces.add(trackedRaceForColumn);
raceNameRaceColumnNameFleetnameList
.add(new Triple<>(trackedRaceForColumn.getRaceIdentifier().getRaceName(),
raceColumn.getName(), fleet.getName()));
}
final Fleet fleet = fleets.iterator().next();
DynamicTrackedRace trackedRaceForColumn = (DynamicTrackedRace) raceColumn.getTrackedRace(fleet);
if (trackedRaceForColumn == null) {
trackedRaceForColumn = trackRace(regattaLeaderboard, raceColumn, fleet);
}
trackedRaces.add(trackedRaceForColumn);
raceNameRaceColumnNameFleetnameList
.add(new Triple<>(trackedRaceForColumn.getRaceIdentifier().getRaceName(),
raceColumn.getName(), fleet.getName()));
} catch (Exception e) {
throw new AllInOneImportException(e, errors);
}
} catch (Exception e) {
throw new AllInOneImportException(e, errors);
}
} else if (importMode == ImportMode.NEW_RACE) {
// When uploading files with identical name, the second RaceColumn will be named with the upload time in its name
@@ -28,32 +28,34 @@ public class TrackFilesExportPostServlet extends SailingServerHttpServlet {
private static final Logger log = Logger.getLogger(TrackFilesExportPostServlet.class.toString());
private TrackedRace getTrackedRace(String regattaString, String raceString) {
Regatta regatta = getService().getRegattaByName(regattaString);
if (regatta == null)
return null;
RaceDefinition race = regatta.getRaceByName(raceString);
if (race == null)
return null;
return getService().getTrackedRace(regatta, race);
final TrackedRace result;
final Regatta regatta = getService().getRegattaByName(regattaString);
if (regatta == null) {
result = null;
} else {
final RaceDefinition race = regatta.getRaceByName(raceString);
if (race == null) {
result = null;
} else {
result = getService().getTrackedRace(regatta, race);
}
}
return result;
}
private List<TrackedRace> getTrackedRaces(String[] regattaRaces) {
List<TrackedRace> races = new ArrayList<TrackedRace>();
final List<TrackedRace> races = new ArrayList<TrackedRace>();
for (String regattaRace : regattaRaces) {
String[] split = regattaRace.split(":");
if (split.length == 0)
continue;
String regattaString = split[0];
String raceString = split[1];
TrackedRace trackedRace = getTrackedRace(regattaString, raceString);
if (trackedRace == null)
continue;
races.add(trackedRace);
final String[] split = regattaRace.split(":");
if (split.length != 0) {
final String regattaString = split[0];
final String raceString = split[1];
final TrackedRace trackedRace = getTrackedRace(regattaString, raceString);
if (trackedRace != null) {
races.add(trackedRace);
}
}
}
return races;
}
@@ -144,17 +144,17 @@ public abstract class AbstractWindImporter {
if (uploadRequest.races.size() > 0) {
for (RegattaAndRaceIdentifier raceEntry : uploadRequest.races) {
DynamicTrackedRace trackedRace = service.getTrackedRace(raceEntry);
SecurityUtils.getSubject()
.checkPermission(trackedRace.getIdentifier().getStringPermission(DefaultActions.UPDATE));
if (trackedRace != null) {
SecurityUtils.getSubject()
.checkPermission(trackedRace.getIdentifier().getStringPermission(DefaultActions.UPDATE));
trackedRaces.add(trackedRace);
}
}
} else {
for (Regatta regatta : service.getAllRegattas()) {
for (RaceDefinition raceDefinition : regatta.getAllRaces()) {
final DynamicTrackedRace trackedRace = service.getTrackedRegatta(regatta).getTrackedRace(raceDefinition);
if (SecurityUtils.getSubject()
final DynamicTrackedRace trackedRace = service.getTrackedRegatta(regatta).getExistingTrackedRace(raceDefinition);
if (trackedRace != null && SecurityUtils.getSubject()
.isPermitted(trackedRace.getIdentifier().getStringPermission(DefaultActions.UPDATE))) {
trackedRaces.add(trackedRace);
}
@@ -161,11 +161,18 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch
@Override
RaceDefinition getRace(RegattaAndRaceIdentifier raceIdentifier);
/**
* Looks for a {@link DynamicTrackedRace} inside the {@link TrackedRegatta} identified by {@code regatta},
* keyed by the race definition {@code race}. If no such race is found, e.g., because it is still in its "loading"
* phase and the {@link TrackedRace} hasn't been created and added to the {@link TrackedRegatta} yet, or because
* it is in the process of being removed, with the {@link TrackedRace} already gone but the {@link RaceDefinition}
* still available (see also bug 5982), this method will return {@code null} without any waiting or blocking.
*/
DynamicTrackedRace getTrackedRace(Regatta regatta, RaceDefinition race);
/**
* When the regatta, tracked regatta and race definition are found, this method waits for the
* tracked race to appear.
* This method does not for the tracked race to appear. If any of regatta, race definition or tracked race are not
* found, {@code null} is returned.
*/
DynamicTrackedRace getTrackedRace(RegattaAndRaceIdentifier raceIdentifier);
@@ -475,6 +475,8 @@ public class ImportMasterDataOperation extends
DynamicTrackedRace trackedRace = toState
.getTrackedRace((RegattaAndRaceIdentifier) raceIdentifier);
raceColumn.setTrackedRace(fleet, trackedRace);
// in case the TrackedRace wasn't found (see also bug 5982), at least record the race identifier
raceColumn.setRaceIdentifier(fleet, raceIdentifier);
}
}
}
@@ -708,7 +710,7 @@ public class ImportMasterDataOperation extends
RaceHandle raceHandle = toState.addRace(/* default */ null, paramToStartTracking, /* do not wait */ -1);
final RaceDefinition race = raceHandle.getRace(RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS);
if (race != null) {
final DynamicTrackedRace trackedRace = raceHandle.getTrackedRegatta().getTrackedRace(race);
final DynamicTrackedRace trackedRace = raceHandle.getTrackedRegatta().getTrackedRace(race); // wait/block for tracked race to show up
result.add(trackedRace);
ensureOwnership(trackedRace.getIdentifier(), securityService);
creationCount.addOneTrackedRace(race.getId().toString());
@@ -3054,7 +3054,7 @@ Replicator {
* already have been removed by the stopAllTrackersForWhichRaceIsLastReachable(...) above; maybe a
* Java memory model idiosyncrasy with the queue thread not "seeing" the change to dataTrackers?).
* We must make sure to not block its execution, e.g., by synchronization, because otherwise
* the the removedTrackedRegatta(regatta) call below may not return, either, as it waits for
* the removedTrackedRegatta(regatta) call below may not return, either, as it waits for
* tasks it may enqueue after the task enqueued here. See bug 5879.
*/
trackedRegatta.removeTrackedRace(trackedRace, Optional.of(
@@ -3104,6 +3104,9 @@ Replicator {
}
}
}
// FIXME bug5982: when the TrackedRace is removed already, queries for the RaceDefinition would still be satisfied up to this point, with a RaceDefinition that is about to disappear;
// FIXME bug5982: no TrackedRace will ever appear again for that RaceDefinition; we should consider introducing a locking pattern around the transaction of removing TrackedRace+RaceDefinition
// FIXME bug5982: and synchronize accordingly with TrackedRegattaImpl.getTrackedRace(RaceDefinition).
// remove the race from the (default) regatta if the regatta is not persistently stored
regatta.removeRace(race);
if (!regatta.isPersistent() && Util.isEmpty(regatta.getAllRaces())) {
@@ -3170,7 +3173,7 @@ Replicator {
@Override
public DynamicTrackedRace getTrackedRace(Regatta regatta, RaceDefinition race) {
return getOrCreateTrackedRegatta(regatta).getTrackedRace(race);
return getOrCreateTrackedRegatta(regatta).getExistingTrackedRace(race);
}
private DynamicTrackedRace getExistingTrackedRace(Regatta regatta, RaceDefinition race) {
@@ -3234,9 +3237,9 @@ Replicator {
if (regatta != null) {
DynamicTrackedRegatta trackedRegatta = regattaTrackingCache.get(regatta);
if (trackedRegatta != null) {
RaceDefinition race = getRace(raceIdentifier);
RaceDefinition race = regatta.getRaceByName(raceIdentifier.getRaceName());
if (race != null) {
result = trackedRegatta.getTrackedRace(race);
result = trackedRegatta.getExistingTrackedRace(race);
}
}
}
@@ -404,7 +404,7 @@ public class SimulationServiceImpl implements SimulationService {
});
}
if (!legListeners.containsKey(legIdentifier.getRaceIdentifier())) {
TrackedRace trackedRace = racingEventService.getTrackedRace(legIdentifier);
final TrackedRace trackedRace = racingEventService.getTrackedRace(legIdentifier);
if (trackedRace != null) {
LegChangeListener listener = new LegChangeListener(trackedRace);
legListeners.put(legIdentifier.getRaceIdentifier(), listener);