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 | |