Merge branch 'master' into bug1923

This commit is contained in:
Axel Uhl committed 2014-05-08 00:34:08 +02:00
commit b33035c00c
19 files changed
+607 -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