001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.imagery;
003
004import java.io.IOException;
005import java.io.StringReader;
006import java.net.MalformedURLException;
007import java.net.URL;
008import java.nio.charset.StandardCharsets;
009import java.util.List;
010import java.util.concurrent.Callable;
011import java.util.concurrent.TimeUnit;
012
013import org.openstreetmap.gui.jmapviewer.tilesources.BingAerialTileSource;
014import org.openstreetmap.gui.jmapviewer.tilesources.TileSourceInfo;
015import org.openstreetmap.josm.gui.util.GuiHelper;
016import org.openstreetmap.josm.io.CacheCustomContent;
017import org.openstreetmap.josm.io.OnlineResource;
018import org.openstreetmap.josm.tools.HttpClient;
019import org.openstreetmap.josm.tools.Logging;
020import org.xml.sax.InputSource;
021
022/**
023 * Bing TileSource with cached attribution
024 *
025 * @author Wiktor Niesiobędzki
026 * @since 8526
027 */
028public class CachedAttributionBingAerialTileSource extends BingAerialTileSource {
029    private Runnable attributionDownloadedTask;
030
031    /**
032     * Creates tile source
033     * @param info ImageryInfo description of this tile source
034     */
035    public CachedAttributionBingAerialTileSource(ImageryInfo info) {
036        super(info);
037    }
038
039    /**
040     * Creates tile source
041     * @param info ImageryInfo description of this tile source
042     * @param attributionDownloadedTask runnable to be executed once attribution is loaded
043     */
044
045    public CachedAttributionBingAerialTileSource(TileSourceInfo info, Runnable attributionDownloadedTask) {
046        super(info);
047        this.attributionDownloadedTask = attributionDownloadedTask;
048    }
049
050    class BingAttributionData extends CacheCustomContent<IOException> {
051
052        BingAttributionData() {
053            super("bing.attribution.xml", CacheCustomContent.INTERVAL_HOURLY);
054        }
055
056        @Override
057        protected byte[] updateData() throws IOException {
058            URL u = getAttributionUrl();
059            final String r = HttpClient.create(u).connect().fetchContent();
060            Logging.info("Successfully loaded Bing attribution data.");
061            return r.getBytes(StandardCharsets.UTF_8);
062        }
063
064        @Override
065        protected void checkOfflineAccess() {
066            try {
067                String attributionUrl = getAttributionUrl().toExternalForm();
068                OnlineResource.ALL.checkOfflineAccess(attributionUrl, attributionUrl);
069            } catch (MalformedURLException e) {
070                Logging.error(e);
071            }
072        }
073    }
074
075    @Override
076    protected Callable<List<Attribution>> getAttributionLoaderCallable() {
077        return () -> {
078            BingAttributionData attributionLoader = new BingAttributionData();
079            int waitTimeSec = 1;
080            while (true) {
081                try {
082                    String xml = attributionLoader.updateIfRequiredString();
083                    List<Attribution> ret = parseAttributionText(new InputSource(new StringReader(xml)));
084                    if (attributionDownloadedTask != null) {
085                        GuiHelper.runInEDT(attributionDownloadedTask);
086                        attributionDownloadedTask = null;
087                    }
088                    return ret;
089                } catch (IOException ex) {
090                    Logging.log(Logging.LEVEL_WARN, "Could not connect to Bing API. Will retry in " + waitTimeSec + " seconds.", ex);
091                    Thread.sleep(TimeUnit.SECONDS.toMillis(waitTimeSec));
092                    waitTimeSec *= 2;
093                }
094            }
095        };
096    }
097}