diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTracker.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTracker.java index 983f46386e1..1b4294b5070 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTracker.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTracker.java @@ -42,7 +42,11 @@ public interface RaceTracker { /** * Stops tracking the races. - * @param preemptive TODO + * + * @param preemptive + * if false, the tracker will continue to process data already received but will stop + * receiving new data. If true, the tracker will stop processing data immediately, ignoring + * (dropping) all data already received but not yet processed. */ void stop(boolean preemptive) throws MalformedURLException, IOException, InterruptedException; diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java index c003915d2eb..2474c6c2297 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java @@ -260,7 +260,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl * these race states can be garbage-collected when the race log is no longer attached. The race states are created * lazily, synchronizing on this weak hash map. */ - protected final transient WeakHashMap raceStates; + protected transient WeakHashMap raceStates; protected transient ConcurrentHashMap attachedRegattaLogs; @@ -497,6 +497,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl */ private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException, PatchFailedException { ois.defaultReadObject(); + raceStates = new WeakHashMap<>(); attachedRaceLogs = new ConcurrentHashMap<>(); markPassingsTimes = new ArrayList>>(); // The short time wind cache needs to be there before operations such as maneuver recalculation try to access it @@ -3158,26 +3159,30 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl if (!legs.isEmpty()) { if (allMarksHavePositions && numberOfMarks == 2) { final TrackedLeg legDeterminingDirection = getLegDeterminingDirectionInWhichToPassWaypoint(waypoint); - Distance crossTrackErrorOfMark0OnLineFromMark1ToNextWaypoint = markPositions.get(0) - .crossTrackError(markPositions.get(1), legDeterminingDirection.getLegBearing(timePoint)); - final Position portMarkPositionWhileApproachingLine; - final Position starboardMarkPositionWhileApproachingLine; - final Mark starboardMarkWhileApproachingLine; - final Mark portMarkWhileApproachingLine; - if (crossTrackErrorOfMark0OnLineFromMark1ToNextWaypoint.getMeters() < 0) { - portMarkWhileApproachingLine = Util.get(waypoint.getMarks(), 0); - portMarkPositionWhileApproachingLine = markPositions.get(0); - starboardMarkWhileApproachingLine = Util.get(waypoint.getMarks(), 1); - starboardMarkPositionWhileApproachingLine = markPositions.get(1); + if (legDeterminingDirection == null) { + result = null; } else { - portMarkWhileApproachingLine = Util.get(waypoint.getMarks(), 1); - portMarkPositionWhileApproachingLine = markPositions.get(1); - starboardMarkWhileApproachingLine = Util.get(waypoint.getMarks(), 0); - starboardMarkPositionWhileApproachingLine = markPositions.get(0); + Distance crossTrackErrorOfMark0OnLineFromMark1ToNextWaypoint = markPositions.get(0) + .crossTrackError(markPositions.get(1), legDeterminingDirection.getLegBearing(timePoint)); + final Position portMarkPositionWhileApproachingLine; + final Position starboardMarkPositionWhileApproachingLine; + final Mark starboardMarkWhileApproachingLine; + final Mark portMarkWhileApproachingLine; + if (crossTrackErrorOfMark0OnLineFromMark1ToNextWaypoint.getMeters() < 0) { + portMarkWhileApproachingLine = Util.get(waypoint.getMarks(), 0); + portMarkPositionWhileApproachingLine = markPositions.get(0); + starboardMarkWhileApproachingLine = Util.get(waypoint.getMarks(), 1); + starboardMarkPositionWhileApproachingLine = markPositions.get(1); + } else { + portMarkWhileApproachingLine = Util.get(waypoint.getMarks(), 1); + portMarkPositionWhileApproachingLine = markPositions.get(1); + starboardMarkWhileApproachingLine = Util.get(waypoint.getMarks(), 0); + starboardMarkPositionWhileApproachingLine = markPositions.get(0); + } + result = new LineMarksWithPositions(portMarkPositionWhileApproachingLine, + starboardMarkPositionWhileApproachingLine, starboardMarkWhileApproachingLine, + portMarkWhileApproachingLine); } - result = new LineMarksWithPositions(portMarkPositionWhileApproachingLine, - starboardMarkPositionWhileApproachingLine, starboardMarkWhileApproachingLine, - portMarkWhileApproachingLine); } else { result = null; // either the position(s) or one or more marks is/are unknown, or the waypoint is not a two-mark waypoint } diff --git a/java/com.sap.sailing.feature/feature.xml b/java/com.sap.sailing.feature/feature.xml index 94b606ebe74..2aad440fdba 100755 --- a/java/com.sap.sailing.feature/feature.xml +++ b/java/com.sap.sailing.feature/feature.xml @@ -1,454 +1,447 @@ - - - - + + + + The SAP Sailing Server. Provides connectivity to a TracTrac live GPS feed as well as a live feed from an Expedition system. -Computes leaderboard information for sailing races and offers RESTful APIs to all of this. - - - - [Enter Copyright Description here.] - - - - [Enter License Description here.] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Computes leaderboard information for sailing races and offers RESTful APIs to all of this. + + + + [Enter Copyright Description here.] + + + + [Enter License Description here.] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java index 9dadbd5328d..98df542aab3 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java @@ -140,7 +140,7 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS WindInfoForRaceDTO getAveragedWindInfo(RegattaAndRaceIdentifier raceIdentifier, Date from, long millisecondsStepWidth, int numberOfFixes, double latDeg, double lngDeg, Collection windSources) throws NoWindException; - Boolean getPolarResults(RegattaAndRaceIdentifier raceIdentifier); + boolean getPolarResults(RegattaAndRaceIdentifier raceIdentifier); SimulatorResultsDTO getSimulatorResults(RegattaAndRaceIdentifier raceIdentifier, Date from, Date prevStartTime); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 1b23ce39c06..0f886bb2f75 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -1423,13 +1423,19 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } @Override - public Boolean getPolarResults(RegattaAndRaceIdentifier raceIdentifier) { + public boolean getPolarResults(RegattaAndRaceIdentifier raceIdentifier) { + final boolean result; TrackedRace trackedRace = getExistingTrackedRace(raceIdentifier); - BoatClass boatClass = trackedRace.getRace().getBoatClass(); - PolarDataService polarData = getService().getPolarDataService(); - PolarDiagram polarDiagram = new PolarDiagramGPS(boatClass, polarData); - return new Boolean(polarDiagram != null); - }; + if (trackedRace == null) { + result = false; + } else { + BoatClass boatClass = trackedRace.getRace().getBoatClass(); + PolarDataService polarData = getService().getPolarDataService(); + PolarDiagram polarDiagram = new PolarDiagramGPS(boatClass, polarData); + result = polarDiagram != null; + } + return result; + } @Override public SimulatorResultsDTO getSimulatorResults(RegattaAndRaceIdentifier raceIdentifier, Date from, Date prevStartTime) { diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java index a2b082373bd..d7c330e03df 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java @@ -190,7 +190,7 @@ public class InitialLoadReplicationObjectIdentityTest extends AbstractServerRepl replicationDescriptorPair.getA().initialLoad(); replicator.setSuspended(false); synchronized (replicator) { - while (!replicator.isQueueEmpty()) { + while (!replicator.isQueueEmptyOrStopped()) { replicator.wait(); } } diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/PrematureOperationReceiptTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/PrematureOperationReceiptTest.java index 18d90dc0e82..5b34ef7f79a 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/PrematureOperationReceiptTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/PrematureOperationReceiptTest.java @@ -60,7 +60,7 @@ public class PrematureOperationReceiptTest extends AbstractServerReplicationTest } replicator.setSuspended(false); synchronized (replicator) { - while (!replicator.isQueueEmpty()) { + while (!replicator.isQueueEmptyOrStopped()) { replicator.wait(); } } diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java index f5b3eb811e8..970b2f27d40 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java @@ -129,6 +129,7 @@ public class TrackRaceReplicationTest extends AbstractServerReplicationTest { @Test public void testReassignmentToLeaderboardReplication() throws Exception { + logger.info("Running testReassignmentToLeaderboardReplication()"); final String leaderboardName = "Test Leaderboard"; Leaderboard masterLeaderboard = master.apply(new CreateFlexibleLeaderboard(leaderboardName, null, new int[0], new LowPoint(), null)); final String columnName = "R1"; @@ -153,10 +154,12 @@ public class TrackRaceReplicationTest extends AbstractServerReplicationTest { RaceColumn column = replicaLeaderboard.getRaceColumnByName(columnName); assertNotNull(column); assertSame(replicaTrackedRace, column.getTrackedRace(defaultFleet)); + logger.info("Done running testReassignmentToLeaderboardReplication()"); } @Test public void testRaceTimeReplication() throws InterruptedException, Exception { + logger.info("Running testRaceTimeReplication()"); startTracking(); // now wait at least until the start of tracking time has been received on the master copy boolean receivedStartAndEndOfTracking = master.getTrackedRace(raceIdentifier).getStartOfTracking() != null && @@ -176,21 +179,27 @@ public class TrackRaceReplicationTest extends AbstractServerReplicationTest { ((DynamicTrackedRace) masterTrackedRace).setStartTimeReceived(now); Thread.sleep(1000); assertEquals(now, replicaTrackedRace.getStartOfRace()); + logger.info("Done running testRaceTimeReplication()"); } @Test public void testWindSourcesToExcludeReplication() throws InterruptedException, Exception { - startTracking(); - Thread.sleep(1000); - TrackedRace replicaTrackedRace = replica.getTrackedRace(raceIdentifier); - assertTrue(Util.isEmpty(masterTrackedRace.getWindSourcesToExclude())); - assertTrue(Util.isEmpty(replicaTrackedRace.getWindSourcesToExclude())); - masterTrackedRace.setWindSourcesToExclude(Collections.singleton(new WindSourceImpl(WindSourceType.WEB))); - assertEquals(1, Util.size(masterTrackedRace.getWindSourcesToExclude())); - assertEquals(WindSourceType.WEB, masterTrackedRace.getWindSourcesToExclude().iterator().next().getType()); - Thread.sleep(1000); - assertEquals(1, Util.size(replicaTrackedRace.getWindSourcesToExclude())); - assertEquals(WindSourceType.WEB, replicaTrackedRace.getWindSourcesToExclude().iterator().next().getType()); + try { + logger.info("Running testWindSourcesToExcludeReplication()"); + startTracking(); + Thread.sleep(1000); + TrackedRace replicaTrackedRace = replica.getTrackedRace(raceIdentifier); + assertTrue(Util.isEmpty(masterTrackedRace.getWindSourcesToExclude())); + assertTrue(Util.isEmpty(replicaTrackedRace.getWindSourcesToExclude())); + masterTrackedRace.setWindSourcesToExclude(Collections.singleton(new WindSourceImpl(WindSourceType.WEB))); + assertEquals(1, Util.size(masterTrackedRace.getWindSourcesToExclude())); + assertEquals(WindSourceType.WEB, masterTrackedRace.getWindSourcesToExclude().iterator().next().getType()); + Thread.sleep(1000); + assertEquals(1, Util.size(replicaTrackedRace.getWindSourcesToExclude())); + assertEquals(WindSourceType.WEB, replicaTrackedRace.getWindSourcesToExclude().iterator().next().getType()); + } finally { + logger.info("Done running testWindSourcesToExcludeReplication()"); + } } @After diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index 7f343217b74..3e31806aea2 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -379,6 +379,8 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes .withInitial(() -> false); private final Set masterDataClassLoaders = new HashSet(); + + private final JoinedClassLoader joinedClassLoader; /** * Constructs a {@link DomainFactory base domain factory} that uses this object's {@link #competitorStore competitor @@ -457,6 +459,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes TypeBasedServiceFinderFactory serviceFinderFactory) { logger.info("Created " + this); this.masterDataClassLoaders.add(this.getClass().getClassLoader()); + joinedClassLoader = new JoinedClassLoader(masterDataClassLoaders); this.operationsSentToMasterForReplication = new HashSet<>(); if (windStore == null) { try { @@ -2291,7 +2294,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes @Override public ClassLoader getDeserializationClassLoader() { - return getCombinedMasterDataClassLoader(); + return joinedClassLoader; } @Override diff --git a/java/com.sap.sse.replication.testsupport/src/com/sap/sse/replication/testsupport/AbstractServerReplicationTestSetUp.java b/java/com.sap.sse.replication.testsupport/src/com/sap/sse/replication/testsupport/AbstractServerReplicationTestSetUp.java index 935a0058734..137a383d16d 100644 --- a/java/com.sap.sse.replication.testsupport/src/com/sap/sse/replication/testsupport/AbstractServerReplicationTestSetUp.java +++ b/java/com.sap.sse.replication.testsupport/src/com/sap/sse/replication/testsupport/AbstractServerReplicationTestSetUp.java @@ -179,6 +179,16 @@ public abstract class AbstractServerReplicationTestSetUp replicable = replicableProvider.getReplicable(replicableIdAsString, /* wait */ false); if (replicable != null) { - Thread.currentThread().setContextClassLoader(replicable.getClass().getClassLoader()); ObjectInputStream ois = new ObjectInputStream(uncompressedInputStream); // no special stream required; only reading a generic byte[] int operationsInMessage = 0; try { @@ -248,11 +251,13 @@ public class ReplicationReceiver implements Runnable { } catch (Exception e) { logger.info("Exception while processing replica: "+e.getMessage()); logger.log(Level.SEVERE, "run", e); - } finally { - Thread.currentThread().setContextClassLoader(oldClassLoader); } } logger.info("Stopped replicator thread. This server will no longer receive events from a master."); + synchronized (this) { + stopped = true; + notifyAll(); + } } /** @@ -313,9 +318,6 @@ public class ReplicationReceiver implements Runnable { queue = new ArrayList<>(); queueByReplicableIdAsString.put(replicableIdAsString, queue); } - if (queue.isEmpty()) { - notifyAll(); - } queue.add(new Pair>(replicable.getId().toString(), operation)); assert !queue.isEmpty(); } @@ -359,6 +361,7 @@ public class ReplicationReceiver implements Runnable { logger.info("Signaled Replicator thread to stop asap."); stopped = true; master.stopConnection(); + notifyAll(); // notify those waiting for stopped } public synchronized boolean isBeingStopped() { @@ -374,8 +377,9 @@ public class ReplicationReceiver implements Runnable { /** * @return true if all queues for all replicables are empty */ - public boolean isQueueEmpty() throws IllegalAccessException { - return (_queue == null || getInboundMessageQueue().isEmpty()) && !queueByReplicableIdAsString.values().stream().anyMatch(q->!q.isEmpty()); + public boolean isQueueEmptyOrStopped() throws IllegalAccessException { + return isBeingStopped() || + (_queue == null || getInboundMessageQueue().isEmpty()) && !queueByReplicableIdAsString.values().stream().anyMatch(q->!q.isEmpty()); } } diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java index c47c5677ade..15f00691519 100755 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java @@ -246,7 +246,12 @@ public class ReplicationServiceImpl implements ReplicationService { replicator = null; serverUUID = UUID.randomUUID(); logger.info("Setting " + serverUUID.toString() + " as unique replication identifier."); - + } + + @Override + protected void finalize() { + logger.info("terminating timer "+timer); + timer.cancel(); } protected ServiceTracker, Replicable> getReplicableTracker() {