diff --git a/src/org/openstreetmap/gui/jmapviewer/tilesources/BingAerialTileSource.java b/src/org/openstreetmap/gui/jmapviewer/tilesources/BingAerialTileSource.java
index 47447f9..029a445 100644
|
a
|
b
|
|
| 9 | 9 | import java.util.ArrayList; |
| 10 | 10 | import java.util.List; |
| 11 | 11 | import java.util.Locale; |
| 12 | | import java.util.concurrent.Callable; |
| 13 | | import java.util.concurrent.ExecutionException; |
| 14 | | import java.util.concurrent.Future; |
| 15 | | import java.util.concurrent.FutureTask; |
| | 12 | import java.util.concurrent.ExecutorService; |
| | 13 | import java.util.concurrent.CompletableFuture; |
| 16 | 14 | import java.util.concurrent.TimeUnit; |
| | 15 | import java.util.function.Supplier; |
| | 16 | import java.util.logging.Level; |
| | 17 | import java.util.logging.Logger; |
| 17 | 18 | import java.util.regex.Pattern; |
| 18 | 19 | |
| 19 | 20 | import javax.imageio.ImageIO; |
| … |
… |
|
| 43 | 44 | public class BingAerialTileSource extends TMSTileSource { |
| 44 | 45 | |
| 45 | 46 | private static final String API_KEY = "Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU"; |
| 46 | | private static volatile Future<List<Attribution>> attributions; // volatile is required for getAttribution(), see below. |
| | 47 | private static volatile List<Attribution> attributions; // volatile is required for getAttribution(), see below. |
| 47 | 48 | private static String imageUrlTemplate; |
| 48 | 49 | private static Integer imageryZoomMax; |
| 49 | 50 | private static String[] subdomains; |
| … |
… |
|
| 52 | 53 | private static final Pattern quadkeyPattern = Pattern.compile("\\{quadkey\\}"); |
| 53 | 54 | private static final Pattern culturePattern = Pattern.compile("\\{culture\\}"); |
| 54 | 55 | private String brandLogoUri; |
| | 56 | private ExecutorService executor; |
| 55 | 57 | |
| 56 | 58 | /** |
| 57 | 59 | * Constructs a new {@code BingAerialTileSource}. |
| … |
… |
public String getTermsOfUseURL() {
|
| 234 | 236 | return "http://opengeodata.org/microsoft-imagery-details"; |
| 235 | 237 | } |
| 236 | 238 | |
| 237 | | protected Callable<List<Attribution>> getAttributionLoaderCallable() { |
| 238 | | return new Callable<List<Attribution>>() { |
| 239 | | |
| 240 | | @Override |
| 241 | | public List<Attribution> call() throws Exception { |
| 242 | | int waitTimeSec = 1; |
| 243 | | while (true) { |
| 244 | | try { |
| 245 | | InputSource xml = new InputSource(getAttributionUrl().openStream()); |
| 246 | | List<Attribution> r = parseAttributionText(xml); |
| 247 | | return r; |
| 248 | | } catch (IOException ex) { |
| 249 | | System.err.println("Could not connect to Bing API. Will retry in " + waitTimeSec + " seconds."); |
| 250 | | Thread.sleep(TimeUnit.SECONDS.toMillis(waitTimeSec)); |
| 251 | | waitTimeSec *= 2; |
| 252 | | } |
| 253 | | } |
| 254 | | } |
| 255 | | }; |
| 256 | | } |
| 257 | | |
| 258 | 239 | protected List<Attribution> getAttribution() { |
| 259 | 240 | if (attributions == null) { |
| 260 | 241 | // see http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html |
| 261 | 242 | synchronized (BingAerialTileSource.class) { |
| 262 | 243 | if (attributions == null) { |
| 263 | | final FutureTask<List<Attribution>> loader = new FutureTask<>(getAttributionLoaderCallable()); |
| 264 | | new Thread(loader, "bing-attribution-loader").start(); |
| 265 | | attributions = loader; |
| | 244 | final Supplier<List<Attribution>> loader = new Supplier<List<Attribution>>() { |
| | 245 | |
| | 246 | @Override |
| | 247 | public List<Attribution> get() { |
| | 248 | int waitTimeSec = 1; |
| | 249 | while (true) { |
| | 250 | try { |
| | 251 | InputSource xml = new InputSource(getAttributionUrl().openStream()); |
| | 252 | List<Attribution> r = parseAttributionText(xml); |
| | 253 | return r; |
| | 254 | } catch (IOException ex) { |
| | 255 | System.err.println("Could not connect to Bing API. Will retry in " + waitTimeSec + " seconds."); |
| | 256 | try { |
| | 257 | Thread.sleep(TimeUnit.SECONDS.toMillis(waitTimeSec)); |
| | 258 | } catch (InterruptedException ex1) { |
| | 259 | Logger.getLogger(BingAerialTileSource.class.getName()).log(Level.SEVERE, null, ex1); |
| | 260 | } |
| | 261 | waitTimeSec *= 2; |
| | 262 | } |
| | 263 | } |
| | 264 | } |
| | 265 | }; |
| | 266 | CompletableFuture.supplyAsync(loader, executor).thenAccept(attr -> attributions = attr); |
| 266 | 267 | } |
| 267 | 268 | } |
| 268 | 269 | } |
| 269 | | try { |
| 270 | | return attributions.get(); |
| 271 | | } catch (ExecutionException ex) { |
| 272 | | throw new RuntimeException(ex.getCause()); |
| 273 | | } catch (InterruptedException ign) { |
| 274 | | System.err.println("InterruptedException: " + ign.getMessage()); |
| 275 | | } |
| 276 | 270 | return null; |
| 277 | 271 | } |
| 278 | 272 | |