Skip to content

Commit 65c92ed

Browse files
watmildontsmock
authored andcommitted
See #24875: Don't use sources without a URL, survive a dead overture catalog
The default source list currently fails to load at all: the overture catalog referenced from sources.json returns 403, and that IOException discarded every default source. The preferences then only show the "Loading" placeholder row, which has an empty URL but still counted as valid, so enabling it stored it as a source and every download failed with "MalformedURLException: no protocol:". * A blank URL is no longer a valid URL (MapWithAIInfo#hasValidUrl) * MapWithAILayerInfo#add ignores sources without a URL; this also drops already-stored broken entries on load * The placeholder row can no longer be enabled/activated * Downloads skip sources without a URL instead of throwing * A failing overture catalog only drops the overture sources
1 parent 8122a5e commit 65c92ed

5 files changed

Lines changed: 44 additions & 4 deletions

File tree

‎src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIDataUtils.java‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ public static DataSet getData(Collection<Bounds> bounds, int maximumDimensions)
172172
public static ForkJoinTask<DataSet> download(ProgressMonitor monitor, Bounds bound, MapWithAIInfo mapWithAIInfo,
173173
int maximumDimensions) {
174174
return ForkJoinTask.adapt(() -> {
175+
if (Utils.isStripEmpty(mapWithAIInfo.getUrlExpanded())) {
176+
Logging.warn("MapWithAI: Skipping source without a URL: {0}", mapWithAIInfo.getName());
177+
return new DataSet();
178+
}
175179
final var downloader = new BoundingBoxMapWithAIDownloader(bound, mapWithAIInfo,
176180
DetectTaskingManagerUtils.hasTaskingManagerLayer());
177181
try {

‎src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAIInfo.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ public List<String> getConflationParameterString() {
485485
* @return {@code true} if this source will have a valid url
486486
*/
487487
public boolean hasValidUrl() {
488-
return this.url != null || (this.isConflated() && this.conflationUrl != null);
488+
return !Utils.isStripEmpty(this.url) || (this.isConflated() && !Utils.isStripEmpty(this.conflationUrl));
489489
}
490490

491491
public String getUrlExpanded() {

‎src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAILayerInfo.java‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -369,14 +369,20 @@ private void updateEsriLayers(@Nonnull final Collection<MapWithAIInfo> layers) {
369369
/**
370370
* Update the overture layers
371371
* @param layers The layers to iterate through and modify
372-
* @throws IOException If something happens while parsing overture layers
373372
*/
374-
private void updateOvertureLayers(@Nonnull final Collection<MapWithAIInfo> layers) throws IOException {
373+
private void updateOvertureLayers(@Nonnull final Collection<MapWithAIInfo> layers) {
375374
final var overtureLayers = new ArrayList<MapWithAIInfo>(4);
376375
for (var layer : layers) {
377376
if (MapWithAIType.OVERTURE == layer.getSourceType()) {
378377
try (var reader = new OvertureSourceReader(layer)) {
378+
reader.setFastFail(this.fastFail);
379379
reader.parse().ifPresent(overtureLayers::addAll);
380+
} catch (IOException e) {
381+
// See #24875: an unavailable catalog should only drop its own layers, not
382+
// every default source.
383+
Logging.warn("MapWithAI: Could not load overture catalog {0}: {1}", layer.getUrl(),
384+
e.getMessage());
385+
Logging.trace(e);
380386
}
381387
}
382388
}
@@ -576,6 +582,12 @@ private static boolean isSimilar(String a, String b) {
576582
* @param info imagery entry to add
577583
*/
578584
public void add(MapWithAIInfo info) {
585+
if (info == null || !info.hasValidUrl()) {
586+
// See #24875: the "Loading" placeholder (or a broken preference entry) must
587+
// never become a source
588+
Logging.warn("MapWithAI: Ignoring source without a URL: {0}", info == null ? null : info.getName());
589+
return;
590+
}
579591
layers.add(info);
580592
this.listeners.fireEvent(l -> l.changeEvent(info));
581593
}

‎src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/MapWithAIProvidersPanel.java‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,10 @@ private static void clickListener(MouseEvent e) {
514514
}
515515
} else if (tr("Enabled").equals(tableName)) {
516516
final var info = MapWithAIDefaultLayerTableModel.getRow(realRow);
517+
if (!info.hasValidUrl()) {
518+
// The "Loading" placeholder
519+
return;
520+
}
517521
final var instance = MapWithAILayerInfo.getInstance();
518522
if (instance.getLayers().contains(info)) {
519523
instance.remove(info);
@@ -823,7 +827,8 @@ public void actionPerformed(ActionEvent e) {
823827
return;
824828
}
825829
final var selected = Arrays.stream(defaultTable.getSelectedRows()).map(defaultTable::convertRowIndexToModel)
826-
.mapToObj(MapWithAIDefaultLayerTableModel::getRow).collect(Collectors.toCollection(ArrayList::new));
830+
.mapToObj(MapWithAIDefaultLayerTableModel::getRow).filter(MapWithAIInfo::hasValidUrl)
831+
.collect(Collectors.toCollection(ArrayList::new));
827832
if (selected.stream().anyMatch(MapWithAILayerTableModel::doesNotContain)) {
828833
final var toAdd = selected.stream().filter(MapWithAILayerTableModel::doesNotContain).toList();
829834
activeTable.getSelectionModel().clearSelection();

‎src/test/unit/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAIInfoTest.java‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
55
import static org.junit.jupiter.api.Assertions.assertEquals;
6+
import static org.junit.jupiter.api.Assertions.assertFalse;
67
import static org.junit.jupiter.api.Assertions.assertNull;
78
import static org.junit.jupiter.api.Assertions.assertSame;
89
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -198,4 +199,22 @@ public List<MapWithAIInfo> getAllDefaultLayers() {
198199
assertSame(info, MapWithAILayerInfo.getInstance().getLayers().get(0));
199200
assertEquals("22", info.getId());
200201
}
202+
203+
/**
204+
* Non-regression test for #24875: a source with an empty url (like the
205+
* "Loading" placeholder in the preferences) must not be usable as a source.
206+
*/
207+
@Test
208+
void testTicket24875EmptyUrl() {
209+
final var placeholder = new MapWithAIInfo("Loading", "");
210+
assertFalse(placeholder.hasValidUrl());
211+
assertFalse(new MapWithAIInfo("Blank", " ").hasValidUrl());
212+
assertFalse(new MapWithAIInfo("Null").hasValidUrl());
213+
assertTrue(new MapWithAIInfo("Real", "https://test.example").hasValidUrl());
214+
215+
final var layerInfo = MapWithAILayerInfo.getInstance();
216+
layerInfo.clear();
217+
layerInfo.add(placeholder);
218+
assertTrue(layerInfo.getLayers().isEmpty());
219+
}
201220
}

0 commit comments

Comments
 (0)