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