kopia lustrzana https://github.com/JOSM/MapWithAI
MapWithAILayerInfo: Don't block JOSM startup
Signed-off-by: Taylor Smock <tsmock@fb.com>pull/1/head
rodzic
64ff82db04
commit
ed6b8bcd5d
|
@ -24,6 +24,7 @@ import org.openstreetmap.josm.gui.MapFrame;
|
|||
import org.openstreetmap.josm.gui.download.DownloadDialog;
|
||||
import org.openstreetmap.josm.gui.download.OSMDownloadSource;
|
||||
import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
|
||||
import org.openstreetmap.josm.gui.util.GuiHelper;
|
||||
import org.openstreetmap.josm.io.remotecontrol.RequestProcessor;
|
||||
import org.openstreetmap.josm.plugins.Plugin;
|
||||
import org.openstreetmap.josm.plugins.PluginInformation;
|
||||
|
@ -35,6 +36,7 @@ import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIObject;
|
|||
import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIRemoteControl;
|
||||
import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIUploadHook;
|
||||
import org.openstreetmap.josm.plugins.mapwithai.backend.MergeDuplicateWaysAction;
|
||||
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAILayerInfo;
|
||||
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.PreConflatedDataUtils;
|
||||
import org.openstreetmap.josm.plugins.mapwithai.data.validation.tests.ConnectingNodeInformationTest;
|
||||
import org.openstreetmap.josm.plugins.mapwithai.data.validation.tests.RoutingIslandsTest;
|
||||
|
@ -118,6 +120,9 @@ public final class MapWithAIPlugin extends Plugin implements Destroyable {
|
|||
MapPaintUtils.addMapWithAIPaintStyles();
|
||||
|
||||
destroyables = new ArrayList<>();
|
||||
// Run in EDT to avoid blocking (has to be run before MapWithAIDownloadOptions
|
||||
// so its already initialized)
|
||||
GuiHelper.runInEDT(MapWithAILayerInfo::getInstance);
|
||||
MapWithAIDownloadOptions mapWithAIDownloadOptions = new MapWithAIDownloadOptions();
|
||||
mapWithAIDownloadOptions.addGui(DownloadDialog.getInstance());
|
||||
destroyables.add(mapWithAIDownloadOptions);
|
||||
|
|
|
@ -16,18 +16,18 @@ import java.util.Objects;
|
|||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import org.openstreetmap.josm.actions.ExpertToggleAction;
|
||||
import org.openstreetmap.josm.data.StructUtils;
|
||||
import org.openstreetmap.josm.data.imagery.ImageryInfo;
|
||||
import org.openstreetmap.josm.data.preferences.BooleanProperty;
|
||||
import org.openstreetmap.josm.gui.MainApplication;
|
||||
import org.openstreetmap.josm.gui.PleaseWaitRunnable;
|
||||
import org.openstreetmap.josm.gui.util.GuiHelper;
|
||||
import org.openstreetmap.josm.io.CachedFile;
|
||||
import org.openstreetmap.josm.io.NetworkManager;
|
||||
import org.openstreetmap.josm.io.imagery.ImageryReader;
|
||||
|
@ -48,6 +48,8 @@ public class MapWithAILayerInfo {
|
|||
*/
|
||||
public static final BooleanProperty SHOW_PREVIEW = new BooleanProperty("mapwithai.sources.preview", false);
|
||||
|
||||
/** Finish listeners */
|
||||
private ListenerList<FinishListener> finishListenerListenerList = ListenerList.create();
|
||||
/** List of all usable layers */
|
||||
private final List<MapWithAIInfo> layers = Collections.synchronizedList(new ArrayList<>());
|
||||
/** List of layer ids of all usable layers */
|
||||
|
@ -70,23 +72,29 @@ public class MapWithAILayerInfo {
|
|||
private static MapWithAILayerInfo instance;
|
||||
|
||||
public static MapWithAILayerInfo getInstance() {
|
||||
AtomicBoolean finished;
|
||||
final AtomicBoolean finished = new AtomicBoolean();
|
||||
synchronized (DEFAULT_LAYER_SITES) {
|
||||
if (instance == null) {
|
||||
finished = new AtomicBoolean();
|
||||
instance = new MapWithAILayerInfo(() -> finished.set(true));
|
||||
instance = new MapWithAILayerInfo(() -> {
|
||||
synchronized (finished) {
|
||||
finished.set(true);
|
||||
finished.notifyAll();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
finished = null;
|
||||
finished.set(true);
|
||||
}
|
||||
}
|
||||
// Avoid a deadlock in the EDT.
|
||||
if (finished != null && !SwingUtilities.isEventDispatchThread()) {
|
||||
while (!finished.get()) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
Logging.error(e);
|
||||
Thread.currentThread().interrupt();
|
||||
if (!finished.get() && !SwingUtilities.isEventDispatchThread()) {
|
||||
synchronized (finished) {
|
||||
while (!finished.get()) {
|
||||
try {
|
||||
finished.wait();
|
||||
} catch (InterruptedException e) {
|
||||
Logging.error(e);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -169,7 +177,12 @@ public class MapWithAILayerInfo {
|
|||
layers.removeIf(i -> i.getUrl().contains("localhost:8111"));
|
||||
Collections.sort(layers);
|
||||
}
|
||||
loadDefaults(false, MainApplication.worker, fastFail, listener);
|
||||
// Ensure that the cache is initialized prior to running in the fork join pool
|
||||
// on webstart
|
||||
if (System.getSecurityManager() != null) {
|
||||
ESRISourceReader.SOURCE_CACHE.getClass();
|
||||
}
|
||||
loadDefaults(false, ForkJoinPool.commonPool(), fastFail, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -188,18 +201,53 @@ public class MapWithAILayerInfo {
|
|||
* @since 12634
|
||||
*/
|
||||
public void loadDefaults(boolean clearCache, ExecutorService worker, boolean fastFail, FinishListener listener) {
|
||||
final DefaultEntryLoader loader = new DefaultEntryLoader(clearCache, fastFail, listener);
|
||||
final DefaultEntryLoader loader = new DefaultEntryLoader(clearCache, fastFail);
|
||||
if (this.finishListenerListenerList == null) {
|
||||
this.finishListenerListenerList = ListenerList.create();
|
||||
}
|
||||
if (listener != null) {
|
||||
this.finishListenerListenerList.addListener(listener);
|
||||
}
|
||||
if (worker == null) {
|
||||
loader.realRun();
|
||||
PleaseWaitRunnable pleaseWaitRunnable = new PleaseWaitRunnable(tr("Update default entries")) {
|
||||
@Override
|
||||
protected void cancel() {
|
||||
loader.canceled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void realRun() {
|
||||
loader.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finish() {
|
||||
loader.finish();
|
||||
}
|
||||
};
|
||||
pleaseWaitRunnable.run();
|
||||
} else {
|
||||
worker.execute(loader);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a listener for when the data finishes updating
|
||||
*
|
||||
* @param finishListener The listener
|
||||
*/
|
||||
public void addFinishListener(final FinishListener finishListener) {
|
||||
if (this.finishListenerListenerList == null) {
|
||||
finishListener.onFinish();
|
||||
} else {
|
||||
this.finishListenerListenerList.addListener(finishListener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loader/updater of the available imagery entries
|
||||
*/
|
||||
class DefaultEntryLoader extends PleaseWaitRunnable {
|
||||
class DefaultEntryLoader implements Runnable {
|
||||
|
||||
private final boolean clearCache;
|
||||
private final boolean fastFail;
|
||||
|
@ -207,30 +255,28 @@ public class MapWithAILayerInfo {
|
|||
private MapWithAISourceReader reader;
|
||||
private boolean canceled;
|
||||
private boolean loadError;
|
||||
private final FinishListener listener;
|
||||
|
||||
DefaultEntryLoader(boolean clearCache, boolean fastFail, FinishListener listener) {
|
||||
super(tr("Update default entries"));
|
||||
DefaultEntryLoader(boolean clearCache, boolean fastFail) {
|
||||
this.clearCache = clearCache;
|
||||
this.fastFail = fastFail;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cancel() {
|
||||
canceled = true;
|
||||
Utils.close(reader);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void realRun() {
|
||||
public void run() {
|
||||
if (this.clearCache) {
|
||||
ESRISourceReader.SOURCE_CACHE.clear();
|
||||
}
|
||||
for (String source : getImageryLayersSites()) {
|
||||
if (canceled) {
|
||||
return;
|
||||
}
|
||||
loadSource(source);
|
||||
}
|
||||
GuiHelper.runInEDT(this::finish);
|
||||
this.finish();
|
||||
}
|
||||
|
||||
protected void loadSource(String source) {
|
||||
|
@ -242,6 +288,9 @@ public class MapWithAILayerInfo {
|
|||
reader = new MapWithAISourceReader(source);
|
||||
reader.setFastFail(fastFail);
|
||||
Collection<MapWithAIInfo> result = reader.parse();
|
||||
// This is called here to "pre-cache" the layer information, to avoid blocking
|
||||
// the EDT
|
||||
this.updateEsriLayers(result);
|
||||
newLayers.addAll(result);
|
||||
} catch (IOException ex) {
|
||||
loadError = true;
|
||||
|
@ -249,18 +298,26 @@ public class MapWithAILayerInfo {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finish() {
|
||||
defaultLayers.clear();
|
||||
allDefaultLayers.clear();
|
||||
defaultLayers.addAll(newLayers);
|
||||
for (MapWithAIInfo layer : newLayers) {
|
||||
/**
|
||||
* Update the esri layer information
|
||||
*
|
||||
* @param layers The layers to update
|
||||
*/
|
||||
private void updateEsriLayers(@Nonnull final Collection<MapWithAIInfo> layers) {
|
||||
for (MapWithAIInfo layer : layers) {
|
||||
if (MapWithAIType.ESRI == layer.getSourceType()) {
|
||||
allDefaultLayers.addAll(parseEsri(layer));
|
||||
} else {
|
||||
allDefaultLayers.add(layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void finish() {
|
||||
defaultLayers.clear();
|
||||
allDefaultLayers.clear();
|
||||
defaultLayers.addAll(newLayers);
|
||||
this.updateEsriLayers(newLayers);
|
||||
defaultLayerIds.clear();
|
||||
|
||||
Collections.sort(defaultLayers, new MapWithAIInfo.MapWithAIInfoCategoryComparator());
|
||||
|
@ -271,8 +328,10 @@ public class MapWithAILayerInfo {
|
|||
if (!loadError && !defaultLayerIds.isEmpty()) {
|
||||
dropOldEntries();
|
||||
}
|
||||
if (listener != null) {
|
||||
listener.onFinish();
|
||||
final ListenerList<FinishListener> listenerList = MapWithAILayerInfo.this.finishListenerListenerList;
|
||||
MapWithAILayerInfo.this.finishListenerListenerList = null;
|
||||
if (listenerList != null) {
|
||||
listenerList.fireEvent(FinishListener::onFinish);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,8 +342,8 @@ public class MapWithAILayerInfo {
|
|||
* @return The Feature Servers for the ESRI layer
|
||||
*/
|
||||
private Collection<MapWithAIInfo> parseEsri(MapWithAIInfo layer) {
|
||||
try (ESRISourceReader esriReader = new ESRISourceReader(layer)) {
|
||||
return esriReader.parse();
|
||||
try {
|
||||
return new ESRISourceReader(layer).parse();
|
||||
} catch (IOException e) {
|
||||
Logging.error(e);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import java.util.stream.Stream;
|
|||
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
|
||||
import org.openstreetmap.josm.gui.util.GuiHelper;
|
||||
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAICategory;
|
||||
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo;
|
||||
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAILayerInfo;
|
||||
|
@ -49,6 +50,7 @@ class MapWithAIDefaultLayerTableModel extends DefaultTableModel {
|
|||
columnDataRetrieval.add(i -> i.getAttributionText(0, null, null));
|
||||
columnDataRetrieval.add(info -> Optional.ofNullable(info.getTermsOfUseURL()).orElse(""));
|
||||
columnDataRetrieval.add(i -> MapWithAILayerInfo.getInstance().getLayers().contains(i));
|
||||
MapWithAILayerInfo.getInstance().addFinishListener(() -> GuiHelper.runInEDT(this::fireTableDataChanged));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -58,12 +60,15 @@ class MapWithAIDefaultLayerTableModel extends DefaultTableModel {
|
|||
* @return The imagery info at the given row number
|
||||
*/
|
||||
public static MapWithAIInfo getRow(int row) {
|
||||
if (row == 0 && MapWithAILayerInfo.getInstance().getAllDefaultLayers().isEmpty()) {
|
||||
return new MapWithAIInfo(tr("Loading"), "");
|
||||
}
|
||||
return MapWithAILayerInfo.getInstance().getAllDefaultLayers().get(row);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return MapWithAILayerInfo.getInstance().getAllDefaultLayers().size();
|
||||
return Math.max(MapWithAILayerInfo.getInstance().getAllDefaultLayers().size(), 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -76,7 +81,7 @@ class MapWithAIDefaultLayerTableModel extends DefaultTableModel {
|
|||
|
||||
@Override
|
||||
public Object getValueAt(int row, int column) {
|
||||
MapWithAIInfo info = MapWithAILayerInfo.getInstance().getAllDefaultLayers().get(row);
|
||||
MapWithAIInfo info = getRow(row);
|
||||
if (column < columnDataRetrieval.size()) {
|
||||
return columnDataRetrieval.get(column).apply(info);
|
||||
}
|
||||
|
|
|
@ -190,8 +190,8 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
if (MapWithAILayerTableModel.contains(info)) {
|
||||
Color t = IMAGERY_BACKGROUND_COLOR.get();
|
||||
GuiHelper.setBackgroundReadable(label, isSelected ? t.darker() : t);
|
||||
} else if (this.area != null && (this.area.intersects(info.getBounds())
|
||||
|| (info.getBounds() != null && info.getBounds().intersects(this.area)))) {
|
||||
} else if (this.area != null && info.getBounds() != null
|
||||
&& (this.area.intersects(info.getBounds()) || (info.getBounds().intersects(this.area)))) {
|
||||
Color t = MAPWITHAI_AREA_BACKGROUND_COLOR.get();
|
||||
GuiHelper.setBackgroundReadable(label, isSelected ? t.darker() : t);
|
||||
} else {
|
||||
|
@ -657,7 +657,8 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
info.setSourceType(MapWithAIType.THIRD_PARTY);
|
||||
}
|
||||
if (MapWithAIType.ESRI == info.getSourceType()) {
|
||||
try (ESRISourceReader reader = new ESRISourceReader(info)) {
|
||||
final ESRISourceReader reader = new ESRISourceReader(info);
|
||||
try {
|
||||
for (MapWithAIInfo i : reader.parse()) {
|
||||
activeModel.addRow(i);
|
||||
}
|
||||
|
@ -845,11 +846,11 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
@Override
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
MapWithAILayerInfo.getInstance().loadDefaults(true, MainApplication.worker, false, () -> {
|
||||
defaultModel.fireTableDataChanged();
|
||||
defaultTable.getSelectionModel().clearSelection();
|
||||
defaultTableListener.clearMap();
|
||||
GuiHelper.runInEDT(defaultModel::fireTableDataChanged);
|
||||
GuiHelper.runInEDT(defaultTable.getSelectionModel()::clearSelection);
|
||||
GuiHelper.runInEDT(defaultTableListener::clearMap);
|
||||
/* loading new file may change active layers */
|
||||
activeModel.fireTableDataChanged();
|
||||
GuiHelper.runInEDT(activeModel::fireTableDataChanged);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,23 @@
|
|||
// License: GPL. For details, see LICENSE file.
|
||||
package org.openstreetmap.josm.plugins.mapwithai.io.mapwithai;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.Closeable;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.json.Json;
|
||||
import javax.json.JsonArray;
|
||||
import javax.json.JsonNumber;
|
||||
|
@ -21,23 +26,30 @@ import javax.json.JsonReader;
|
|||
import javax.json.JsonString;
|
||||
import javax.json.JsonStructure;
|
||||
import javax.json.JsonValue;
|
||||
import javax.json.stream.JsonParsingException;
|
||||
|
||||
import org.openstreetmap.josm.data.cache.JCSCacheManager;
|
||||
import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryBounds;
|
||||
import org.openstreetmap.josm.io.CachedFile;
|
||||
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAICategory;
|
||||
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo;
|
||||
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIType;
|
||||
import org.openstreetmap.josm.spi.preferences.Config;
|
||||
import org.openstreetmap.josm.tools.HttpClient;
|
||||
import org.openstreetmap.josm.tools.Logging;
|
||||
import org.openstreetmap.josm.tools.Utils;
|
||||
|
||||
import org.apache.commons.jcs3.access.CacheAccess;
|
||||
import org.apache.commons.jcs3.engine.behavior.IElementAttributes;
|
||||
|
||||
/**
|
||||
* Take a {@link MapWithAIInfo.MapWithAIType#ESRI} layer and convert it to a
|
||||
* list of "true" layers.
|
||||
*/
|
||||
public class ESRISourceReader implements Closeable {
|
||||
public class ESRISourceReader {
|
||||
/** The cache storing ESRI source information (json) */
|
||||
public static final CacheAccess<String, String> SOURCE_CACHE = JCSCacheManager.getCache("mapwithai:esrisources", 5,
|
||||
50_000, new File(Config.getDirs().getCacheDirectory(true), "mapwithai").getPath());
|
||||
private final MapWithAIInfo source;
|
||||
private final List<CachedFile> cachedFiles = new ArrayList<>();
|
||||
private boolean fastFail;
|
||||
private final List<MapWithAICategory> ignoreConflationCategories;
|
||||
private static final String JSON_QUERY_PARAM = "?f=json";
|
||||
|
@ -84,11 +96,13 @@ public class ESRISourceReader implements Closeable {
|
|||
String next = "1";
|
||||
String searchUrl = startReplace.matcher(search).replaceAll(next);
|
||||
while (!next.equals("-1")) {
|
||||
try (CachedFile layers = new CachedFile(url + "content/groups/" + group + searchUrl);
|
||||
BufferedReader i = layers.getContentReader();
|
||||
JsonReader reader = Json.createReader(i)) {
|
||||
cachedFiles.add(layers);
|
||||
layers.setFastFail(fastFail);
|
||||
final String finalUrl = url + "content/groups/" + group + searchUrl;
|
||||
final String jsonString = getJsonString(finalUrl, this.fastFail);
|
||||
if (jsonString == null) {
|
||||
continue;
|
||||
}
|
||||
try (JsonReader reader = Json
|
||||
.createReader(new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8)))) {
|
||||
JsonStructure parser = reader.read();
|
||||
if (parser.getValueType() == JsonValue.ValueType.OBJECT) {
|
||||
JsonObject obj = parser.asJsonObject();
|
||||
|
@ -102,7 +116,7 @@ public class ESRISourceReader implements Closeable {
|
|||
information.add(parse(feature));
|
||||
}
|
||||
}
|
||||
} catch (ClassCastException | IOException e) {
|
||||
} catch (ClassCastException e) {
|
||||
Logging.error(e);
|
||||
next = "-1";
|
||||
}
|
||||
|
@ -115,6 +129,59 @@ public class ESRISourceReader implements Closeable {
|
|||
return information;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the json string for a URL
|
||||
*
|
||||
* @param url The URL to get
|
||||
* @param fastFail Fail fast (1 second)
|
||||
* @return The json string, or {@code null}.
|
||||
*/
|
||||
@Nullable
|
||||
private static String getJsonString(@Nonnull final String url, final boolean fastFail) {
|
||||
String jsonString = SOURCE_CACHE.get(url);
|
||||
// TODO FIXME remove sometime after January 2022 (give it a chance to cleanup
|
||||
// directories)
|
||||
CachedFile.cleanup(url);
|
||||
if (jsonString == null) {
|
||||
synchronized (SOURCE_CACHE) {
|
||||
jsonString = SOURCE_CACHE.get(url);
|
||||
if (jsonString == null) {
|
||||
HttpClient client = null;
|
||||
try {
|
||||
client = HttpClient.create(new URL(url));
|
||||
if (fastFail) {
|
||||
client.setReadTimeout(1000);
|
||||
}
|
||||
final HttpClient.Response response = client.connect();
|
||||
jsonString = response.fetchContent();
|
||||
if (jsonString != null && response.getResponseCode() < 400
|
||||
&& response.getResponseCode() >= 200) {
|
||||
final IElementAttributes elementAttributes = SOURCE_CACHE.getDefaultElementAttributes();
|
||||
// getExpiration returns milliseconds
|
||||
final long expirationTime = response.getExpiration();
|
||||
if (expirationTime > 0) {
|
||||
elementAttributes.setMaxLife(response.getExpiration());
|
||||
} else {
|
||||
elementAttributes.setMaxLife(TimeUnit.SECONDS.toMillis(
|
||||
Config.getPref().getLong("mirror.maxtime", TimeUnit.DAYS.toSeconds(7))));
|
||||
}
|
||||
SOURCE_CACHE.put(url, jsonString, elementAttributes);
|
||||
} else {
|
||||
jsonString = null;
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
Logging.error(e);
|
||||
} finally {
|
||||
if (client != null) {
|
||||
client.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return jsonString;
|
||||
}
|
||||
|
||||
private MapWithAIInfo parse(JsonObject feature) {
|
||||
// Use the initial esri server information to keep conflation info
|
||||
MapWithAIInfo newInfo = new MapWithAIInfo(source);
|
||||
|
@ -168,42 +235,60 @@ public class ESRISourceReader implements Closeable {
|
|||
return (newInfo);
|
||||
}
|
||||
|
||||
private static String featureService(MapWithAIInfo mapwithaiInfo, String url) {
|
||||
String toGet = url.endsWith(JSON_QUERY_PARAM) ? url : url.concat(JSON_QUERY_PARAM);
|
||||
try (CachedFile featureServer = new CachedFile(toGet);
|
||||
BufferedReader br = featureServer.getContentReader();
|
||||
JsonReader reader = Json.createReader(br)) {
|
||||
/**
|
||||
* Get feature service information
|
||||
*
|
||||
* @param mapwithaiInfo The info to update
|
||||
* @param url The url to get
|
||||
* @return The base url for the feature service
|
||||
*/
|
||||
@Nullable
|
||||
private String featureService(@Nonnull MapWithAIInfo mapwithaiInfo, @Nonnull String url) {
|
||||
final String toGet = url.endsWith(JSON_QUERY_PARAM) ? url : url.concat(JSON_QUERY_PARAM);
|
||||
final String jsonString = getJsonString(toGet, this.fastFail);
|
||||
if (jsonString == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try (JsonReader reader = Json
|
||||
.createReader(new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8)))) {
|
||||
JsonObject info = reader.readObject();
|
||||
JsonArray layers = info.getJsonArray("layers");
|
||||
// This fixes #20551
|
||||
if (layers == null || layers.stream().noneMatch(jsonvalue -> jsonvalue != null)) {
|
||||
if (layers == null || layers.stream().noneMatch(Objects::nonNull)) {
|
||||
return null;
|
||||
}
|
||||
// TODO use all the layers?
|
||||
JsonObject layer = layers.stream().filter(jsonValue -> jsonValue != null).findFirst()
|
||||
.orElse(JsonObject.EMPTY_JSON_OBJECT).asJsonObject();
|
||||
JsonObject layer = layers.stream().filter(Objects::nonNull).findFirst().orElse(JsonValue.EMPTY_JSON_OBJECT)
|
||||
.asJsonObject();
|
||||
if (layer.containsKey("id")) {
|
||||
String partialUrl = (url.endsWith("/") ? url : url + "/") + layer.getInt("id");
|
||||
mapwithaiInfo.setReplacementTags(getReplacementTags(partialUrl));
|
||||
|
||||
return partialUrl;
|
||||
}
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
} catch (JsonParsingException e) {
|
||||
Logging.error(e);
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Map<String, String> getReplacementTags(String layerUrl) {
|
||||
/**
|
||||
* Get the replacement tags for a feature service
|
||||
*
|
||||
* @param layerUrl The service url
|
||||
* @return A map of replacement tags
|
||||
*/
|
||||
@Nonnull
|
||||
private Map<String, String> getReplacementTags(@Nonnull String layerUrl) {
|
||||
String toGet = layerUrl.endsWith(JSON_QUERY_PARAM) ? layerUrl : layerUrl.concat(JSON_QUERY_PARAM);
|
||||
try (CachedFile featureServer = new CachedFile(toGet);
|
||||
BufferedReader br = featureServer.getContentReader();
|
||||
JsonReader reader = Json.createReader(br)) {
|
||||
return reader.readObject().getJsonArray("fields").getValuesAs(JsonObject.class).stream()
|
||||
.collect(Collectors.toMap(o -> o.getString("name"), ESRISourceReader::getReplacementTag));
|
||||
} catch (IOException e) {
|
||||
Logging.error(e);
|
||||
final String jsonString = getJsonString(toGet, this.fastFail);
|
||||
if (jsonString != null) {
|
||||
try (JsonReader reader = Json
|
||||
.createReader(new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8)))) {
|
||||
return reader.readObject().getJsonArray("fields").getValuesAs(JsonObject.class).stream()
|
||||
.collect(Collectors.toMap(o -> o.getString("name"), ESRISourceReader::getReplacementTag));
|
||||
}
|
||||
}
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
@ -225,10 +310,4 @@ public class ESRISourceReader implements Closeable {
|
|||
public void setFastFail(boolean fastFail) {
|
||||
this.fastFail = fastFail;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
cachedFiles.forEach(Utils::close);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ import java.io.IOException;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo;
|
||||
|
@ -19,6 +21,16 @@ class ESRISourceReaderTest {
|
|||
@RegisterExtension
|
||||
MapWithAITestRules rule = (MapWithAITestRules) new MapWithAITestRules().wiremock().projection();
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
ESRISourceReader.SOURCE_CACHE.clear();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
this.setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that ESRI servers are properly added
|
||||
*
|
||||
|
@ -33,15 +45,14 @@ class ESRISourceReaderTest {
|
|||
String tUrl = rule.getWireMock().baseUrl() + "/sharing/rest";
|
||||
for (String url : Arrays.asList(tUrl, tUrl + "/")) {
|
||||
info.setUrl(url);
|
||||
try (ESRISourceReader reader = new ESRISourceReader(info)) {
|
||||
Collection<MapWithAIInfo> layers = reader.parse();
|
||||
assertFalse(layers.isEmpty(), "There should be a MapWithAI layer");
|
||||
assertTrue(layers.stream().noneMatch(i -> info.getUrl().equals(i.getUrl())),
|
||||
"The ESRI server should be expanded to feature servers");
|
||||
assertTrue(layers.stream().allMatch(i -> MapWithAIType.ESRI_FEATURE_SERVER.equals(i.getSourceType())),
|
||||
"There should only be ESRI feature servers");
|
||||
assertEquals(24, layers.size());
|
||||
}
|
||||
final ESRISourceReader reader = new ESRISourceReader(info);
|
||||
Collection<MapWithAIInfo> layers = reader.parse();
|
||||
assertFalse(layers.isEmpty(), "There should be a MapWithAI layer");
|
||||
assertTrue(layers.stream().noneMatch(i -> info.getUrl().equals(i.getUrl())),
|
||||
"The ESRI server should be expanded to feature servers");
|
||||
assertTrue(layers.stream().allMatch(i -> MapWithAIType.ESRI_FEATURE_SERVER.equals(i.getSourceType())),
|
||||
"There should only be ESRI feature servers");
|
||||
assertEquals(24, layers.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.awaitility.Awaitility;
|
|||
import org.awaitility.Durations;
|
||||
import org.junit.jupiter.api.extension.InvocationInterceptor.Invocation;
|
||||
import org.junit.runners.model.InitializationError;
|
||||
|
||||
import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
|
||||
import org.openstreetmap.josm.gui.util.GuiHelper;
|
||||
import org.openstreetmap.josm.io.OsmApi;
|
||||
|
|
Ładowanie…
Reference in New Issue