diff --git a/java/com.sap.sailing.domain.windfinderadapter/src/com/sap/sailing/domain/windfinderadapter/impl/ReviewedSpotsCollectionImpl.java b/java/com.sap.sailing.domain.windfinderadapter/src/com/sap/sailing/domain/windfinderadapter/impl/ReviewedSpotsCollectionImpl.java index 1757e423c6c..c2d2a6b8098 100644 --- a/java/com.sap.sailing.domain.windfinderadapter/src/com/sap/sailing/domain/windfinderadapter/impl/ReviewedSpotsCollectionImpl.java +++ b/java/com.sap.sailing.domain.windfinderadapter/src/com/sap/sailing/domain/windfinderadapter/impl/ReviewedSpotsCollectionImpl.java @@ -5,6 +5,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; +import java.util.Collections; import java.util.HashSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -20,6 +21,7 @@ import org.json.simple.parser.ParseException; import com.sap.sailing.domain.windfinder.ReviewedSpotsCollection; import com.sap.sailing.domain.windfinder.Spot; +import com.sap.sse.common.util.BackoffTracker; import com.sap.sse.util.HttpUrlConnectionHelper; import com.sap.sse.util.ThreadPoolUtil; @@ -42,18 +44,17 @@ public class ReviewedSpotsCollectionImpl implements ReviewedSpotsCollection { * map initialized with the result of calling {@link #loadSpots()}. */ private Future> spotsByIdCache; + + private BackoffTracker backoffTracker; public ReviewedSpotsCollectionImpl(String id) { this.id = id; this.parser = new WindFinderReportParser(); - this.spotsByIdCache = ThreadPoolUtil.INSTANCE.getDefaultForegroundTaskThreadPoolExecutor().schedule(()->{ + this.spotsByIdCache = ThreadPoolUtil.INSTANCE.getDefaultForegroundTaskThreadPoolExecutor().schedule(() -> { + backoffTracker = new BackoffTracker(TimeUnit.SECONDS.toMillis(5), 2); final ConcurrentMap result = new ConcurrentHashMap<>(); - try { - for (final Spot spot : loadSpots()) { - result.put(spot.getId(), spot); - } - } catch (Exception e) { - logger.log(Level.SEVERE, "Problem loading spots for spot collection "+id, e); + for (final Spot spot : loadSpots()) { + result.put(spot.getId(), spot); } return result; }, /* delay */ 0, TimeUnit.MILLISECONDS); @@ -103,19 +104,22 @@ public class ReviewedSpotsCollectionImpl implements ReviewedSpotsCollection { return result; } - private Iterable loadSpots() throws IOException, ParseException, MalformedURLException { - final Iterable result; - final InputStreamReader in = new InputStreamReader( - (InputStream) HttpUrlConnectionHelper.redirectConnection( - new URL(Activator.BASE_URL_FOR_JSON_DOCUMENTS+"/"+getId()+SPOT_LIST_DOCUMENT_SUFFIX)) - .getContent()); - try { - JSONArray spotsAsJson = (JSONArray) new JSONParser().parse(in); - result = parser.parseSpots(spotsAsJson, this); - return result; - } finally { - in.close(); + private Iterable loadSpots() { + Iterable result = Collections.emptySet(); + if (!backoffTracker.backOff()) { + try (InputStreamReader in = new InputStreamReader((InputStream) HttpUrlConnectionHelper + .redirectConnection( + new URL(Activator.BASE_URL_FOR_JSON_DOCUMENTS + "/" + getId() + SPOT_LIST_DOCUMENT_SUFFIX)) + .getContent())) { + JSONArray spotsAsJson = (JSONArray) new JSONParser().parse(in); + result = parser.parseSpots(spotsAsJson, this); + backoffTracker.clear(); + } catch (Exception e) { + logger.log(Level.SEVERE, "Problem loading spots for spot collection " + id, e); + backoffTracker.logFailure(); + } } + return result; } @Override diff --git a/java/com.sap.sse.common/src/com/sap/sse/common/util/BackoffTracker.java b/java/com.sap.sse.common/src/com/sap/sse/common/util/BackoffTracker.java new file mode 100644 index 00000000000..a7e2784cdd4 --- /dev/null +++ b/java/com.sap.sse.common/src/com/sap/sse/common/util/BackoffTracker.java @@ -0,0 +1,40 @@ +package com.sap.sse.common.util; + +import java.util.concurrent.TimeUnit; + +public class BackoffTracker { + private Long backOffUntil; + private final int factor; + private Long currentTimeoutInMillis; + private final Long initialTimeoutInMillis; + private static final Long maxTimeout = TimeUnit.MINUTES.toMillis(5); + + /** + * A tracker to handle continous failures of processes. Before every process execution call {@link #backOff()} to query whether there is a timeout. + * Every failure should call {@link #logFailure()}, which will increase the time until {@link #backOff()} returns false again. + * @param initialTimeoutInMillis + * @param backoffMultiplier: The factor by which the timeout will be multiplied on consecutive failures + */ + public BackoffTracker(Long initialTimeoutInMillis, int backoffMultiplier) { + this.initialTimeoutInMillis = initialTimeoutInMillis; + this.factor = backoffMultiplier; + } + + public void logFailure() { + if(currentTimeoutInMillis == null) { + currentTimeoutInMillis = initialTimeoutInMillis; + } + final Long newTimeOut = currentTimeoutInMillis * factor; + currentTimeoutInMillis = newTimeOut >= maxTimeout ? maxTimeout : newTimeOut; + backOffUntil = System.currentTimeMillis() + currentTimeoutInMillis; + } + + public boolean backOff() { + return backOffUntil == null ? false : backOffUntil > System.currentTimeMillis(); + } + + public void clear() { + currentTimeoutInMillis = null; + backOffUntil = null; + } +}