Changeset 19145 in josm


Ignore:
Timestamp:
2024-07-16T19:14:08+02:00 (5 months ago)
Author:
taylor.smock
Message:

Fix #23721: Application hangs indefinitely if Bing API unavailable

This adds a progress monitor that the user can cancel if Bing attribution doesn't
load quickly enough. This additionally removes the need for Thread.sleep calls
by using a timer thread.

This requires an update to the JMapViewer dependency.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/imagery/CachedAttributionBingAerialTileSource.java

    r18912 r19145  
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.data.imagery;
     3
     4import static org.openstreetmap.josm.tools.I18n.tr;
    35
    46import java.io.IOException;
     
    810import java.nio.charset.StandardCharsets;
    911import java.util.List;
     12import java.util.Timer;
     13import java.util.TimerTask;
    1014import java.util.concurrent.Callable;
    11 import java.util.concurrent.TimeUnit;
     15import java.util.concurrent.atomic.AtomicBoolean;
     16import java.util.concurrent.atomic.AtomicReference;
    1217
    1318import org.openstreetmap.gui.jmapviewer.tilesources.BingAerialTileSource;
    1419import org.openstreetmap.gui.jmapviewer.tilesources.TileSourceInfo;
     20import org.openstreetmap.josm.gui.progress.ProgressMonitor;
     21import org.openstreetmap.josm.gui.progress.swing.PleaseWaitProgressMonitor;
    1522import org.openstreetmap.josm.gui.util.GuiHelper;
    1623import org.openstreetmap.josm.io.CacheCustomContent;
     
    8188    @Override
    8289    protected Callable<List<Attribution>> getAttributionLoaderCallable() {
     90        final AtomicReference<List<Attribution>> attributions = new AtomicReference<>();
     91        final AtomicBoolean finished = new AtomicBoolean();
    8392        return () -> {
    84             BingAttributionData attributionLoader = new BingAttributionData();
    85             int waitTimeSec = 1;
    86             while (true) {
    87                 try {
    88                     String xml = attributionLoader.updateIfRequiredString();
    89                     List<Attribution> ret = parseAttributionText(new InputSource(new StringReader(xml)));
    90                     if (attributionDownloadedTask != null) {
    91                         GuiHelper.runInEDT(attributionDownloadedTask);
    92                         attributionDownloadedTask = null;
    93                     }
    94                     return ret;
    95                 } catch (IOException ex) {
    96                     Logging.log(Logging.LEVEL_WARN, "Could not connect to Bing API. Will retry in " + waitTimeSec + " seconds.", ex);
    97                     Thread.sleep(TimeUnit.SECONDS.toMillis(waitTimeSec));
    98                     waitTimeSec *= 2;
     93            final PleaseWaitProgressMonitor monitor = new PleaseWaitProgressMonitor();
     94            monitor.beginTask(tr("Attempting to fetch Bing attribution information"), ProgressMonitor.ALL_TICKS);
     95            final Timer timer = new Timer(Thread.currentThread().getName() + "-timer", true);
     96            timer.schedule(new AttributionTimerTask(monitor, timer, 1, attributions, finished), 0);
     97            synchronized (finished) {
     98                while (!finished.get() && !monitor.isCanceled()) {
     99                    finished.wait(1000);
    99100                }
    100101            }
     102            monitor.finishTask();
     103            monitor.close();
     104            return attributions.get();
    101105        };
    102106    }
     107
     108    /**
     109     * A timer task for fetching Bing attribution information
     110     */
     111    private class AttributionTimerTask extends TimerTask {
     112        private final ProgressMonitor monitor;
     113        private final Timer timer;
     114        private final int waitTimeSec;
     115        private final AtomicReference<List<Attribution>> attributions;
     116        private final AtomicBoolean finished;
     117
     118        /**
     119         * Create a new task for fetching Bing attribution data
     120         * @param monitor The monitor to update and use for cancellations
     121         * @param timer The timer thread to add the next task to, if this task failed to fetch the attribution data
     122         * @param waitTimeSec The time this task is waiting in seconds prior to execution
     123         * @param attributions The reference to put attributions in
     124         * @param finished Set to {@code true} when we have successfully fetched attributions <i>or</i> fetching has been cancelled.
     125         */
     126        AttributionTimerTask(ProgressMonitor monitor, Timer timer, int waitTimeSec,
     127                             AtomicReference<List<Attribution>> attributions, AtomicBoolean finished) {
     128            this.monitor = monitor;
     129            this.timer = timer;
     130            this.waitTimeSec = waitTimeSec;
     131            this.attributions = attributions;
     132            this.finished = finished;
     133        }
     134
     135        @Override
     136        public void run() {
     137            BingAttributionData attributionLoader = new BingAttributionData();
     138            try {
     139                String xml = attributionLoader.updateIfRequiredString();
     140                List<Attribution> ret;
     141                try (StringReader sr = new StringReader(xml)) {
     142                    ret = parseAttributionText(new InputSource(sr));
     143                }
     144                if (attributionDownloadedTask != null) {
     145                    GuiHelper.runInEDT(attributionDownloadedTask);
     146                    attributionDownloadedTask = null;
     147                }
     148                this.attributions.set(ret);
     149                this.finished.set(true);
     150            } catch (IOException ex) {
     151                final String message = tr("Could not connect to Bing API. Will retry in {0} seconds.", waitTimeSec);
     152                Logging.log(Logging.LEVEL_WARN, message, ex);
     153                if (this.monitor.isCanceled()) {
     154                    this.finished.set(true);
     155                    return;
     156                }
     157                this.monitor.setCustomText(message);
     158                this.monitor.worked(1);
     159                final int newWaitTimeSec = 2 * this.waitTimeSec;
     160                this.timer.schedule(new AttributionTimerTask(this.monitor, this.timer, newWaitTimeSec, this.attributions, this.finished),
     161                        newWaitTimeSec * 1000L);
     162            } finally {
     163                synchronized (this.finished) {
     164                    this.finished.notifyAll();
     165                }
     166            }
     167        }
     168    }
    103169}
Note: See TracChangeset for help on using the changeset viewer.