diff --git src/org/openstreetmap/gui/jmapviewer/Tile.java src/org/openstreetmap/gui/jmapviewer/Tile.java
index 82ab19e..edd6554 100644
|
|
|
public class Tile {
|
| 48 | 48 | protected int zoom; |
| 49 | 49 | protected BufferedImage image; |
| 50 | 50 | protected String key; |
| 51 | | protected boolean loaded = false; |
| 52 | | protected boolean loading = false; |
| 53 | | protected boolean error = false; |
| | 51 | protected volatile boolean loaded = false; // field accessed by multiple threads without any monitors, needs to be volatile |
| | 52 | protected volatile boolean loading = false; |
| | 53 | protected volatile boolean error = false; |
| 54 | 54 | protected String error_message; |
| 55 | 55 | |
| 56 | 56 | /** TileLoader-specific tile metadata */ |
diff --git src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java
index 15d4f45..b4a0fbd 100644
|
|
|
import java.util.logging.Logger;
|
| 25 | 25 | import org.apache.commons.jcs.access.behavior.ICacheAccess; |
| 26 | 26 | import org.apache.commons.jcs.engine.behavior.ICacheElement; |
| 27 | 27 | import org.openstreetmap.gui.jmapviewer.FeatureAdapter; |
| | 28 | import org.openstreetmap.josm.Main; |
| 28 | 29 | import org.openstreetmap.josm.data.cache.ICachedLoaderListener.LoadResult; |
| 29 | 30 | import org.openstreetmap.josm.data.preferences.IntegerProperty; |
| 30 | 31 | |
| … |
… |
public abstract class JCSCachedTileLoaderJob<K, V extends CacheEntry> implements
|
| 282 | 283 | } |
| 283 | 284 | } catch (Exception e) { |
| 284 | 285 | log.log(Level.WARNING, "JCS - Error while loading object from cache: {0}; {1}", new Object[]{e.getMessage(), getUrl()}); |
| 285 | | log.log(Level.FINE, "Stacktrace", e); |
| | 286 | Main.warn(e); |
| 286 | 287 | for (ICachedLoaderListener l: listeners) { |
| 287 | 288 | l.loadingFinished(cacheData, LoadResult.FAILURE); |
| 288 | 289 | } |
| … |
… |
public abstract class JCSCachedTileLoaderJob<K, V extends CacheEntry> implements
|
| 381 | 382 | cache.put(getCacheKey(), createCacheEntry(new byte[]{}), attributes); |
| 382 | 383 | return handleNotFound(); |
| 383 | 384 | } catch (Exception e) { |
| 384 | | log.log(Level.WARNING, "JCS - Exception during download " + getUrl(), e); |
| | 385 | log.log(Level.WARNING, "JCS - Exception during download {0}", getUrl()); |
| | 386 | Main.warn(e); |
| 385 | 387 | } |
| 386 | 388 | log.log(Level.WARNING, "JCS - Silent failure during download: {0}", getUrl()); |
| 387 | 389 | return false; |
diff --git src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java
index e0fd382..a627c90 100644
|
|
|
package org.openstreetmap.josm.data.imagery;
|
| 4 | 4 | import java.io.ByteArrayInputStream; |
| 5 | 5 | import java.io.IOException; |
| 6 | 6 | import java.net.URL; |
| | 7 | import java.util.HashSet; |
| 7 | 8 | import java.util.Map; |
| | 9 | import java.util.Set; |
| 8 | 10 | import java.util.concurrent.ConcurrentHashMap; |
| | 11 | import java.util.concurrent.ConcurrentMap; |
| 9 | 12 | import java.util.concurrent.Executor; |
| 10 | 13 | import java.util.concurrent.Semaphore; |
| 11 | 14 | import java.util.concurrent.ThreadPoolExecutor; |
| … |
… |
import org.openstreetmap.josm.data.preferences.IntegerProperty;
|
| 36 | 39 | public class TMSCachedTileLoaderJob extends JCSCachedTileLoaderJob<String, BufferedImageCacheEntry> implements TileJob, ICachedLoaderListener { |
| 37 | 40 | private static final Logger log = FeatureAdapter.getLogger(TMSCachedTileLoaderJob.class.getCanonicalName()); |
| 38 | 41 | private Tile tile; |
| 39 | | private TileLoaderListener listener; |
| 40 | 42 | private volatile URL url; |
| 41 | 43 | |
| | 44 | // we need another deduplication of Tile Loader listeners, as for each submit, new TMSCachedTileLoaderJob was created |
| | 45 | // that way, we reduce calls to tileLoadingFinished, and general CPU load due to surplus Map repaints |
| | 46 | private static final ConcurrentMap<String,Set<TileLoaderListener>> inProgress = new ConcurrentHashMap<>(); |
| | 47 | |
| 42 | 48 | /** |
| 43 | 49 | * Limit definition for per host concurrent connections |
| 44 | 50 | */ |
| … |
… |
public class TMSCachedTileLoaderJob extends JCSCachedTileLoaderJob<String, Buffe
|
| 132 | 138 | Map<String, String> headers) { |
| 133 | 139 | super(cache, connectTimeout, readTimeout, headers); |
| 134 | 140 | this.tile = tile; |
| 135 | | this.listener = listener; |
| | 141 | if (listener != null) { |
| | 142 | String deduplicationKey = getCacheKey(); |
| | 143 | synchronized (inProgress) { |
| | 144 | Set<TileLoaderListener> newListeners = inProgress.get(deduplicationKey); |
| | 145 | if (newListeners == null) { |
| | 146 | newListeners = new HashSet<>(); |
| | 147 | inProgress.put(deduplicationKey, newListeners); |
| | 148 | } |
| | 149 | newListeners.add(listener); |
| | 150 | } |
| | 151 | } |
| 136 | 152 | } |
| 137 | 153 | |
| 138 | 154 | @Override |
| … |
… |
public class TMSCachedTileLoaderJob extends JCSCachedTileLoaderJob<String, Buffe
|
| 226 | 242 | |
| 227 | 243 | @Override |
| 228 | 244 | public void loadingFinished(CacheEntry object, LoadResult result) { |
| | 245 | Set<TileLoaderListener> listeners; |
| | 246 | synchronized (inProgress) { |
| | 247 | listeners = inProgress.remove(getCacheKey()); |
| | 248 | } |
| | 249 | |
| 229 | 250 | try { |
| 230 | | tile.finishLoading(); // whatever happened set that loading has finished |
| 231 | | switch(result){ |
| 232 | | case FAILURE: |
| 233 | | tile.setError("Problem loading tile"); |
| 234 | | // no break intentional here |
| 235 | | case SUCCESS: |
| 236 | | handleNoTileAtZoom(); |
| 237 | | if (object != null) { |
| 238 | | byte[] content = object.getContent(); |
| 239 | | if (content != null && content.length > 0) { |
| 240 | | tile.loadImage(new ByteArrayInputStream(content)); |
| | 251 | if(!tile.isLoaded()) { //if someone else already loaded tile, skip all the handling |
| | 252 | tile.finishLoading(); // whatever happened set that loading has finished |
| | 253 | switch(result){ |
| | 254 | case FAILURE: |
| | 255 | tile.setError("Problem loading tile"); |
| | 256 | // no break intentional here |
| | 257 | case SUCCESS: |
| | 258 | handleNoTileAtZoom(); |
| | 259 | if (object != null) { |
| | 260 | byte[] content = object.getContent(); |
| | 261 | if (content != null && content.length > 0) { |
| | 262 | tile.loadImage(new ByteArrayInputStream(content)); |
| | 263 | } |
| 241 | 264 | } |
| | 265 | // no break intentional here |
| | 266 | case REJECTED: |
| | 267 | // do nothing |
| 242 | 268 | } |
| 243 | | // no break intentional here |
| 244 | | case REJECTED: |
| 245 | | // do nothing |
| 246 | 269 | } |
| 247 | | if (listener != null) { |
| 248 | | listener.tileLoadingFinished(tile, result.equals(LoadResult.SUCCESS)); |
| | 270 | |
| | 271 | // always check, if there is some listener interested in fact, that tile has finished loading |
| | 272 | if (listeners != null) { // listeners might be null, if some other thread notified already about success |
| | 273 | for(TileLoaderListener l: listeners) { |
| | 274 | l.tileLoadingFinished(tile, result.equals(LoadResult.SUCCESS)); |
| | 275 | } |
| 249 | 276 | } |
| 250 | 277 | } catch (IOException e) { |
| 251 | 278 | log.log(Level.WARNING, "JCS TMS - error loading object for tile {0}: {1}", new Object[] {tile.getKey(), e.getMessage()}); |
| 252 | 279 | tile.setError(e.getMessage()); |
| 253 | 280 | tile.setLoaded(false); |
| 254 | | if (listener != null) { |
| 255 | | listener.tileLoadingFinished(tile, false); |
| | 281 | if (listeners != null) { // listeners might be null, if some other thread notified already about success |
| | 282 | for(TileLoaderListener l: listeners) { |
| | 283 | l.tileLoadingFinished(tile, false); |
| | 284 | } |
| 256 | 285 | } |
| 257 | 286 | } |
| 258 | 287 | } |