Merge remote-tracking branch 'origin/master' into mark-rounding-inference

This commit is contained in:
Nicolas committed 2014-05-08 12:37:47 +02:00
commit 4437f96502
20 files changed
+613 -51

No files matched your search

@@ -0,0 +1,5 @@
package com.sap.sailing.domain.base;
public interface BoatChangeListener {
void sailIdChanged(String oldSailId, String newSailId);
}
@@ -10,4 +10,17 @@ public interface Competitor extends Named, WithID, IsManagedBySharedDomainFactor
Boat getBoat();
Color getColor();
/**
* Adds a listener to this competitor. The listener is also added to the boat and the team for changes.
* Adding a listener that is already part of this competitor's listeners set remains without effect.
*/
void addCompetitorChangeListener(CompetitorChangeListener listener);
/**
* Removes a listener from this competitor. The listener is also removed from the boat and the team.
* Trying to remove a listener that is not currently in the set of this competitor's listener remains
* without effect.
*/
void removeCompetitorChangeListener(CompetitorChangeListener listener);
}
@@ -0,0 +1,9 @@
package com.sap.sailing.domain.base;
import com.sap.sailing.domain.common.Color;
public interface CompetitorChangeListener extends BoatChangeListener, NationalityChangeListener {
void colorChanged(Color oldColor, Color newColor);
void nameChanged(String oldName, String newName);
}
@@ -0,0 +1,7 @@
package com.sap.sailing.domain.base;
import java.io.Serializable;
public interface NationalityChangeListener extends Serializable {
void nationalityChanged(WithNationality what, Nationality oldNationality, Nationality newNationality);
}
@@ -1,17 +1,31 @@
package com.sap.sailing.domain.base.impl;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.HashSet;
import java.util.Set;
import com.sap.sailing.domain.base.BoatChangeListener;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.common.impl.NamedImpl;
import com.sap.sailing.domain.common.impl.Util;
public class BoatImpl extends NamedImpl implements DynamicBoat {
private static final long serialVersionUID = 3489730487528955788L;
private final BoatClass boatClass;
private String sailID;
private transient Set<BoatChangeListener> listeners;
public BoatImpl(String name, BoatClass boatClass, String sailID) {
super(name);
this.boatClass = boatClass;
this.sailID = sailID;
this.listeners = new HashSet<BoatChangeListener>();
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
listeners = new HashSet<BoatChangeListener>();
}
@Override
@@ -26,6 +40,32 @@ public class BoatImpl extends NamedImpl implements DynamicBoat {
@Override
public void setSailId(String newSailId) {
this.sailID = newSailId;
final String oldSailId = this.sailID;
if (!Util.equalsWithNull(oldSailId, newSailId)) {
this.sailID = newSailId;
for (BoatChangeListener listener : getListeners()) {
listener.sailIdChanged(oldSailId, newSailId);
}
}
}
@Override
public void addBoatChangeListener(BoatChangeListener listener) {
synchronized (listeners) {
listeners.add(listener);
}
}
@Override
public void removeCompetitorChangeListener(BoatChangeListener listener) {
synchronized (listeners) {
listeners.remove(listener);
}
}
private Iterable<BoatChangeListener> getListeners() {
synchronized (listeners) {
return new HashSet<BoatChangeListener>(listeners);
}
}
}
@@ -1,10 +1,16 @@
package com.sap.sailing.domain.base.impl;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CompetitorChangeListener;
import com.sap.sailing.domain.base.SharedDomainFactory;
import com.sap.sailing.domain.common.Color;
import com.sap.sailing.domain.common.impl.Util;
public class CompetitorImpl implements DynamicCompetitor {
private static final long serialVersionUID = 294603681016643157L;
@@ -12,8 +18,8 @@ public class CompetitorImpl implements DynamicCompetitor {
private final DynamicBoat boat;
private final Serializable id;
private String name;
private Color color;
private transient Set<CompetitorChangeListener> listeners;
public CompetitorImpl(Serializable id, String name, Color color, DynamicTeam team, DynamicBoat boat) {
this.id = id;
@@ -21,6 +27,12 @@ public class CompetitorImpl implements DynamicCompetitor {
this.team = team;
this.boat = boat;
this.color = color;
this.listeners = new HashSet<CompetitorChangeListener>();
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
listeners = new HashSet<CompetitorChangeListener>();
}
@Override
@@ -35,7 +47,13 @@ public class CompetitorImpl implements DynamicCompetitor {
@Override
public void setName(String newName) {
this.name = newName;
final String oldName = this.name;
if (!Util.equalsWithNull(oldName, newName)) {
this.name = newName;
for (CompetitorChangeListener listener : getListeners()) {
listener.nameChanged(oldName, newName);
}
}
}
@Override
@@ -64,6 +82,36 @@ public class CompetitorImpl implements DynamicCompetitor {
}
public void setColor(Color color) {
this.color = color;
final Color oldColor = this.color;
if (!Util.equalsWithNull(oldColor, color)) {
this.color = color;
for (CompetitorChangeListener listener : getListeners()) {
listener.colorChanged(oldColor, color);
}
}
}
@Override
public void addCompetitorChangeListener(CompetitorChangeListener listener) {
synchronized (listeners) {
listeners.add(listener);
}
getBoat().addBoatChangeListener(listener);
getTeam().addNationalityChangeListener(listener);
}
@Override
public void removeCompetitorChangeListener(CompetitorChangeListener listener) {
synchronized (listeners) {
listeners.remove(listener);
}
getBoat().removeCompetitorChangeListener(listener);
getTeam().removeNationalityChangeListener(listener);
}
private Iterable<CompetitorChangeListener> getListeners() {
synchronized (listeners) {
return new HashSet<CompetitorChangeListener>(listeners);
}
}
}
@@ -1,7 +1,13 @@
package com.sap.sailing.domain.base.impl;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.BoatChangeListener;
public interface DynamicBoat extends Boat {
void setSailId(String newSailId);
void addBoatChangeListener(BoatChangeListener listener);
void removeCompetitorChangeListener(BoatChangeListener listener);
}
@@ -1,8 +1,13 @@
package com.sap.sailing.domain.base.impl;
import com.sap.sailing.domain.base.Nationality;
import com.sap.sailing.domain.base.NationalityChangeListener;
import com.sap.sailing.domain.base.Person;
public interface DynamicPerson extends Person {
void setNationality(Nationality newNationality);
void addNationalityChangeListener(NationalityChangeListener listener);
void removeNationalityChangeListener(NationalityChangeListener listener);
}
@@ -1,8 +1,13 @@
package com.sap.sailing.domain.base.impl;
import com.sap.sailing.domain.base.Nationality;
import com.sap.sailing.domain.base.NationalityChangeListener;
import com.sap.sailing.domain.base.Team;
public interface DynamicTeam extends Team {
void setNationality(Nationality newNationality);
void addNationalityChangeListener(NationalityChangeListener listener);
void removeNationalityChangeListener(NationalityChangeListener listener);
}
@@ -1,24 +1,37 @@
package com.sap.sailing.domain.base.impl;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import com.sap.sailing.domain.base.Nationality;
import com.sap.sailing.domain.base.NationalityChangeListener;
import com.sap.sailing.domain.common.impl.NamedImpl;
import com.sap.sailing.domain.common.impl.Util;
public class PersonImpl extends NamedImpl implements DynamicPerson {
private static final long serialVersionUID = -2104903799224233508L;
private Nationality nationality;
private final Date dateOfBirth;
private final String description;
private transient Set<NationalityChangeListener> listeners;
public PersonImpl(String name, Nationality nationality, Date dateOfBirth, String description) {
super(name);
this.nationality = nationality;
this.dateOfBirth = dateOfBirth;
this.description = description;
this.listeners = new HashSet<NationalityChangeListener>();
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
listeners = new HashSet<NationalityChangeListener>();
}
@Override
public InputStream getImage() {
throw new UnsupportedOperationException("Image for person not yet implemented");
@@ -31,7 +44,13 @@ public class PersonImpl extends NamedImpl implements DynamicPerson {
@Override
public void setNationality(Nationality newNationality) {
this.nationality = newNationality;
final Nationality oldNationality = this.nationality;
if (!Util.equalsWithNull(oldNationality, newNationality)) {
this.nationality = newNationality;
for (NationalityChangeListener listener : getListeners()) {
listener.nationalityChanged(this, oldNationality, newNationality);
}
}
}
@Override
@@ -44,4 +63,23 @@ public class PersonImpl extends NamedImpl implements DynamicPerson {
return description;
}
@Override
public void addNationalityChangeListener(NationalityChangeListener listener) {
synchronized (listeners) {
listeners.add(listener);
}
}
@Override
public void removeNationalityChangeListener(NationalityChangeListener listener) {
synchronized (listeners) {
listeners.remove(listener);
}
}
private Iterable<NationalityChangeListener> getListeners() {
synchronized (listeners) {
return new HashSet<NationalityChangeListener>(listeners);
}
}
}
@@ -1,18 +1,80 @@
package com.sap.sailing.domain.base.impl;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.HashSet;
import java.util.Set;
import com.sap.sailing.domain.base.Nationality;
import com.sap.sailing.domain.base.NationalityChangeListener;
import com.sap.sailing.domain.base.Person;
import com.sap.sailing.domain.base.WithNationality;
import com.sap.sailing.domain.common.impl.NamedImpl;
import com.sap.sailing.domain.common.impl.Util;
public class TeamImpl extends NamedImpl implements DynamicTeam {
private static final long serialVersionUID = 4646922280429210183L;
private final Iterable<? extends DynamicPerson> sailors;
private final Person coach;
private final DynamicPerson coach;
private final NationalityChangeListener personNationalityChangeForwarder;
private transient Set<NationalityChangeListener> listeners;
public TeamImpl(String name, Iterable<? extends DynamicPerson> sailors, Person coach) {
public TeamImpl(String name, Iterable<? extends DynamicPerson> sailors, DynamicPerson coach) {
super(name);
listeners = new HashSet<NationalityChangeListener>();
this.sailors = sailors;
this.coach = coach;
this.personNationalityChangeForwarder = new NationalityChangeListener() {
private static final long serialVersionUID = -6024941913105410444L;
@Override
public void nationalityChanged(WithNationality what, Nationality oldNationality, Nationality newNationality) {
if (what == getNationalityDonor()) {
for (NationalityChangeListener listener : getNationalityChangeListeners()) {
listener.nationalityChanged(TeamImpl.this, oldNationality, newNationality);
}
}
}
};
setTransitiveListeners();
}
/**
* The listeners on {@link DynamicPerson} are transient. Our {@link #personNationalityChangeForwarder} needs to
* be restored after deserialization.
*/
private void setTransitiveListeners() {
if (this.sailors != null) {
for (DynamicPerson sailor : this.sailors) {
sailor.addNationalityChangeListener(personNationalityChangeForwarder);
}
}
if (this.coach != null) {
this.coach.addNationalityChangeListener(personNationalityChangeForwarder);
}
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
listeners = new HashSet<NationalityChangeListener>();
setTransitiveListeners();
}
public void addNationalityChangeListener(NationalityChangeListener listener) {
synchronized (listeners) {
listeners.add(listener);
}
}
public void removeNationalityChangeListener(NationalityChangeListener listener) {
synchronized (listeners) {
listeners.remove(listener);
}
}
private Iterable<NationalityChangeListener> getNationalityChangeListeners() {
synchronized (listeners) {
return new HashSet<NationalityChangeListener>(listeners);
}
}
@Override
@@ -21,28 +83,38 @@ public class TeamImpl extends NamedImpl implements DynamicTeam {
}
@Override
public Person getCoach() {
public DynamicPerson getCoach() {
return coach;
}
@Override
public Nationality getNationality() {
return getNationalityDonor().getNationality();
}
private WithNationality getNationalityDonor() {
for (Person sailor : getSailors()) {
if (sailor.getNationality() != null) {
return sailor.getNationality();
return sailor;
}
}
if (getCoach() != null) {
return getCoach().getNationality();
return getCoach();
}
return null;
}
@Override
public void setNationality(Nationality newNationality) {
for (Person sailor : getSailors()) {
((DynamicPerson) sailor).setNationality(newNationality);
Nationality oldNationality = getNationality();
if (!Util.equalsWithNull(oldNationality, newNationality)) {
for (Person sailor : getSailors()) {
((DynamicPerson) sailor).setNationality(newNationality);
}
for (NationalityChangeListener listener : getNationalityChangeListeners()) {
listener.nationalityChanged(this, oldNationality, newNationality);
}
}
}
}
@@ -0,0 +1,197 @@
package com.sap.sailing.domain.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import org.junit.Before;
import org.junit.Test;
import com.sap.sailing.domain.base.CompetitorChangeListener;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Nationality;
import com.sap.sailing.domain.base.Team;
import com.sap.sailing.domain.base.WithNationality;
import com.sap.sailing.domain.base.impl.DomainFactoryImpl;
import com.sap.sailing.domain.base.impl.DynamicCompetitor;
import com.sap.sailing.domain.common.Color;
import com.sap.sailing.domain.common.impl.RGBColor;
/**
* Tests the {@link CompetitorChangeListener} stuff including the serialization of the transitive listener
* pattern on the {@link Team} structure.
*
* @author Axel Uhl (D043530)
*
*/
public class CompetitorListenerTest extends AbstractSerializationTest {
private CompetitorChangeListener listener;
private boolean nationalityChanged;
private Object whatChangedNationality;
private Object oldNationality;
private Object newNationality;
private boolean sailIdChanged;
private String oldSailId;
private String newSailId;
private boolean nameChanged;
private String oldName;
private String newName;
private boolean colorChanged;
private Color oldColor;
private Color newColor;
private DynamicCompetitor competitor;
private DomainFactoryImpl baseDomainFactory;
@Before
public void setUp() {
baseDomainFactory = new DomainFactoryImpl();
competitor = TrackBasedTest.createCompetitor("Hasso");
nationalityChanged = false;
whatChangedNationality = null;
oldNationality = null;
newNationality = null;
sailIdChanged = false;
oldSailId = null;
newSailId = null;
nameChanged = false;
oldName = null;
newName = null;
colorChanged = false;
oldColor = null;
newColor = null;
listener = new CompetitorChangeListener() {
private static final long serialVersionUID = 4581029778988240209L;
@Override
public void nationalityChanged(WithNationality what, Nationality oldNationality, Nationality newNationality) {
nationalityChanged = true;
whatChangedNationality = what;
CompetitorListenerTest.this.oldNationality = oldNationality;
CompetitorListenerTest.this.newNationality = newNationality;
}
@Override
public void sailIdChanged(String oldSailId, String newSailId) {
sailIdChanged = true;
CompetitorListenerTest.this.oldSailId = oldSailId;
CompetitorListenerTest.this.newSailId = newSailId;
}
@Override
public void nameChanged(String oldName, String newName) {
nameChanged = true;
CompetitorListenerTest.this.oldName = oldName;
CompetitorListenerTest.this.newName = newName;
}
@Override
public void colorChanged(Color oldColor, Color newColor) {
colorChanged = true;
CompetitorListenerTest.this.oldColor = oldColor;
CompetitorListenerTest.this.newColor = newColor;
}
};
competitor.addCompetitorChangeListener(listener);
}
@Test
public void testSimpleCompetitorListenerPatternForName() {
competitor.setName("Dr. Hasso Plattner");
assertTrue(nameChanged);
assertEquals("Hasso", oldName);
assertEquals("Dr. Hasso Plattner", newName);
assertFalse(sailIdChanged);
assertFalse(colorChanged);
assertFalse(nationalityChanged);
}
@Test
public void testSimpleCompetitorListenerPatternForSailId() {
final String myOldSailId = competitor.getBoat().getSailID();
competitor.getBoat().setSailId("POR 40");
assertFalse(nameChanged);
assertTrue(sailIdChanged);
assertEquals(myOldSailId, oldSailId);
assertEquals("POR 40", newSailId);
assertFalse(colorChanged);
assertFalse(nationalityChanged);
}
@Test
public void testSimpleCompetitorListenerPatternForColor() {
final Color myOldColor = competitor.getColor();
final RGBColor myNewColor = new RGBColor(123, 12, 234);
competitor.setColor(myNewColor);
assertFalse(nameChanged);
assertFalse(sailIdChanged);
assertTrue(colorChanged);
assertEquals(myOldColor, oldColor);
assertEquals(myNewColor, newColor);
assertFalse(nationalityChanged);
}
@Test
public void testSimpleCompetitorListenerPatternForNationality() {
final Nationality myOldNationality = competitor.getTeam().getNationality();
final Nationality myNewNationality = DomainFactory.INSTANCE.getOrCreateNationality("POR");
competitor.getTeam().setNationality(myNewNationality);
assertFalse(nameChanged);
assertFalse(sailIdChanged);
assertFalse(colorChanged);
assertTrue(nationalityChanged);
assertEquals(myOldNationality, oldNationality);
assertEquals(myNewNationality, newNationality);
assertSame(competitor.getTeam(), whatChangedNationality);
}
@Test
public void testCompetitorSerializationLosesAllExternalListeners() throws ClassNotFoundException, IOException {
DomainFactory baseDomainFactory = new DomainFactoryImpl();
DynamicCompetitor clonedCompetitor = cloneBySerialization(competitor, baseDomainFactory);
clonedCompetitor.setName("Dr. Hasso Plattner");
clonedCompetitor.getBoat().setSailId("POR 40");
final RGBColor myNewColor = new RGBColor(123, 12, 234);
clonedCompetitor.setColor(myNewColor);
final Nationality myNewNationality = baseDomainFactory.getOrCreateNationality("POR");
clonedCompetitor.getTeam().setNationality(myNewNationality);
assertFalse(nameChanged);
assertFalse(sailIdChanged);
assertFalse(colorChanged);
assertFalse(nationalityChanged);
}
@Test
public void testCompetitorSerializationListenerPatternForNameOnSerializedCompetitor() throws ClassNotFoundException, IOException {
DynamicCompetitor clonedCompetitor = cloneBySerialization(competitor, baseDomainFactory);
competitor = clonedCompetitor;
competitor.addCompetitorChangeListener(listener);
testSimpleCompetitorListenerPatternForName();
}
@Test
public void testCompetitorSerializationListenerPatternForColorOnSerializedCompetitor() throws ClassNotFoundException, IOException {
DynamicCompetitor clonedCompetitor = cloneBySerialization(competitor, baseDomainFactory);
competitor = clonedCompetitor;
competitor.addCompetitorChangeListener(listener);
testSimpleCompetitorListenerPatternForColor();
}
@Test
public void testCompetitorSerializationListenerPatternForSailIdOnSerializedCompetitor() throws ClassNotFoundException, IOException {
DynamicCompetitor clonedCompetitor = cloneBySerialization(competitor, baseDomainFactory);
competitor = clonedCompetitor;
competitor.addCompetitorChangeListener(listener);
testSimpleCompetitorListenerPatternForSailId();
}
@Test
public void testCompetitorSerializationListenerPatternForNationalityOnSerializedCompetitor() throws ClassNotFoundException, IOException {
DynamicCompetitor clonedCompetitor = cloneBySerialization(competitor, baseDomainFactory);
competitor = clonedCompetitor;
competitor.addCompetitorChangeListener(listener);
testSimpleCompetitorListenerPatternForNationality();
}
}
@@ -7,11 +7,15 @@ import java.util.Set;
import java.util.WeakHashMap;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CompetitorChangeListener;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Nationality;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceColumnListener;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.WithNationality;
import com.sap.sailing.domain.common.Color;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.WindSource;
@@ -39,7 +43,8 @@ import com.sap.sailing.util.impl.NamedReentrantReadWriteLock;
*/
public class LeaderboardCacheManager {
private final WeakHashMap<Leaderboard, CacheInvalidationUponScoreCorrectionListener> scoreCorrectionListeners;
private final NamedReentrantReadWriteLock scoreCorrectionListenersLock;
private final NamedReentrantReadWriteLock scoreCorrectionAndCompetitorChangeListenersLock;
private final WeakHashMap<Leaderboard, CacheInvalidationUponCompetitorChangeListener> competitorChangeListeners;
private final WeakHashMap<Leaderboard, Map<TrackedRace, Set<CacheInvalidationListener>>> invalidationListenersPerLeaderboard;
private final WeakHashMap<Leaderboard, RaceColumnListener> raceColumnListeners;
@@ -113,6 +118,64 @@ public class LeaderboardCacheManager {
}
}
private class CacheInvalidationUponCompetitorChangeListener implements CompetitorChangeListener {
private static final long serialVersionUID = 8308312509904366143L;
private final Leaderboard leaderboard;
private final Set<Competitor> observedCompetitors;
/**
* Registers this listener as listener for all of the <code>leaderboard</code>'s current
* {@link Leaderboard#getCompetitors() competitors}
*/
public CacheInvalidationUponCompetitorChangeListener(Leaderboard leaderboard) {
this.leaderboard = leaderboard;
observedCompetitors = new HashSet<>();
updateCompetitorListeners();
}
@Override
public void sailIdChanged(String oldSailId, String newSailId) {
removeFromCache(leaderboard);
}
@Override
public void nationalityChanged(WithNationality what, Nationality oldNationality, Nationality newNationality) {
removeFromCache(leaderboard);
}
@Override
public void colorChanged(Color oldColor, Color newColor) {
removeFromCache(leaderboard);
}
@Override
public void nameChanged(String oldName, String newName) {
removeFromCache(leaderboard);
}
public synchronized void updateCompetitorListeners() {
Set<Competitor> competitorsToStopObserving = new HashSet<>(observedCompetitors);
for (Competitor competitor : leaderboard.getCompetitors()) {
if (!observedCompetitors.contains(competitor)) {
competitor.addCompetitorChangeListener(this); // start observing competitor
} else {
competitorsToStopObserving.remove(competitor); // keep observing competitor
}
}
for (Competitor competitorToStopObserving : competitorsToStopObserving) {
competitorToStopObserving.removeCompetitorChangeListener(this);
observedCompetitors.remove(competitorToStopObserving);
}
}
public synchronized void removeFromAllCompetitors() {
for (Competitor competitor : observedCompetitors) {
competitor.removeCompetitorChangeListener(this);
}
}
}
private class CacheInvalidationUponScoreCorrectionListener implements ScoreCorrectionListener {
private final Leaderboard leaderboard;
@@ -146,10 +209,12 @@ public class LeaderboardCacheManager {
public LeaderboardCacheManager(LeaderboardCache leaderboardCache) {
this.leaderboardCache = leaderboardCache;
this.invalidationListenersPerLeaderboard = new WeakHashMap<Leaderboard, Map<TrackedRace, Set<CacheInvalidationListener>>>();
this.invalidationListenersPerLeaderboard = new WeakHashMap<>();
this.raceColumnListeners = new WeakHashMap<Leaderboard, RaceColumnListener>();
this.scoreCorrectionListeners = new WeakHashMap<Leaderboard, CacheInvalidationUponScoreCorrectionListener>();
this.scoreCorrectionListenersLock = new NamedReentrantReadWriteLock("Score correction listeners", /* fair */ false);
this.scoreCorrectionListeners = new WeakHashMap<>();
this.scoreCorrectionAndCompetitorChangeListenersLock = new NamedReentrantReadWriteLock(
"Score correction and competitor change listeners", /* fair */false);
this.competitorChangeListeners = new WeakHashMap<>();
}
private void removeFromCache(Leaderboard leaderboard) {
@@ -167,14 +232,21 @@ public class LeaderboardCacheManager {
synchronized (raceColumnListeners) {
leaderboard.removeRaceColumnListener(raceColumnListeners.remove(leaderboard));
}
LockUtil.lockForWrite(scoreCorrectionListenersLock);
LockUtil.lockForWrite(scoreCorrectionAndCompetitorChangeListenersLock);
final CacheInvalidationUponScoreCorrectionListener removedScoreCorrectionListener;
final CacheInvalidationUponCompetitorChangeListener removedCompetitorChangeListener;
try {
removedScoreCorrectionListener = scoreCorrectionListeners.remove(leaderboard);
removedCompetitorChangeListener = competitorChangeListeners.remove(leaderboard);
} finally {
LockUtil.unlockAfterWrite(scoreCorrectionListenersLock);
LockUtil.unlockAfterWrite(scoreCorrectionAndCompetitorChangeListenersLock);
}
if (removedScoreCorrectionListener != null) {
leaderboard.getScoreCorrection().removeScoreCorrectionListener(removedScoreCorrectionListener);
}
if (removedCompetitorChangeListener != null) {
removedCompetitorChangeListener.removeFromAllCompetitors();
}
leaderboard.getScoreCorrection().removeScoreCorrectionListener(removedScoreCorrectionListener);
// invalidate after the listeners have been removed; this is important because invalidation may trigger a
// re-calculation which then in turn may call add(leaderboard) asynchronously again which may be executed
// before the listener removal happens here. This could lead to a race condition where listeners are
@@ -195,22 +267,25 @@ public class LeaderboardCacheManager {
private void registerAsListener(final Leaderboard leaderboard) {
// only add as listener again if not yet added
final boolean containsKey;
LockUtil.lockForRead(scoreCorrectionListenersLock);
LockUtil.lockForRead(scoreCorrectionAndCompetitorChangeListenersLock);
try {
containsKey = scoreCorrectionListeners.containsKey(leaderboard);
} finally {
LockUtil.unlockAfterRead(scoreCorrectionListenersLock);
LockUtil.unlockAfterRead(scoreCorrectionAndCompetitorChangeListenersLock);
}
if (!containsKey) {
LockUtil.lockForWrite(scoreCorrectionListenersLock);
LockUtil.lockForWrite(scoreCorrectionAndCompetitorChangeListenersLock);
try {
for (TrackedRace trackedRace : leaderboard.getTrackedRaces()) {
registerListener(leaderboard, trackedRace);
}
final CacheInvalidationUponScoreCorrectionListener scoreCorrectionListener = new CacheInvalidationUponScoreCorrectionListener(
leaderboard);
final CacheInvalidationUponCompetitorChangeListener competitorChangeListener = new CacheInvalidationUponCompetitorChangeListener(
leaderboard);
leaderboard.getScoreCorrection().addScoreCorrectionListener(scoreCorrectionListener);
scoreCorrectionListeners.put(leaderboard, scoreCorrectionListener);
competitorChangeListeners.put(leaderboard, competitorChangeListener);
final RaceColumnListener raceColumnListener = new RaceColumnListener() {
private static final long serialVersionUID = 8165124797028386317L;
@@ -312,7 +387,7 @@ public class LeaderboardCacheManager {
raceColumnListeners.put(leaderboard, raceColumnListener);
}
} finally {
LockUtil.unlockAfterWrite(scoreCorrectionListenersLock);
LockUtil.unlockAfterWrite(scoreCorrectionAndCompetitorChangeListenersLock);
}
}
}
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.tracking.impl;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CompetitorChangeListener;
import com.sap.sailing.domain.base.IsManagedBySharedDomainFactory;
import com.sap.sailing.domain.base.SharedDomainFactory;
import com.sap.sailing.domain.base.Team;
@@ -63,6 +64,14 @@ public class DummyMarkPassingWithTimePointOnly implements MarkPassing {
public Color getColor() {
return null;
}
@Override
public void addCompetitorChangeListener(CompetitorChangeListener listener) {
}
@Override
public void removeCompetitorChangeListener(CompetitorChangeListener listener) {
}
};
}
}
@@ -132,10 +132,14 @@ public class CompetitorEditDialog extends DataEntryDialog<CompetitorDTO> {
@Override
protected CompetitorDTO getResult() {
Color color;
try {
color = new RGBColor(displayColorTextBox.getText());
} catch (IllegalArgumentException iae) {
color = new InvalidColor(iae);
if (displayColorTextBox.getText() == null || displayColorTextBox.getText().isEmpty()) {
color = null;
} else {
try {
color = new RGBColor(displayColorTextBox.getText());
} catch (IllegalArgumentException iae) {
color = new InvalidColor(iae);
}
}
CompetitorDTO result = new CompetitorDTOImpl(name.getText(), color,
/* twoLetterIsoCountryCode */ null,
@@ -6,7 +6,6 @@ import java.util.Set;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import com.sap.sailing.domain.base.Person;
import com.sap.sailing.domain.base.SharedDomainFactory;
import com.sap.sailing.domain.base.impl.DynamicPerson;
import com.sap.sailing.domain.base.impl.DynamicTeam;
@@ -32,7 +31,7 @@ public class TeamJsonDeserializer implements JsonDeserializer<DynamicTeam> {
public DynamicTeam deserialize(JSONObject object)
throws JsonDeserializationException {
String name = (String) object.get(TeamJsonSerializer.FIELD_NAME);
Person coach = personDeserializer.deserialize((JSONObject) object
DynamicPerson coach = personDeserializer.deserialize((JSONObject) object
.get(TeamJsonSerializer.FIELD_COACH));
Set<DynamicPerson> sailors = new HashSet<DynamicPerson>();
@@ -37,7 +37,6 @@ import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.Person;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series;
@@ -193,7 +192,7 @@ public class MasterDataImportTest {
Set<DynamicPerson> sailors = new HashSet<DynamicPerson>();
sailors.add(new PersonImpl("Froderik Poterson", new NationalityImpl("GER"), new Date(645487200000L),
"Oberhoschy"));
Person coach = new PersonImpl("Lennart Hensler", new NationalityImpl("GER"), new Date(645487200000L),
DynamicPerson coach = new PersonImpl("Lennart Hensler", new NationalityImpl("GER"), new Date(645487200000L),
"Der Lennart halt");
DynamicTeam team = new TeamImpl("Pros", sailors, coach);
BoatClass boatClass = new BoatClassImpl("H16", true);
@@ -204,7 +203,7 @@ public class MasterDataImportTest {
Set<DynamicPerson> sailors2 = new HashSet<DynamicPerson>();
sailors2.add(new PersonImpl("Angela Merkel", new NationalityImpl("GER"), new Date(645487200000L),
"segelt auch mit"));
Person coach2 = new PersonImpl("Peer Steinbrueck", new NationalityImpl("GER"), new Date(645487200000L),
DynamicPerson coach2 = new PersonImpl("Peer Steinbrueck", new NationalityImpl("GER"), new Date(645487200000L),
"Bester Coach");
DynamicTeam team2 = new TeamImpl("Noobs", sailors2, coach2);
DynamicBoat boat2 = new BoatImpl("LahmeEnte", boatClass, "GER1337");
@@ -402,7 +401,7 @@ public class MasterDataImportTest {
Set<DynamicPerson> sailors = new HashSet<DynamicPerson>();
sailors.add(new PersonImpl("Froderik Poterson", new NationalityImpl("GER"), new Date(645487200000L),
"Oberhoschy"));
Person coach = new PersonImpl("Lennart Hensler", new NationalityImpl("GER"), new Date(645487200000L),
DynamicPerson coach = new PersonImpl("Lennart Hensler", new NationalityImpl("GER"), new Date(645487200000L),
"Der Lennart halt");
DynamicTeam team = new TeamImpl("Pros", sailors, coach);
BoatClass boatClass = new BoatClassImpl("H16", true);
@@ -412,7 +411,7 @@ public class MasterDataImportTest {
UUID competitor2UUID = UUID.randomUUID();
Set<DynamicPerson> sailors2 = new HashSet<DynamicPerson>();
sailors2.add(new PersonImpl("Test Mustermann", new NationalityImpl("GER"), new Date(645487200000L), "desc"));
Person coach2 = new PersonImpl("Max Test", new NationalityImpl("GER"), new Date(645487200000L), "desc");
DynamicPerson coach2 = new PersonImpl("Max Test", new NationalityImpl("GER"), new Date(645487200000L), "desc");
DynamicTeam team2 = new TeamImpl("Pros2", sailors2, coach2);
DynamicBoat boat2 = new BoatImpl("FastBoat", boatClass, "GER70133");
CompetitorImpl competitor2 = new CompetitorImpl(competitor2UUID, "Froderik", Color.RED, team2, boat2);
@@ -678,7 +677,7 @@ public class MasterDataImportTest {
Set<DynamicPerson> sailors = new HashSet<DynamicPerson>();
sailors.add(new PersonImpl("Froderik Poterson", new NationalityImpl("GER"), new Date(645487200000L),
"Oberhoschy"));
Person coach = new PersonImpl("Lennart Hensler", new NationalityImpl("GER"), new Date(645487200000L),
DynamicPerson coach = new PersonImpl("Lennart Hensler", new NationalityImpl("GER"), new Date(645487200000L),
"Der Lennart halt");
DynamicTeam team = new TeamImpl("Pros", sailors, coach);
BoatClass boatClass = new BoatClassImpl("H16", true);
@@ -689,7 +688,7 @@ public class MasterDataImportTest {
UUID competitor2UUID = UUID.randomUUID();
Set<DynamicPerson> sailors2 = new HashSet<DynamicPerson>();
sailors2.add(new PersonImpl("Test Mustermann", new NationalityImpl("GER"), new Date(645487200000L), "desc"));
Person coach2 = new PersonImpl("Max Test", new NationalityImpl("GER"), new Date(645487200000L), "desc");
DynamicPerson coach2 = new PersonImpl("Max Test", new NationalityImpl("GER"), new Date(645487200000L), "desc");
DynamicTeam team2 = new TeamImpl("Pros2", sailors2, coach2);
DynamicBoat boat2 = new BoatImpl("FastBoat", boatClass, "GER70133");
Competitor competitor2 = sourceDomainFactory.getCompetitorStore().getOrCreateCompetitor(competitor2UUID,
@@ -833,7 +832,7 @@ public class MasterDataImportTest {
Set<DynamicPerson> sailors = new HashSet<DynamicPerson>();
sailors.add(new PersonImpl("Froderik Poterson", new NationalityImpl("GER"), new Date(645487200000L),
"Oberhoschy"));
Person coach = new PersonImpl("Lennart Hensler", new NationalityImpl("GER"), new Date(645487200000L),
DynamicPerson coach = new PersonImpl("Lennart Hensler", new NationalityImpl("GER"), new Date(645487200000L),
"Der Lennart halt");
DynamicTeam team = new TeamImpl("Pros", sailors, coach);
BoatClass boatClass = new BoatClassImpl("H16", true);
@@ -844,7 +843,7 @@ public class MasterDataImportTest {
Set<DynamicPerson> sailors2 = new HashSet<DynamicPerson>();
sailors2.add(new PersonImpl("Angela Merkel", new NationalityImpl("GER"), new Date(645487200000L),
"segelt auch mit"));
Person coach2 = new PersonImpl("Peer Steinbrueck", new NationalityImpl("GER"), new Date(645487200000L),
DynamicPerson coach2 = new PersonImpl("Peer Steinbrueck", new NationalityImpl("GER"), new Date(645487200000L),
"Bester Coach");
DynamicTeam team2 = new TeamImpl("Noobs", sailors2, coach2);
DynamicBoat boat2 = new BoatImpl("LahmeEnte", boatClass, "GER1337");
@@ -1029,7 +1028,7 @@ public class MasterDataImportTest {
Set<DynamicPerson> sailors = new HashSet<DynamicPerson>();
sailors.add(new PersonImpl("Froderik Poterson", new NationalityImpl("GER"), new Date(645487200000L),
"Oberhoschy"));
Person coach = new PersonImpl("Lennart Hensler", new NationalityImpl("GER"), new Date(645487200000L),
DynamicPerson coach = new PersonImpl("Lennart Hensler", new NationalityImpl("GER"), new Date(645487200000L),
"Der Lennart halt");
DynamicTeam team = new TeamImpl("Pros", sailors, coach);
BoatClass boatClass = new BoatClassImpl("H16", true);
@@ -1040,7 +1039,7 @@ public class MasterDataImportTest {
Set<DynamicPerson> sailors2 = new HashSet<DynamicPerson>();
sailors2.add(new PersonImpl("Angela Merkel", new NationalityImpl("GER"), new Date(645487200000L),
"segelt auch mit"));
Person coach2 = new PersonImpl("Peer Steinbrueck", new NationalityImpl("GER"), new Date(645487200000L),
DynamicPerson coach2 = new PersonImpl("Peer Steinbrueck", new NationalityImpl("GER"), new Date(645487200000L),
"Bester Coach");
DynamicTeam team2 = new TeamImpl("Noobs", sailors2, coach2);
DynamicBoat boat2 = new BoatImpl("LahmeEnte", boatClass, "GER1337");
@@ -1127,7 +1126,7 @@ public class MasterDataImportTest {
Set<DynamicPerson> sailorsToOverride = new HashSet<DynamicPerson>();
sailorsToOverride.add(new PersonImpl("Froderik Poterson", new NationalityImpl("GER"), new Date(
645487200000L), "Oberhoschy"));
Person coachToOverride = new PersonImpl("Lennart Hensler", new NationalityImpl("GER"), new Date(
DynamicPerson coachToOverride = new PersonImpl("Lennart Hensler", new NationalityImpl("GER"), new Date(
645487200000L), "Der Lennart halt");
DynamicTeam teamToOverride = new TeamImpl("Pros", sailorsToOverride, coachToOverride);
BoatClass boatClassToOverride = new BoatClassImpl("H16", true);
@@ -1298,7 +1297,7 @@ public class MasterDataImportTest {
Set<DynamicPerson> sailors = new HashSet<DynamicPerson>();
sailors.add(new PersonImpl("Froderik Poterson", new NationalityImpl("GER"), new Date(645487200000L),
"Oberhoschy"));
Person coach = new PersonImpl("Lennart Hensler", new NationalityImpl("GER"), new Date(645487200000L),
DynamicPerson coach = new PersonImpl("Lennart Hensler", new NationalityImpl("GER"), new Date(645487200000L),
"Der Lennart halt");
DynamicTeam team = new TeamImpl("Pros", sailors, coach);
BoatClass boatClass = new BoatClassImpl("H16", true);
@@ -1309,7 +1308,7 @@ public class MasterDataImportTest {
Set<DynamicPerson> sailors2 = new HashSet<DynamicPerson>();
sailors2.add(new PersonImpl("Angela Merkel", new NationalityImpl("GER"), new Date(645487200000L),
"segelt auch mit"));
Person coach2 = new PersonImpl("Peer Steinbrueck", new NationalityImpl("GER"), new Date(645487200000L),
DynamicPerson coach2 = new PersonImpl("Peer Steinbrueck", new NationalityImpl("GER"), new Date(645487200000L),
"Bester Coach");
DynamicTeam team2 = new TeamImpl("Noobs", sailors2, coach2);
DynamicBoat boat2 = new BoatImpl("LahmeEnte", boatClass, "GER1337");
@@ -1431,7 +1430,7 @@ public class MasterDataImportTest {
Set<DynamicPerson> sailors = new HashSet<DynamicPerson>();
sailors.add(new PersonImpl("Froderik Poterson", new NationalityImpl("GER"), new Date(645487200000L),
"Oberhoschy"));
Person coach = new PersonImpl("Lennart Hensler", new NationalityImpl("GER"), new Date(645487200000L),
DynamicPerson coach = new PersonImpl("Lennart Hensler", new NationalityImpl("GER"), new Date(645487200000L),
"Der Lennart halt");
DynamicTeam team = new TeamImpl("Pros", sailors, coach);
BoatClass boatClass = new BoatClassImpl("H16", true);
@@ -1442,7 +1441,7 @@ public class MasterDataImportTest {
Set<DynamicPerson> sailors2 = new HashSet<DynamicPerson>();
sailors2.add(new PersonImpl("Angela Merkel", new NationalityImpl("GER"), new Date(645487200000L),
"segelt auch mit"));
Person coach2 = new PersonImpl("Peer Steinbrueck", new NationalityImpl("GER"), new Date(645487200000L),
DynamicPerson coach2 = new PersonImpl("Peer Steinbrueck", new NationalityImpl("GER"), new Date(645487200000L),
"Bester Coach");
DynamicTeam team2 = new TeamImpl("Noobs", sailors2, coach2);
DynamicBoat boat2 = new BoatImpl("LahmeEnte", boatClass, "GER1337");
@@ -1638,7 +1637,7 @@ public class MasterDataImportTest {
Set<DynamicPerson> sailors = new HashSet<DynamicPerson>();
sailors.add(new PersonImpl("Froderik Poterson", new NationalityImpl("GER"), new Date(645487200000L),
"Oberhoschy"));
Person coach = new PersonImpl("Lennart Hensler", new NationalityImpl("GER"), new Date(645487200000L),
DynamicPerson coach = new PersonImpl("Lennart Hensler", new NationalityImpl("GER"), new Date(645487200000L),
"Der Lennart halt");
DynamicTeam team = new TeamImpl("Pros", sailors, coach);
BoatClass boatClass = new BoatClassImpl("H16", true);
@@ -1649,7 +1648,7 @@ public class MasterDataImportTest {
Set<DynamicPerson> sailors2 = new HashSet<DynamicPerson>();
sailors2.add(new PersonImpl("Angela Merkel", new NationalityImpl("GER"), new Date(645487200000L),
"segelt auch mit"));
Person coach2 = new PersonImpl("Peer Steinbrueck", new NationalityImpl("GER"), new Date(645487200000L),
DynamicPerson coach2 = new PersonImpl("Peer Steinbrueck", new NationalityImpl("GER"), new Date(645487200000L),
"Bester Coach");
DynamicTeam team2 = new TeamImpl("Noobs", sailors2, coach2);
DynamicBoat boat2 = new BoatImpl("LahmeEnte", boatClass, "GER1337");
@@ -29,7 +29,9 @@ public class UpdateCompetitor extends AbstractRacingEventServiceOperation<Compet
@Override
public Competitor internalApplyTo(RacingEventService toState) throws Exception {
return toState.getBaseDomainFactory().getCompetitorStore().updateCompetitor(idAsString, newName, newDisplayColor, newSailId, newNationality);
Competitor result = toState.getBaseDomainFactory().getCompetitorStore()
.updateCompetitor(idAsString, newName, newDisplayColor, newSailId, newNationality);
return result;
}
@Override
+25 -2
View File
@@ -95,7 +95,30 @@
<a href='http://www.extremesailingseries.com' target='_blank'>Official website</a>
</div>
</div>
<div class='raceSection'>
<div class='raceSection'>
<h5 class='date'>
2013 Season
</h5>
<div class='raceTimepoint'></div>
<a href='http://bundesliga2013.sapsailing.com' target='_blank'>
<ul class='racePanel'>
<li class='raceName'>
German Sailing League / Bundesliga 2013
</li>
<li class='raceLocation'>
Germany
</li>
</ul>
</a>
<div class='raceDescription'>
<p>German Sailing League / Bundesliga 2013</p>
<a href='http://bundesliga2013.sapsailing.com' target='_blank'>SAP Sailing Analytics</a><br/>
<a href='http://segelbundesliga.de' target='_blank'>Official website</a>
</div>
</div>
<div class='raceSection'>
<h5 class='date'>
Sep 19-22
</h5>
@@ -115,7 +138,7 @@
<a href='http://505idm2013.sapsailing.com' target='_blank'>SAP Sailing Analytics</a><br/>
<a href='http://505idm2013.de' target='_blank'>Official website</a>
</div>
</div>
</div>
<div class='raceSection'>
<h5 class='date'>
July 19 - 28
@@ -38,6 +38,12 @@
<h2 class="articleSubheadline">May 2014</h2>
<ul class="bulletList">
<li>A hint regarding the "distance to start line" metrics: please keep in mind that currently this
measurement is calculated as the distance of the GPS tracker from the line. Unless the tracker
is mounted near the bow (or at the bow sprit, if any), the distance calculated this way will
be greater than what counts for crossing the line. Ordering, comparing and aggregating over this
figure still makes sense as long as you remember this detail.
</li>
<li>Introduced a new leaderboard entry "XTE +/-" featuring a signed cross-track error which shows
for a leg and also aggregated for an entire race on which side of the leg a competitor stayed on
average. Currently, the "side" is defined as viewing in the leg's direction, so "left" for a downwind