diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RemoteSailingServerSet.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RemoteSailingServerSet.java index f9993be852e..915554f9f8e 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RemoteSailingServerSet.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RemoteSailingServerSet.java @@ -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 remoteSailingServers; - + private final ConcurrentMap, Exception>> cachedEventsForRemoteSailingServers; /** @@ -62,53 +65,79 @@ 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, new Runnable() { + @Override + public void run() { + remoteSailingServers.clear(); + cachedEventsForRemoteSailingServers.clear(); + } + }); } public void add(RemoteSailingServerReference remoteSailingServerReference) { - remoteSailingServers.put(remoteSailingServerReference.getName(), remoteSailingServerReference); - triggerAsynchronousEventCacheUpdate(remoteSailingServerReference); + LockUtil.executeWithWriteLock(lock, new Runnable() { + + @Override + public void run() { + remoteSailingServers.put(remoteSailingServerReference.getName(), remoteSailingServerReference); + triggerAsynchronousEventCacheUpdate(remoteSailingServerReference); + } + }); } - + private void updateRemoteSailingServerReferenceEventCaches() { - for (RemoteSailingServerReference ref : remoteSailingServers.values()) { - triggerAsynchronousEventCacheUpdate(ref); - } + LockUtil.executeWithReadLock(lock, new Runnable() { + + @Override + public void run() { + for (RemoteSailingServerReference ref : remoteSailingServers.values()) { + triggerAsynchronousEventCacheUpdate(ref); + } + } + }); } - + /** - * If this set has a {@link RemoteSailingServerReference} whose {@link RemoteSailingServerReference#getName() name} equals - * name, it is returned. Otherwise, null is returned. + * If this set has a {@link RemoteSailingServerReference} whose {@link RemoteSailingServerReference#getName() name} + * equals name, it is returned. Otherwise, null 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); } + new Thread("Event Cache Updater for remote server " + ref) { + @Override + public void run() { + updateRemoteServerEventCacheSynchronously(ref); + } }.start(); } - private Util.Pair, Exception> updateRemoteServerEventCacheSynchronously(RemoteSailingServerReference ref) { + private Util.Pair, Exception> updateRemoteServerEventCacheSynchronously( + RemoteSailingServerReference ref) { BufferedReader bufferedReader = null; Util.Pair, 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 events = new HashSet<>(); for (Object eventAsObject : eventsAsJsonArray) { @@ -123,13 +152,28 @@ 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, Exception>(/* events */ null, e); } - cachedEventsForRemoteSailingServers.put(ref, result); + + final Pair, Exception> finalResult = result; + + LockUtil.executeWithWriteLock(lock, new Runnable() { + + @Override + public void run() { + // 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 +184,56 @@ public class RemoteSailingServerSet { } public Map, 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 ref 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 + * ref was not yet part of this remote sailing server reference set, it is automatically added. */ public Util.Pair, 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 getLiveRemoteServerReferences() { List result = new ArrayList<>(); - for (Map.Entry, 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, Exception>> e : cachedEventsForRemoteSailingServers + .entrySet()) { + if (e.getValue().getB() == null) { + // no exception; reference considered live + result.add(e.getKey()); + } } + } finally { + LockUtil.unlockAfterRead(lock); } return result; }