diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java index 00b73d8a9b0..41c8c16ef3b 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java @@ -92,7 +92,12 @@ import com.sap.sse.security.shared.WithQualifiedObjectIdentifier; *

* * The overall race standings can be requested in terms of a competitor's ranking. More detailed information about what - * happens / happened within a leg is available from {@link TrackedLeg} and {@link TrackedLegOfCompetitor}. + * happens / happened within a leg is available from {@link TrackedLeg} and {@link TrackedLegOfCompetitor}.

+ * + * Note: if you de-serialize objects of this type, make sure to invoke {@link #initializeAfterDeserialization} after the + * entire object graph, including any cycles across {@link Regatta} objects, such as through the race's + * {@link #getTrackedRegatta()} and {@link TrackedRegatta#getRegatta()} links, has been fully read and its structure + * established. This will, e.g., ensure that XTE and maneuver cache calculations are triggered initially. * * @author Axel Uhl (d043530) * @@ -105,6 +110,17 @@ public interface TrackedRace final long MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS = 30000; final long DEFAULT_LIVE_DELAY_IN_MILLISECONDS = 5000; + + /** + * Runs the things after de-serialization of the entire graph of the enclosing {@link Regatta} required to + * fully initialize this object, such as triggering an initial calculation of the {@link #crossTrackErrorCache}, + * adjusting the leg structure to the race's current course, and triggering the maneuver cache calculations. + *

+ * + * Note that this method won't be called for an object that is instance of a subclass. Therefore, subclasses are + * advised to invoke this method in their own {@code readResolve()} implementation. + */ + void initializeAfterDeserialization(); RaceDefinition getRace(); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java index fbd0e7ce0b5..e0f87476292 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java @@ -2,7 +2,6 @@ package com.sap.sailing.domain.tracking.impl; import java.io.IOException; import java.io.ObjectInputStream; -import java.io.ObjectStreamException; import java.io.Serializable; import java.net.URISyntaxException; import java.util.ArrayList; @@ -175,11 +174,11 @@ DynamicTrackedRace, GPSTrackListener { raceAbortedListeners = new HashSet<>(); } - protected Object readResolve() throws ObjectStreamException { + protected Object readResolve() { if (markPassingCalculator != null) { logListener.setMarkPassingUpdateListener(markPassingCalculator.getListener()); } - return super.readResolve(); + return this; } private RaceStateChangedListener createRaceStateStartTimeChangeListener() { 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 e9e384d3c32..b4a10b76a04 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 @@ -3,7 +3,6 @@ package com.sap.sailing.domain.tracking.impl; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.io.ObjectStreamException; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; @@ -790,15 +789,8 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl logger.info("Deserialized race " + getRace().getName()); } - /** - * Runs the things after de-serialization that require the object to be fully constructed, - * including its subclasses.

- * - * Note that this method won't be called for an object that is instance of a subclass. - * Therefore, subclasses are advised to invoke this method in their own {@code readResolve()} - * implementation. - */ - protected Object readResolve() throws ObjectStreamException { + @Override + public void initializeAfterDeserialization() { crossTrackErrorCache.invalidate(); // considering the unlikely possibility that the course and this tracked race's internal structures // may be inconsistent, e.g., due to non-atomic serialization of course and tracked race; see bug 2223 @@ -808,7 +800,6 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl throw new RuntimeException(e); } // a bit unclean: this also tries to work on the DynamicTrackedRaceImpl which isn't fully initialized yet; see also bug6039 triggerManeuverCacheRecalculationForAllCompetitors(); // a bit unclean: this also tries to work on the DynamicTrackedRaceImpl which isn't fully initialized yet; see also bug6039 - return this; } /** 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 65e313a263a..eb1d693dfed 100644 --- 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 @@ -3688,6 +3688,7 @@ Replicator { for (TrackedRace trackedRace : trackedRegatta.getTrackedRaces()) { ((TrackedRaceImpl) trackedRace).setRaceLogResolver(this); ((TrackedRaceImpl) trackedRace).registerRegattaListener(); + trackedRace.initializeAfterDeserialization(); } } finally { trackedRegatta.unlockTrackedRacesAfterRead();