Rework URL handling

Signed-off-by: Taylor Smock <tsmock@fb.com>
pull/1/head
Taylor Smock 2021-11-10 11:24:10 -07:00
rodzic 8c80790127
commit 99f4de5dd4
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 625F6A74A3E4311A
16 zmienionych plików z 258 dodań i 343 usunięć

Wyświetl plik

@ -3,6 +3,9 @@ package org.openstreetmap.josm.plugins.mapwithai;
import static org.openstreetmap.josm.tools.I18n.tr;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
@ -11,9 +14,6 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import org.openstreetmap.josm.actions.JosmAction;
import org.openstreetmap.josm.actions.PreferencesAction;
import org.openstreetmap.josm.data.validation.OsmValidator;
@ -47,6 +47,8 @@ import org.openstreetmap.josm.plugins.mapwithai.gui.MapWithAIMenu;
import org.openstreetmap.josm.plugins.mapwithai.gui.download.MapWithAIDownloadOptions;
import org.openstreetmap.josm.plugins.mapwithai.gui.download.MapWithAIDownloadSourceType;
import org.openstreetmap.josm.plugins.mapwithai.gui.preferences.MapWithAIPreferences;
import org.openstreetmap.josm.plugins.mapwithai.spi.preferences.MapWithAIConfig;
import org.openstreetmap.josm.plugins.mapwithai.spi.preferences.MapWithAIUrls;
import org.openstreetmap.josm.plugins.mapwithai.tools.MapPaintUtils;
import org.openstreetmap.josm.plugins.mapwithai.tools.MapWithAICopyProhibit;
import org.openstreetmap.josm.spi.preferences.Config;
@ -81,6 +83,8 @@ public final class MapWithAIPlugin extends Plugin implements Destroyable {
public MapWithAIPlugin(PluginInformation info) {
super(info);
MapWithAIConfig.setUrlsProvider(MapWithAIUrls.getInstance());
preferenceSetting = new MapWithAIPreferences();
// Add MapWithAI specific menu

Wyświetl plik

@ -24,15 +24,12 @@ import org.openstreetmap.josm.data.coor.LatLon;
import org.openstreetmap.josm.io.CachedFile;
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo;
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAILayerInfo;
import org.openstreetmap.josm.plugins.mapwithai.spi.preferences.MapWithAIConfig;
import org.openstreetmap.josm.tools.Logging;
import org.openstreetmap.josm.tools.Territories;
import org.openstreetmap.josm.tools.Utils;
public class DataAvailability {
/** The default server URL */
public static final String DEFAULT_SERVER_URL = "https://gokaart.gitlab.io/JOSM_MapWithAI/json/sources.json";
/** This points to a list of default sources that can be used with MapWithAI */
private static String defaultServerUrl = DEFAULT_SERVER_URL;
/** A map of tag -&gt; message of possible data types */
static final Map<String, String> POSSIBLE_DATA_POINTS = new TreeMap<>();
@ -51,16 +48,14 @@ public class DataAvailability {
*/
private static final List<Class<? extends DataAvailability>> DATA_SOURCES = new ArrayList<>();
private static DataAvailability instance;
/**
* A map of countries to a map of available types
* ({@code Map<Country, Map<Type, IsAvailable>>}
*/
static final Map<String, Map<String, Boolean>> COUNTRIES = new HashMap<>();
private static class InstanceHelper {
static DataAvailability instance = new DataAvailability();
}
protected DataAvailability() {
if (DataAvailability.class.equals(this.getClass())) {
initialize();
@ -71,7 +66,7 @@ public class DataAvailability {
* Initialize the class
*/
private static void initialize() {
try (CachedFile jsonFile = new CachedFile(defaultServerUrl);
try (CachedFile jsonFile = new CachedFile(MapWithAIConfig.getUrls().getMapWithAISourcesJson());
JsonParser jsonParser = Json.createParser(jsonFile.getContentReader());) {
jsonFile.setMaxAge(SEVEN_DAYS_IN_SECONDS);
jsonParser.next();
@ -162,11 +157,11 @@ public class DataAvailability {
* @return the unique instance
*/
public static DataAvailability getInstance() {
if (InstanceHelper.instance == null || COUNTRIES.isEmpty()
if (instance == null || COUNTRIES.isEmpty()
|| MapWithAIPreferenceHelper.getMapWithAIUrl().isEmpty()) {
InstanceHelper.instance = new DataAvailability();
instance = new DataAvailability();
}
return InstanceHelper.instance;
return instance;
}
/**
@ -248,7 +243,7 @@ public class DataAvailability {
*
* @return List of terms of use urls
*/
public static final List<String> getTermsOfUse() {
public static List<String> getTermsOfUse() {
return Stream.concat(MapWithAILayerInfo.getInstance().getLayers().stream().map(MapWithAIInfo::getTermsOfUseURL),
DATA_SOURCES.stream().map(clazz -> {
try {
@ -267,7 +262,7 @@ public class DataAvailability {
*
* @return List of privacy policy urls
*/
public static final List<String> getPrivacyPolicy() {
public static List<String> getPrivacyPolicy() {
return Stream
.concat(MapWithAILayerInfo.getInstance().getLayers().stream().map(MapWithAIInfo::getPrivacyPolicyURL),
DATA_SOURCES.stream().map(clazz -> {
@ -282,22 +277,4 @@ public class DataAvailability {
.filter(Objects::nonNull).filter(str -> !Utils.removeWhiteSpaces(str).isEmpty()).distinct()
.collect(Collectors.toList());
}
/**
* Set the URL to use to get MapWithAI information (`sources.json`)
*
* @param url The URL which serves MapWithAI servers
*/
public static void setReleaseUrl(String url) {
defaultServerUrl = url;
}
/**
* Get the URL for the `sources.json`.
*
* @return The URL which serves MapWithAI servers
*/
public static String getReleaseUrl() {
return defaultServerUrl;
}
}

Wyświetl plik

@ -97,10 +97,13 @@ public class DataConflationSender implements RunnableFuture<DataSet> {
.append(protocolVersion.getProtocol()).append('/').append(protocolVersion.getMajor()).append('.')
.append(protocolVersion.getMinor()).append(' ').append(response.getStatusLine().getStatusCode())
.toString());
this.done = true;
} catch (IOException | UnsupportedOperationException | IllegalDataException e) {
Logging.error(e);
}
this.done = true;
synchronized (this) {
this.notifyAll();
}
}
@Override
@ -113,6 +116,9 @@ public class DataConflationSender implements RunnableFuture<DataSet> {
}
this.done = true;
this.cancelled = true;
synchronized (this) {
this.notifyAll();
}
return true;
}
@ -128,8 +134,10 @@ public class DataConflationSender implements RunnableFuture<DataSet> {
@Override
public DataSet get() throws InterruptedException, ExecutionException {
while (!isDone()) {
Thread.sleep(100);
synchronized (this) {
while (!isDone()) {
this.wait(100);
}
}
return this.conflatedData;
}
@ -139,9 +147,11 @@ public class DataConflationSender implements RunnableFuture<DataSet> {
long realtime = unit.toMillis(timeout);
long waitTime = realtime > MAX_POLLS ? realtime / MAX_POLLS : 1;
long timeWaited = 0;
while (!isDone()) {
Thread.sleep(waitTime);
timeWaited += waitTime;
synchronized (this) {
while (!isDone()) {
this.wait(waitTime);
timeWaited += waitTime;
}
}
if (!isDone() && timeWaited > realtime) {
throw new TimeoutException();

Wyświetl plik

@ -10,20 +10,20 @@ import java.util.List;
import java.util.Map;
import org.openstreetmap.josm.plugins.mapwithai.io.mapwithai.ConflationSourceReader;
import org.openstreetmap.josm.plugins.mapwithai.spi.preferences.MapWithAIConfig;
import org.openstreetmap.josm.tools.Logging;
public final class MapWithAIConflationCategory {
private static final Map<MapWithAICategory, List<String>> CONFLATION_URLS = new EnumMap<>(MapWithAICategory.class);
private static final String EMPTY_URL = "";
private static final String DEFAULT_CONFLATION_JSON = "https://gokaart.gitlab.io/JOSM_MapWithAI/json/conflation_servers.json";
private static String conflationJson = DEFAULT_CONFLATION_JSON;
static {
initialize();
}
static void initialize() {
public static void initialize() {
CONFLATION_URLS.clear();
try (ConflationSourceReader reader = new ConflationSourceReader(conflationJson)) {
try (ConflationSourceReader reader = new ConflationSourceReader(
MapWithAIConfig.getUrls().getConflationServerJson())) {
reader.parse().ifPresent(CONFLATION_URLS::putAll);
} catch (IOException e) {
Logging.error(e);
@ -54,30 +54,4 @@ public final class MapWithAIConflationCategory {
Collection<String> list = CONFLATION_URLS.computeIfAbsent(category, i -> new ArrayList<>(1));
list.add(url);
}
/**
* Set the URL to use to get conflation servers
*
* @param url The URL to use
*/
public static void setConflationJsonLocation(String url) {
conflationJson = url;
initialize();
}
/**
* Reset the conflation json location to the default location
*/
public static void resetConflationJsonLocation() {
setConflationJsonLocation(DEFAULT_CONFLATION_JSON);
}
/**
* Get the current conflation json location
*
* @return The URL that is used to build conflation information
*/
public static String getConflationJsonLocation() {
return conflationJson;
}
}

Wyświetl plik

@ -3,12 +3,8 @@ package org.openstreetmap.josm.plugins.mapwithai.data.mapwithai;
import static org.openstreetmap.josm.tools.I18n.tr;
import javax.annotation.Nonnull;
import javax.swing.SwingUtilities;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@ -24,6 +20,9 @@ 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.gui.jmapviewer.tilesources.TileSourceInfo;
import org.openstreetmap.josm.actions.ExpertToggleAction;
import org.openstreetmap.josm.data.Preferences;
@ -37,6 +36,7 @@ import org.openstreetmap.josm.io.imagery.ImageryReader;
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo.MapWithAIPreferenceEntry;
import org.openstreetmap.josm.plugins.mapwithai.io.mapwithai.ESRISourceReader;
import org.openstreetmap.josm.plugins.mapwithai.io.mapwithai.MapWithAISourceReader;
import org.openstreetmap.josm.plugins.mapwithai.spi.preferences.MapWithAIConfig;
import org.openstreetmap.josm.spi.preferences.Config;
import org.openstreetmap.josm.tools.ListenerList;
import org.openstreetmap.josm.tools.Logging;
@ -68,15 +68,15 @@ public class MapWithAILayerInfo {
/** The prefix for configuration of the MapWithAI sources */
public static final String CONFIG_PREFIX = "mapwithai.sources.";
private static final String[] DEFAULT_LAYER_SITES = {
"https://gokaart.gitlab.io/JOSM_MapWithAI/json/sources.json" };
/** Unique instance -- MUST be after DEFAULT_LAYER_SITES */
private static MapWithAILayerInfo instance;
public static MapWithAILayerInfo getInstance() {
if (instance != null) {
return instance;
}
final AtomicBoolean finished = new AtomicBoolean();
synchronized (DEFAULT_LAYER_SITES) {
synchronized (MapWithAILayerInfo.class) {
if (instance == null) {
instance = new MapWithAILayerInfo(() -> {
synchronized (finished) {
@ -111,7 +111,8 @@ public class MapWithAILayerInfo {
* @since 7434
*/
public static Collection<String> getImageryLayersSites() {
return Config.getPref().getList(CONFIG_PREFIX + "layers.sites", Arrays.asList(DEFAULT_LAYER_SITES));
return Config.getPref().getList(CONFIG_PREFIX + "layers.sites",
Collections.singletonList(MapWithAIConfig.getUrls().getMapWithAISourcesJson()));
}
/**
@ -122,7 +123,11 @@ public class MapWithAILayerInfo {
* {@link org.openstreetmap.josm.spi.preferences.IPreferences#putList}
*/
public static boolean setImageryLayersSites(Collection<String> sites) {
return Config.getPref().putList(CONFIG_PREFIX + "layers.sites", new ArrayList<>(sites));
if (sites == null || sites.isEmpty()) {
return Config.getPref().put(CONFIG_PREFIX + "layers.sites", null);
} else {
return Config.getPref().putList(CONFIG_PREFIX + "layers.sites", new ArrayList<>(sites));
}
}
private MapWithAILayerInfo(FinishListener listener) {

Wyświetl plik

@ -210,9 +210,11 @@ public class ESRISourceReader {
@Nullable
private static String getJsonString(@Nonnull final String url, final long defaultMaxAge, 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 (Config.getPref() != null) {
// 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);

Wyświetl plik

@ -0,0 +1,32 @@
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.mapwithai.spi.preferences;
/**
* Interface for a provider of certain URLs. Modelled after
* {@link org.openstreetmap.josm.spi.preferences.IUrls}.
*
* @author Taylor Smock
*/
public interface IMapWithAIUrls {
/**
* Get the conflation server json URL
*
* @return The URL with additional conflation servers
*/
String getConflationServerJson();
/**
* Get the URL for MapWithAI sources
*
* @return The URL with source information
*/
String getMapWithAISourcesJson();
/**
* Get the URL for the MapWithAI paintstyle
*
* @return The URL to use to get the paint style
*/
String getMapWithAIPaintStyle();
}

Wyświetl plik

@ -0,0 +1,32 @@
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.mapwithai.spi.preferences;
import java.util.Objects;
/**
* Class to hold the global preferences objects. Modeled after
* {@link org.openstreetmap.josm.spi.preferences.Config}.
*
* @author Taylor Smock
*/
public class MapWithAIConfig {
private static IMapWithAIUrls urls;
/**
* Get class that provides the value of certain URLs
*
* @return the global {@link IMapWithAIUrls} instance
*/
public static IMapWithAIUrls getUrls() {
return urls;
}
/**
* Install the global URLs provider.
*
* @param urls the global URLs provider instance to set (must not be null)
*/
public static void setUrlsProvider(IMapWithAIUrls urls) {
MapWithAIConfig.urls = Objects.requireNonNull(urls);
}
}

Wyświetl plik

@ -0,0 +1,43 @@
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.mapwithai.spi.preferences;
public class MapWithAIUrls implements IMapWithAIUrls {
/** The default url for additional conflation servers */
private static final String DEFAULT_CONFLATION_JSON = "https://gokaart.gitlab.io/JOSM_MapWithAI/json/conflation_servers.json";
/** The default URL for the MapWithAI sources */
private static final String DEFAULT_MAPWITHAI_SOURCES_JSON = "https://gokaart.gitlab.io/JOSM_MapWithAI/json/sources.json";
/** The default url for the MapWithAI paint style */
private static final String DEFAULT_PAINT_STYLE_RESOURCE_URL = "https://josm.openstreetmap.de/josmfile?page=Styles/MapWithAI&zip=1";
private static class InstanceHolder {
static final MapWithAIUrls INSTANCE = new MapWithAIUrls();
}
/**
* Returns the unique instance.
*
* @return the unique instance
*/
public static MapWithAIUrls getInstance() {
return MapWithAIUrls.InstanceHolder.INSTANCE;
}
@Override
public String getConflationServerJson() {
return DEFAULT_CONFLATION_JSON;
}
@Override
public String getMapWithAISourcesJson() {
return DEFAULT_MAPWITHAI_SOURCES_JSON;
}
@Override
public String getMapWithAIPaintStyle() {
return DEFAULT_PAINT_STYLE_RESOURCE_URL;
}
private MapWithAIUrls() {
// Hide the constructor
}
}

Wyświetl plik

@ -38,14 +38,12 @@ import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
import org.openstreetmap.josm.gui.util.GuiHelper;
import org.openstreetmap.josm.io.CachedFile;
import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin;
import org.openstreetmap.josm.plugins.mapwithai.spi.preferences.MapWithAIConfig;
import org.openstreetmap.josm.tools.ColorHelper;
import org.openstreetmap.josm.tools.Logging;
public final class MapPaintUtils {
/** The default url for the MapWithAI paint style */
public static final String DEFAULT_PAINT_STYLE_RESOURCE_URL = "https://josm.openstreetmap.de/josmfile?page=Styles/MapWithAI&zip=1";
private static String paintStyleResourceUrl = DEFAULT_PAINT_STYLE_RESOURCE_URL;
private static final Pattern TEST_PATTERN = Pattern
.compile("^https?:\\/\\/(www\\.)?localhost[:0-9]*\\/josmfile\\?page=Styles\\/MapWithAI&zip=1$");
@ -95,8 +93,8 @@ public final class MapPaintUtils {
.forEach(MapPaintStyles::removeStyle);
if (!checkIfMapWithAIPaintStyleExists()) {
final MapCSSStyleSource style = new MapCSSStyleSource(paintStyleResourceUrl, MapWithAIPlugin.NAME,
"MapWithAI");
final MapCSSStyleSource style = new MapCSSStyleSource(MapWithAIConfig.getUrls().getMapWithAIPaintStyle(),
MapWithAIPlugin.NAME, "MapWithAI");
return MapPaintStyles.addStyle(style);
}
return getMapWithAIPaintStyle();
@ -104,7 +102,8 @@ public final class MapPaintUtils {
public static synchronized boolean checkIfMapWithAIPaintStyleExists() {
return MapPaintStyles.getStyles().getStyleSources().parallelStream().filter(MapCSSStyleSource.class::isInstance)
.map(MapCSSStyleSource.class::cast).anyMatch(source -> paintStyleResourceUrl.equals(source.url)
.map(MapCSSStyleSource.class::cast)
.anyMatch(source -> MapWithAIConfig.getUrls().getMapWithAIPaintStyle().equals(source.url)
|| TEST_PATTERN.matcher(source.url).matches());
}
@ -114,7 +113,8 @@ public final class MapPaintUtils {
public static synchronized void removeMapWithAIPaintStyles() {
// WebStart has issues with streams and EDT permissions. Don't use streams.
for (StyleSource style : new ArrayList<>(MapPaintStyles.getStyles().getStyleSources())) {
if (paintStyleResourceUrl.equals(style.url) || TEST_PATTERN.matcher(style.url).matches()) {
if (MapWithAIConfig.getUrls().getMapWithAIPaintStyle().equals(style.url)
|| TEST_PATTERN.matcher(style.url).matches()) {
GuiHelper.runInEDT(() -> MapPaintStyles.removeStyle(style));
}
}
@ -126,29 +126,12 @@ public final class MapPaintUtils {
* @return get the MapWithAI Paint style
*/
public static synchronized StyleSource getMapWithAIPaintStyle() {
return MapPaintStyles.getStyles().getStyleSources().parallelStream().filter(
source -> paintStyleResourceUrl.equals(source.url) || TEST_PATTERN.matcher(source.url).matches())
return MapPaintStyles.getStyles().getStyleSources().parallelStream()
.filter(source -> MapWithAIConfig.getUrls().getMapWithAIPaintStyle().equals(source.url)
|| TEST_PATTERN.matcher(source.url).matches())
.findAny().orElse(null);
}
/**
* Set the URL for the MapWithAI paint style
*
* @param paintUrl The paint style for MapWithAI
*/
public static synchronized void setPaintStyleUrl(String paintUrl) {
paintStyleResourceUrl = paintUrl;
}
/**
* Get the url for the paint style for MapWithAI
*
* @return The url for the paint style
*/
public static synchronized String getPaintStyleUrl() {
return paintStyleResourceUrl;
}
/**
* Add sources to the paint style
*

Wyświetl plik

@ -12,10 +12,11 @@ import java.util.List;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.openstreetmap.josm.plugins.mapwithai.backend.DataAvailability;
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo;
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIType;
import org.openstreetmap.josm.plugins.mapwithai.spi.preferences.MapWithAIConfig;
import org.openstreetmap.josm.plugins.mapwithai.testutils.annotations.NoExceptions;
import org.openstreetmap.josm.plugins.mapwithai.testutils.annotations.Wiremock;
import org.openstreetmap.josm.testutils.JOSMTestRules;
import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
@ -28,15 +29,17 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
*/
@NoExceptions
@BasicPreferences
@Wiremock
class MapWithAISourceReaderTestIT {
@RegisterExtension
@SuppressFBWarnings("URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
public JOSMTestRules rule = new JOSMTestRules().territories().projection();
@Test
@Wiremock(false)
void testDefaultSourceIT() throws IOException {
DataAvailability.setReleaseUrl(DataAvailability.DEFAULT_SERVER_URL);
try (MapWithAISourceReader source = new MapWithAISourceReader(DataAvailability.getReleaseUrl())) {
try (MapWithAISourceReader source = new MapWithAISourceReader(
MapWithAIConfig.getUrls().getMapWithAISourcesJson())) {
List<MapWithAIInfo> infoList = source.parse().orElse(Collections.emptyList());
assertFalse(infoList.isEmpty(), "There should be viable sources");
for (MapWithAIType type : Arrays.asList(MapWithAIType.FACEBOOK, MapWithAIType.THIRD_PARTY)) {

Wyświetl plik

@ -9,7 +9,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.ArrayList;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.awaitility.Durations;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -23,6 +22,8 @@ import org.openstreetmap.josm.plugins.mapwithai.testutils.annotations.Wiremock;
import org.openstreetmap.josm.testutils.JOSMTestRules;
import org.openstreetmap.josm.tools.Territories;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
* Test class for testing availability
*
@ -60,7 +61,7 @@ class MapWithAIAvailabilityTest {
}
@Test
void testgetDataLatLon() {
void testGetDataLatLon() {
assertTrue(DataAvailability.getDataTypes(new LatLon(0, 0)).isEmpty(), "There should not be data in the ocean");
assertTrue(DataAvailability.getDataTypes(new LatLon(40, -100)).getOrDefault("highway", false),
"The US should have highway data");
@ -81,7 +82,7 @@ class MapWithAIAvailabilityTest {
new ArrayList<>(MapWithAILayerInfo.getInstance().getLayers())
.forEach(i -> MapWithAILayerInfo.getInstance().remove(i));
DataAvailability.getInstance();
testgetDataLatLon();
testGetDataLatLon();
MapWithAILayerInfo.getInstance().getLayers().forEach(i -> MapWithAILayerInfo.getInstance().remove(i));
DataAvailability.getInstance();
testHasDataLatLon();

Wyświetl plik

@ -10,15 +10,18 @@ import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.JCheckBox;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.openstreetmap.josm.data.Bounds;
import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIDataUtilsTest;
import org.openstreetmap.josm.plugins.mapwithai.testutils.annotations.Wiremock;
import org.openstreetmap.josm.testutils.JOSMTestRules;
import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
@BasicPreferences
@Wiremock
class MapWithAIDownloadSourceTypeTest {
@RegisterExtension
@SuppressFBWarnings("URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")

Wyświetl plik

@ -6,12 +6,11 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import javax.swing.SpinnerNumberModel;
import java.lang.reflect.Field;
import java.util.List;
import javax.swing.SpinnerNumberModel;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@ -22,6 +21,8 @@ import org.openstreetmap.josm.plugins.mapwithai.testutils.annotations.MapWithAIS
import org.openstreetmap.josm.testutils.JOSMTestRules;
import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
* Test class for {@link MapWithAIPreferences}
*

Wyświetl plik

@ -1,78 +1,24 @@
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.mapwithai.testutils;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.lang.Thread.UncaughtExceptionHandler;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.logging.Level;
import java.util.stream.Collectors;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.common.FileSource;
import com.github.tomakehurst.wiremock.extension.Parameters;
import com.github.tomakehurst.wiremock.extension.ResponseTransformer;
import com.github.tomakehurst.wiremock.http.Request;
import com.github.tomakehurst.wiremock.http.Response;
import com.github.tomakehurst.wiremock.verification.LoggedRequest;
import mockit.integration.TestRunnerDecorator;
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;
import org.openstreetmap.josm.io.OsmApiInitializationException;
import org.openstreetmap.josm.io.OsmTransferCanceledException;
import org.openstreetmap.josm.plugins.mapwithai.backend.DataAvailability;
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIConflationCategory;
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo;
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAILayerInfo;
import org.openstreetmap.josm.plugins.mapwithai.tools.MapPaintUtils;
import org.openstreetmap.josm.spi.preferences.Config;
import org.openstreetmap.josm.testutils.JOSMTestRules;
import org.openstreetmap.josm.tools.Logging;
import mockit.integration.TestRunnerDecorator;
public class MapWithAITestRules extends JOSMTestRules {
private boolean wiremock;
private static WireMockServer wireMock;
private static final List<Object> wireMockUsers = Collections.synchronizedList(new ArrayList<>());
private boolean workerExceptions = true;
private UncaughtExceptionHandler currentExceptionHandler;
private String currentReleaseUrl;
private Collection<String> sourceSites;
private boolean territories;
public MapWithAITestRules() {
super();
}
@Override
public MapWithAITestRules territories() {
this.territories = true;
super.territories();
return this;
}
/**
* @deprecated Use
* {@link org.openstreetmap.josm.plugins.mapwithai.testutils.annotations.Wiremock}
* instead
* @return this, for easy chaining
*/
@Deprecated
public MapWithAITestRules wiremock() {
this.wiremock = true;
territories();
return this;
}
public MapWithAITestRules noWorkerExceptions() {
this.workerExceptions = false;
return this;
@ -92,39 +38,6 @@ public class MapWithAITestRules extends JOSMTestRules {
Logging.getLogger().setFilter(record -> record.getLevel().intValue() >= Level.WARNING.intValue()
|| record.getSourceClassName().startsWith("org.openstreetmap.josm.plugins.mapwithai"));
synchronized (wireMockUsers) {
if (wiremock && wireMock == null) {
wireMock = new WireMockServer(options().usingFilesUnderDirectory("test/resources/wiremock")
.extensions(new WireMockUrlTransformer()).dynamicPort());
wireMock.start();
}
// Sometimes this is called twice, the second time resetting the config but not
// resetting the urls.
if (wiremock && wireMock != null) {
MapPaintUtils.setPaintStyleUrl(replaceUrl(wireMock, MapPaintUtils.getPaintStyleUrl()));
// Avoid cases where tests could write the wiremock url to some fields.
if (currentReleaseUrl == null) {
currentReleaseUrl = DataAvailability.getReleaseUrl();
}
DataAvailability.setReleaseUrl(replaceUrl(wireMock, DataAvailability.getReleaseUrl()));
Config.getPref().put("osm-server.url", wireMock.baseUrl());
// Avoid cases where tests could write the wiremock url to some fields.
if (sourceSites == null) {
sourceSites = MapWithAILayerInfo.getImageryLayersSites();
}
MapWithAILayerInfo.setImageryLayersSites(sourceSites.stream().map(t -> replaceUrl(wireMock, t))
.filter(Objects::nonNull).collect(Collectors.toList()));
MapWithAIConflationCategory.setConflationJsonLocation(
replaceUrl(wireMock, MapWithAIConflationCategory.getConflationJsonLocation()));
try {
OsmApi.getOsmApi().initialize(NullProgressMonitor.INSTANCE);
} catch (OsmTransferCanceledException | OsmApiInitializationException e) {
Logging.error(e);
}
wireMockUsers.add(this);
}
}
if (workerExceptions) {
currentExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
@ -137,86 +50,10 @@ public class MapWithAITestRules extends JOSMTestRules {
@Override
protected void after() throws ReflectiveOperationException {
super.after();
synchronized (wireMockUsers) {
wireMockUsers.remove(this);
if (wiremock && wireMockUsers.isEmpty()) {
MapPaintUtils.removeMapWithAIPaintStyles();
// Run in EDT to avoid stopping wiremock server before wiremock requests finish.
GuiHelper.runInEDTAndWait(wireMock::stop);
List<LoggedRequest> requests = wireMock.findUnmatchedRequests().getRequests();
wireMock = null;
requests.forEach(r -> Logging.error(r.getAbsoluteUrl()));
assertTrue(requests.isEmpty());
Config.getPref().put("osm-server.url", null);
// Avoid cases where tests could write the wiremock url to some fields.
if (currentReleaseUrl != null) {
DataAvailability.setReleaseUrl(currentReleaseUrl);
currentReleaseUrl = null;
}
if (sourceSites != null) {
MapWithAILayerInfo.setImageryLayersSites(sourceSites);
sourceSites = null;
}
MapWithAIConflationCategory.resetConflationJsonLocation();
resetMapWithAILayerInfo();
}
}
if (workerExceptions) {
Thread.setDefaultUncaughtExceptionHandler(currentExceptionHandler);
}
TestRunnerDecorator.cleanUpAllMocks();
}
private void resetMapWithAILayerInfo() {
if (territories) {
synchronized (MapWithAILayerInfo.class) {
MapWithAILayerInfo.getInstance().clear();
MapWithAILayerInfo.getInstance().getDefaultLayers().stream().filter(MapWithAIInfo::isDefaultEntry)
.forEach(MapWithAILayerInfo.getInstance()::add);
MapWithAILayerInfo.getInstance().save();
}
}
}
/**
* Replace URL servers with wiremock
*
* @param wireMockServer The wiremock to point to
* @param url The URL to fix
* @return A url that points at the wiremock server
*/
private static String replaceUrl(WireMockServer wireMockServer, String url) {
try {
URL temp = new URL(url);
return wireMockServer.baseUrl() + temp.getFile();
} catch (MalformedURLException error) {
Logging.error(error);
}
return null;
}
/**
* Replace URL's with the wiremock URL
*
* @author Taylor Smock
*/
private static class WireMockUrlTransformer extends ResponseTransformer {
@Override
public String getName() {
return "Convert urls in responses to wiremock url";
}
@Override
public Response transform(Request request, Response response, FileSource files, Parameters parameters) {
if (wireMock != null && !request.getUrl().endsWith("/capabilities")
&& !response.getHeaders().getContentTypeHeader().mimeTypePart().contains("application/zip")) {
String origBody = response.getBodyAsString();
String newBody = origBody.replaceAll("https?:\\/\\/.*?\\/", wireMock.baseUrl() + "/");
return Response.Builder.like(response).but().body(newBody).build();
}
return response;
}
}
}

Wyświetl plik

@ -5,10 +5,8 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Collections;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import org.awaitility.Awaitility;
import org.awaitility.Durations;
@ -18,7 +16,11 @@ import org.openstreetmap.josm.plugins.mapwithai.backend.DataAvailability;
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIConflationCategory;
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo;
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAILayerInfo;
import org.openstreetmap.josm.plugins.mapwithai.spi.preferences.IMapWithAIUrls;
import org.openstreetmap.josm.plugins.mapwithai.spi.preferences.MapWithAIConfig;
import org.openstreetmap.josm.plugins.mapwithai.spi.preferences.MapWithAIUrls;
import org.openstreetmap.josm.plugins.mapwithai.tools.MapPaintUtils;
import org.openstreetmap.josm.testutils.annotations.AnnotationUtils;
import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
import org.openstreetmap.josm.testutils.annotations.BasicWiremock;
import org.openstreetmap.josm.testutils.annotations.HTTP;
@ -39,9 +41,7 @@ import com.github.tomakehurst.wiremock.http.Response;
@Target({ ElementType.PARAMETER, ElementType.TYPE, ElementType.METHOD })
@BasicPreferences
@HTTP
@ExtendWith(Wiremock.DataAvailabilityExtension.class)
@ExtendWith(Wiremock.MapPaintUtilsExtension.class)
@ExtendWith(Wiremock.MapWithAIConflationCategoryExtension.class)
@ExtendWith(Wiremock.TestMapWithAIUrls.class)
@BasicWiremock(value = "test/resources/wiremock", responseTransformers = Wiremock.WireMockUrlTransformer.class)
public @interface Wiremock {
/**
@ -97,27 +97,6 @@ public @interface Wiremock {
}
}
/**
* Extension for {@link MapPaintUtils}
*/
class MapPaintUtilsExtension extends WiremockExtension {
@Override
public void afterAll(ExtensionContext context) throws Exception {
try {
super.afterAll(context);
} finally {
MapPaintUtils.removeMapWithAIPaintStyles();
MapPaintUtils.setPaintStyleUrl("https://invalid.url/josmfile?page=Styles/MapWithAI&zip=1");
}
}
@Override
public void beforeAll(ExtensionContext context) throws Exception {
super.beforeAll(context);
MapPaintUtils.setPaintStyleUrl(replaceUrl(getWiremock(context), MapPaintUtils.getPaintStyleUrl()));
}
}
/**
* Extension for {@link MapWithAILayerInfo}
*/
@ -127,8 +106,6 @@ public @interface Wiremock {
try {
super.afterAll(context);
} finally {
MapWithAILayerInfo.setImageryLayersSites(
Collections.singleton("https://invalid.url/JOSM_MapWithAI/json/sources.json"));
resetMapWithAILayerInfo();
}
}
@ -136,9 +113,7 @@ public @interface Wiremock {
@Override
public void beforeAll(ExtensionContext context) throws Exception {
super.beforeAll(context);
MapWithAILayerInfo.setImageryLayersSites(
MapWithAILayerInfo.getImageryLayersSites().stream().map(t -> replaceUrl(getWiremock(context), t))
.filter(Objects::nonNull).collect(Collectors.toList()));
MapWithAILayerInfo.setImageryLayersSites(null);
AtomicBoolean finished = new AtomicBoolean();
MapWithAILayerInfo.getInstance().clear();
MapWithAILayerInfo.getInstance().load(false, () -> finished.set(true));
@ -158,45 +133,78 @@ public @interface Wiremock {
}
/**
* Extension for {@link DataAvailability}
*/
class DataAvailabilityExtension extends WiremockExtension {
class TestMapWithAIUrls extends WiremockExtension implements IMapWithAIUrls {
ExtensionContext context;
private static boolean conflationServerInitialized;
@Override
public void afterAll(ExtensionContext context) throws Exception {
try {
super.afterAll(context);
} finally {
DataAvailability.setReleaseUrl("https://invalid.url/JOSM_MapWithAI/json/sources.json");
}
public String getConflationServerJson() {
conflationServerInitialized = true;
return replaceUrl(getWiremock(this.context), MapWithAIUrls.getInstance().getConflationServerJson());
}
@Override
public String getMapWithAISourcesJson() {
return replaceUrl(getWiremock(this.context), MapWithAIUrls.getInstance().getMapWithAISourcesJson());
}
@Override
public String getMapWithAIPaintStyle() {
return replaceUrl(getWiremock(this.context), MapWithAIUrls.getInstance().getMapWithAIPaintStyle());
}
@Override
public void beforeAll(ExtensionContext context) throws Exception {
super.beforeAll(context);
DataAvailability.setReleaseUrl(replaceUrl(getWiremock(context), DataAvailability.getReleaseUrl()));
final Optional<Wiremock> annotation = AnnotationUtils.findFirstParentAnnotation(context, Wiremock.class);
this.context = context;
if (Boolean.FALSE.equals(annotation.map(Wiremock::value).orElse(Boolean.TRUE))) {
MapWithAIConfig.setUrlsProvider(MapWithAIUrls.getInstance());
} else {
MapWithAIConfig.setUrlsProvider(this);
}
if (conflationServerInitialized) {
MapWithAIConflationCategory.initialize();
}
AnnotationUtils.resetStaticClass(DataAvailability.class);
}
@Override
public void beforeEach(ExtensionContext context) throws Exception {
final Optional<Wiremock> annotation = AnnotationUtils.findFirstParentAnnotation(context, Wiremock.class);
if (annotation.isPresent()) {
this.beforeAll(context);
}
super.beforeEach(context);
}
}
/**
* Extension for {@link MapWithAIConflationCategory}
*/
class MapWithAIConflationCategoryExtension extends WiremockExtension {
@Override
public void afterAll(ExtensionContext context) throws Exception {
// @Wiremock stops the WireMockServer prior to this method being called
getWiremock(context).start();
MapPaintUtils.removeMapWithAIPaintStyles();
try {
// This stops the WireMockServer again.
super.afterAll(context);
} finally {
MapWithAIConflationCategory.resetConflationJsonLocation();
MapWithAIConfig.setUrlsProvider(new InvalidMapWithAIUrls());
}
}
@Override
public void beforeAll(ExtensionContext context) throws Exception {
super.beforeAll(context);
MapWithAIConflationCategory.setConflationJsonLocation(
replaceUrl(getWiremock(context), MapWithAIConflationCategory.getConflationJsonLocation()));
}
}
class InvalidMapWithAIUrls implements IMapWithAIUrls {
@Override
public String getConflationServerJson() {
throw new UnsupportedOperationException("Please use the @Wiremock annotation");
}
@Override
public String getMapWithAISourcesJson() {
return this.getConflationServerJson();
}
@Override
public String getMapWithAIPaintStyle() {
return this.getConflationServerJson();
}
}
}