Merge branch 'master' into bug1564

This commit is contained in:
Axel Uhl committed 2016-10-17 13:25:34 +02:00
commit d0774b9419
19 files changed
+1702 -62

No files matched your search

@@ -143,9 +143,17 @@ public class RaceLogScoringReplicator implements RaceColumnListener {
if (positioningList != null) {
for (CompetitorResult positionedCompetitor : positioningList) {
Competitor competitor = service.getBaseDomainFactory().getExistingCompetitorById(positionedCompetitor.getCompetitorId());
int rankByRaceCommittee = getRankInPositioningListByRaceCommittee(positionedCompetitor);
correctScoreInLeaderboard(leaderboard, raceColumn, timePoint, numberOfCompetitorsInRace,
competitor, rankByRaceCommittee, positionedCompetitor.getScore());
// The score is updated when explicitly provided or when no penalty was set;
// in turn, this means that when a penalty is set and no score is explicitly provided,
// it is up to the scoring scheme to infer a penalty score for the MaxPointsReason.
// See also bug 3955.
if (positionedCompetitor.getScore() != null
|| positionedCompetitor.getMaxPointsReason() == null
|| positionedCompetitor.getMaxPointsReason().equals(MaxPointsReason.NONE)) {
int rankByRaceCommittee = getRankInPositioningListByRaceCommittee(positionedCompetitor);
correctScoreInLeaderboard(leaderboard, raceColumn, timePoint, numberOfCompetitorsInRace, competitor,
rankByRaceCommittee, positionedCompetitor.getScore());
}
setMaxPointsReasonInLeaderboardIfNecessary(leaderboard, raceColumn, timePoint, positionedCompetitor.getMaxPointsReason(), competitor);
}
// Since the metadata update is used by the Sailing suite to determine the final state of a race, it has to
@@ -1774,11 +1774,12 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
public void finishedTimeChanged(TimePoint oldFinishedTime, TimePoint newFinishedTime) {
// no action required; the update signaled by this call is implicit; the race log
// updates that led to this change are replicated separately
scheduler.execute(()->
// Notify interested users:
notificationService.notifyUserOnBoatClassRaceChangesStateToFinished(trackedRace.getRace().getBoatClass(), trackedRace,
getMostAppropriateLeaderboard(), getMostAppropriateRaceColumn(), getMostAppropriateFleet()));
if (newFinishedTime.after(MillisecondsTimePoint.now().minus(Duration.ONE_HOUR))) {
scheduler.execute(()->
// Notify interested users:
notificationService.notifyUserOnBoatClassRaceChangesStateToFinished(trackedRace.getRace().getBoatClass(), trackedRace,
getMostAppropriateLeaderboard(), getMostAppropriateRaceColumn(), getMostAppropriateFleet()));
}
}
@Override
@@ -1838,7 +1839,8 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
replicate(new UpdateMarkPassings(getRaceIdentifier(), competitor, markPassings));
final MarkPassing last = Util.last(markPassings);
if (last != null && last.getWaypoint() == trackedRace.getRace().getCourse().getLastWaypoint() &&
trackedRace.getStatus().getStatus() != TrackedRaceStatusEnum.LOADING) {
trackedRace.getStatus().getStatus() != TrackedRaceStatusEnum.LOADING &&
last.getTimePoint().after(MillisecondsTimePoint.now().minus(Duration.ONE_HOUR))) {
scheduler.execute(() ->
// Notify interested users:
notificationService.notifyUserOnCompetitorPassesFinish(competitor, trackedRace,
@@ -33,6 +33,8 @@ import com.sap.sailing.server.gateway.deserialization.impl.LeaderboardGroupBaseJ
import com.sap.sailing.server.gateway.deserialization.impl.VenueJsonDeserializer;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.concurrent.LockUtil;
import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
import com.sap.sse.util.HttpUrlConnectionHelper;
/**
@@ -44,15 +46,16 @@ import com.sap.sse.util.HttpUrlConnectionHelper;
*/
public class RemoteSailingServerSet {
private static final int POLLING_INTERVAL_IN_SECONDS = 60;
private NamedReentrantReadWriteLock lock = new NamedReentrantReadWriteLock("lock for RemoteSailingServerSet", true);
private static final Logger logger = Logger.getLogger(RemoteSailingServerSet.class.getName());
/**
* Holds the remote server references managed by this set. Keys are the
* {@link RemoteSailingServerReference#getName() names} of the server references.
*/
private final ConcurrentMap<String, RemoteSailingServerReference> remoteSailingServers;
private final ConcurrentMap<RemoteSailingServerReference, Util.Pair<Iterable<EventBase>, Exception>> cachedEventsForRemoteSailingServers;
/**
@@ -62,53 +65,63 @@ public class RemoteSailingServerSet {
public RemoteSailingServerSet(ScheduledExecutorService scheduler) {
remoteSailingServers = new ConcurrentHashMap<>();
cachedEventsForRemoteSailingServers = new ConcurrentHashMap<>();
scheduler.scheduleAtFixedRate(new Runnable() { @Override public void run() { updateRemoteSailingServerReferenceEventCaches(); } },
/* initialDelay */ 0, POLLING_INTERVAL_IN_SECONDS, TimeUnit.SECONDS);
scheduler.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
updateRemoteSailingServerReferenceEventCaches();
}
}, /* initialDelay */ 0, POLLING_INTERVAL_IN_SECONDS, TimeUnit.SECONDS);
}
public void clear() {
remoteSailingServers.clear();
cachedEventsForRemoteSailingServers.clear();
LockUtil.executeWithWriteLock(lock, () -> {
remoteSailingServers.clear();
cachedEventsForRemoteSailingServers.clear();
});
}
public void add(RemoteSailingServerReference remoteSailingServerReference) {
remoteSailingServers.put(remoteSailingServerReference.getName(), remoteSailingServerReference);
triggerAsynchronousEventCacheUpdate(remoteSailingServerReference);
LockUtil.executeWithWriteLock(lock, () -> {
remoteSailingServers.put(remoteSailingServerReference.getName(), remoteSailingServerReference);
triggerAsynchronousEventCacheUpdate(remoteSailingServerReference);
});
}
private void updateRemoteSailingServerReferenceEventCaches() {
for (RemoteSailingServerReference ref : remoteSailingServers.values()) {
triggerAsynchronousEventCacheUpdate(ref);
}
LockUtil.executeWithReadLock(lock, () -> {
for (RemoteSailingServerReference ref : remoteSailingServers.values()) {
triggerAsynchronousEventCacheUpdate(ref);
}
});
}
/**
* If this set has a {@link RemoteSailingServerReference} whose {@link RemoteSailingServerReference#getName() name} equals
* <code>name</code>, it is returned. Otherwise, <code>null</code> is returned.
* If this set has a {@link RemoteSailingServerReference} whose {@link RemoteSailingServerReference#getName() name}
* equals <code>name</code>, it is returned. Otherwise, <code>null</code> is returned.
*/
public RemoteSailingServerReference getServerReferenceByName(String name) {
return remoteSailingServers.get(name);
}
private void triggerAsynchronousEventCacheUpdate(final RemoteSailingServerReference ref) {
new Thread("Event Cache Updater for remote server "+ref) {
@Override public void run() { updateRemoteServerEventCacheSynchronously(ref); }
}.start();
new Thread(() -> updateRemoteServerEventCacheSynchronously(ref), "Event Cache Updater for remote server " + ref).start();
}
private Util.Pair<Iterable<EventBase>, Exception> updateRemoteServerEventCacheSynchronously(RemoteSailingServerReference ref) {
private Util.Pair<Iterable<EventBase>, Exception> updateRemoteServerEventCacheSynchronously(
RemoteSailingServerReference ref) {
BufferedReader bufferedReader = null;
Util.Pair<Iterable<EventBase>, Exception> result;
try {
try {
final URL eventsURL = getEventsURL(ref.getURL());
logger.fine("Updating events for remote server "+ref+" from URL "+eventsURL);
logger.fine("Updating events for remote server " + ref + " from URL " + eventsURL);
URLConnection urlConnection = HttpUrlConnectionHelper.redirectConnection(eventsURL);
bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "UTF-8"));
JSONParser parser = new JSONParser();
Object eventsAsObject = parser.parse(bufferedReader);
EventBaseJsonDeserializer deserializer = new EventBaseJsonDeserializer(new VenueJsonDeserializer(
new CourseAreaJsonDeserializer(DomainFactory.INSTANCE)), new LeaderboardGroupBaseJsonDeserializer());
EventBaseJsonDeserializer deserializer = new EventBaseJsonDeserializer(
new VenueJsonDeserializer(new CourseAreaJsonDeserializer(DomainFactory.INSTANCE)),
new LeaderboardGroupBaseJsonDeserializer());
JSONArray eventsAsJsonArray = (JSONArray) eventsAsObject;
final Set<EventBase> events = new HashSet<>();
for (Object eventAsObject : eventsAsJsonArray) {
@@ -123,13 +136,22 @@ public class RemoteSailingServerSet {
}
}
} catch (IOException | ParseException e) {
logger.log(Level.INFO, "Exception trying to fetch events from remote server "+ref+": "+e.getMessage(), e);
logger.log(Level.INFO, "Exception trying to fetch events from remote server " + ref + ": " + e.getMessage(),
e);
result = new Util.Pair<Iterable<EventBase>, Exception>(/* events */ null, e);
}
cachedEventsForRemoteSailingServers.put(ref, result);
final Pair<Iterable<EventBase>, Exception> finalResult = result;
LockUtil.executeWithWriteLock(lock, () -> {
// check that the server was not removed while no lock was held
if (remoteSailingServers.containsValue(ref)) {
cachedEventsForRemoteSailingServers.put(ref, finalResult);
} else {
logger.fine("Omitted update for " + ref + " as it was removed");
}
});
return result;
}
private URL getEventsURL(URL remoteServerBaseURL) throws MalformedURLException {
String getEventsUrl = remoteServerBaseURL.toExternalForm();
if (!getEventsUrl.endsWith("/")) {
@@ -140,36 +162,55 @@ public class RemoteSailingServerSet {
}
public Map<RemoteSailingServerReference, Util.Pair<Iterable<EventBase>, Exception>> getCachedEventsForRemoteSailingServers() {
return Collections.unmodifiableMap(cachedEventsForRemoteSailingServers);
LockUtil.lockForRead(lock);
try {
return Collections.unmodifiableMap(cachedEventsForRemoteSailingServers);
} finally {
LockUtil.unlockAfterRead(lock);
}
}
public RemoteSailingServerReference remove(String name) {
RemoteSailingServerReference ref = remoteSailingServers.remove(name);
if (ref != null) {
cachedEventsForRemoteSailingServers.remove(ref);
LockUtil.lockForWrite(lock);
try{
RemoteSailingServerReference ref = remoteSailingServers.remove(name);
if (ref != null) {
cachedEventsForRemoteSailingServers.remove(ref);
}
return ref;
}finally{
LockUtil.unlockAfterWrite(lock);
}
return ref;
}
/**
* Synchronously fetches the latest events list for the remote server reference specified. The
* result is cached. If <code>ref</code> was not yet part of this remote sailing server reference set,
* it is automatically added.
* Synchronously fetches the latest events list for the remote server reference specified. The result is cached. If
* <code>ref</code> was not yet part of this remote sailing server reference set, it is automatically added.
*/
public Util.Pair<Iterable<EventBase>, Exception> getEventsOrException(RemoteSailingServerReference ref) {
if (!remoteSailingServers.containsKey(ref.getName())) {
remoteSailingServers.put(ref.getName(), ref);
LockUtil.lockForWrite(lock);
try {
if (!remoteSailingServers.containsKey(ref.getName())) {
remoteSailingServers.put(ref.getName(), ref);
}
} finally {
LockUtil.unlockAfterWrite(lock);
}
return updateRemoteServerEventCacheSynchronously(ref);
}
public Iterable<RemoteSailingServerReference> getLiveRemoteServerReferences() {
List<RemoteSailingServerReference> result = new ArrayList<>();
for (Map.Entry<RemoteSailingServerReference, Pair<Iterable<EventBase>, Exception>> e : cachedEventsForRemoteSailingServers.entrySet()) {
if (e.getValue().getB() == null) {
// no exception; reference considered live
result.add(e.getKey());
LockUtil.lockForRead(lock);
try {
for (Map.Entry<RemoteSailingServerReference, Pair<Iterable<EventBase>, Exception>> e : cachedEventsForRemoteSailingServers.entrySet()) {
if (e.getValue().getB() == null) {
// no exception; reference considered live
result.add(e.getKey());
}
}
} finally {
LockUtil.unlockAfterRead(lock);
}
return result;
}