Ticket #22733: 22733.patch

File 22733.patch, 12.3 KB (added by taylor.smock, 3 years ago)
  • src/org/openstreetmap/josm/gui/preferences/imagery/AddWMSLayerPanel.java

    Subject: [PATCH] Fix #22733: UI blocks while trying different WMS endpoint urls and cannot handle `nan` in responses
    ---
    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    diff --git a/src/org/openstreetmap/josm/gui/preferences/imagery/AddWMSLayerPanel.java b/src/org/openstreetmap/josm/gui/preferences/imagery/AddWMSLayerPanel.java
    a b  
    33
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
     6import java.awt.GraphicsEnvironment;
     7import java.awt.event.ActionEvent;
    68import java.awt.event.ActionListener;
    79import java.io.IOException;
    810import java.net.MalformedURLException;
     
    1517import javax.swing.JButton;
    1618import javax.swing.JCheckBox;
    1719import javax.swing.JComboBox;
     20import javax.swing.JComponent;
    1821import javax.swing.JLabel;
    1922import javax.swing.JOptionPane;
    2023import javax.swing.JScrollPane;
     
    2326import org.openstreetmap.josm.data.imagery.ImageryInfo;
    2427import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
    2528import org.openstreetmap.josm.data.imagery.LayerDetails;
     29import org.openstreetmap.josm.gui.MainApplication;
     30import org.openstreetmap.josm.gui.PleaseWaitRunnable;
    2631import org.openstreetmap.josm.gui.bbox.SlippyMapBBoxChooser;
     32import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
     33import org.openstreetmap.josm.gui.progress.ProgressMonitor;
     34import org.openstreetmap.josm.gui.progress.swing.PleaseWaitProgressMonitor;
    2735import org.openstreetmap.josm.gui.util.GuiHelper;
    2836import org.openstreetmap.josm.gui.widgets.JosmTextArea;
     37import org.openstreetmap.josm.io.OsmTransferException;
    2938import org.openstreetmap.josm.io.imagery.WMSImagery;
    3039import org.openstreetmap.josm.tools.GBC;
    3140import org.openstreetmap.josm.tools.Logging;
    3241import org.openstreetmap.josm.tools.Utils;
     42import org.xml.sax.SAXException;
    3343
    3444/**
    3545 * An imagery panel used to add WMS imagery sources.
     
    8191        add(new JLabel(tr("{0} Enter name for this layer", "7.")), GBC.eol());
    8292        add(name, GBC.eop().fill(GBC.HORIZONTAL));
    8393
    84         getLayers.addActionListener(e -> {
    85             try {
    86                 wms = new WMSImagery(Utils.strip(rawUrl.getText()), getCommonHeaders());
    87                 tree.updateTree(wms);
    88                 Collection<String> wmsFormats = wms.getFormats();
    89                 formats.setModel(new DefaultComboBoxModel<>(wmsFormats.toArray(new String[0])));
    90                 formats.setSelectedItem(wms.getPreferredFormat());
    91             } catch (MalformedURLException | InvalidPathException ex1) {
    92                 Logging.log(Logging.LEVEL_ERROR, ex1);
    93                 JOptionPane.showMessageDialog(getParent(), tr("Invalid service URL."),
    94                         tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
    95             } catch (IOException ex2) {
    96                 Logging.log(Logging.LEVEL_ERROR, ex2);
    97                 JOptionPane.showMessageDialog(getParent(), tr("Could not retrieve WMS layer list."),
    98                         tr("WMS Error"), JOptionPane.ERROR_MESSAGE);
    99             } catch (WMSImagery.WMSGetCapabilitiesException ex3) {
    100                 String incomingData = ex3.getIncomingData() != null ? ex3.getIncomingData().trim() : "";
    101                 String title = tr("WMS Error");
    102                 StringBuilder message = new StringBuilder(tr("Could not parse WMS layer list."));
    103                 Logging.log(Logging.LEVEL_ERROR, "Could not parse WMS layer list. Incoming data:\n"+incomingData, ex3);
    104                 if ((incomingData.startsWith("<html>") || incomingData.startsWith("<HTML>"))
    105                   && (incomingData.endsWith("</html>") || incomingData.endsWith("</HTML>"))) {
    106                     GuiHelper.notifyUserHtmlError(this, title, message.toString(), incomingData);
    107                 } else {
    108                     if (ex3.getMessage() != null) {
    109                         message.append('\n').append(ex3.getMessage());
    110                     }
    111                     JOptionPane.showMessageDialog(getParent(), message.toString(), title, JOptionPane.ERROR_MESSAGE);
    112                 }
    113             }
    114         });
     94        getLayers.addActionListener(e -> MainApplication.worker.execute(new GetCapabilitiesRunnable(e)));
    11595
    11696        ActionListener availabilityManagerAction = a -> {
    11797            setDefaultLayers.setEnabled(endpoint.isSelected());
     
    151131        registerValidableComponent(setDefaultLayers);
    152132    }
    153133
     134    /**
     135     * Perform the get WMS layers network calls in a separate thread, mostly to allow for cancellation
     136     */
     137    private class GetCapabilitiesRunnable extends PleaseWaitRunnable {
     138        private final ActionEvent actionEvent;
     139
     140        protected GetCapabilitiesRunnable(ActionEvent actionEvent) {
     141            super(tr("Trying WMS urls"), GraphicsEnvironment.isHeadless() ? NullProgressMonitor.INSTANCE : new PleaseWaitProgressMonitor(),
     142                    false);
     143            this.actionEvent = actionEvent;
     144        }
     145
     146        @Override
     147        protected void cancel() {
     148            // We don't really have something we can do -- we use the monitor to pass the cancel state on
     149        }
     150
     151        @Override
     152        protected void realRun() throws SAXException, IOException, OsmTransferException {
     153            if (actionEvent.getSource() instanceof JComponent) {
     154                GuiHelper.runInEDT(() -> ((JComponent) actionEvent.getSource()).setEnabled(false));
     155            }
     156            ProgressMonitor monitor = getProgressMonitor();
     157            try {
     158                wms = new WMSImagery(Utils.strip(rawUrl.getText()), getCommonHeaders(), monitor);
     159            } catch (MalformedURLException | InvalidPathException ex1) {
     160                Logging.log(Logging.LEVEL_ERROR, ex1);
     161                GuiHelper.runInEDT(() -> JOptionPane.showMessageDialog(getParent(), tr("Invalid service URL."),
     162                        tr("WMS Error"), JOptionPane.ERROR_MESSAGE));
     163            } catch (IOException ex2) {
     164                Logging.log(Logging.LEVEL_ERROR, ex2);
     165                GuiHelper.runInEDT(() -> JOptionPane.showMessageDialog(getParent(), tr("Could not retrieve WMS layer list."),
     166                        tr("WMS Error"), JOptionPane.ERROR_MESSAGE));
     167            } catch (WMSImagery.WMSGetCapabilitiesException ex3) {
     168                String incomingData = ex3.getIncomingData() != null ? ex3.getIncomingData().trim() : "";
     169                String title = tr("WMS Error");
     170                StringBuilder message = new StringBuilder(tr("Could not parse WMS layer list."));
     171                Logging.log(Logging.LEVEL_ERROR, "Could not parse WMS layer list. Incoming data:\n"+incomingData, ex3);
     172                if ((incomingData.startsWith("<html>") || incomingData.startsWith("<HTML>"))
     173                        && (incomingData.endsWith("</html>") || incomingData.endsWith("</HTML>"))) {
     174                    GuiHelper.runInEDT(() -> GuiHelper.notifyUserHtmlError(AddWMSLayerPanel.this, title, message.toString(), incomingData));
     175                } else {
     176                    if (ex3.getMessage() != null) {
     177                        message.append('\n').append(ex3.getMessage());
     178                    }
     179                    GuiHelper.runInEDT(() -> JOptionPane.showMessageDialog(getParent(), message.toString(), title, JOptionPane.ERROR_MESSAGE));
     180                }
     181            }
     182        }
     183
     184        @Override
     185        protected void finish() {
     186            if (wms != null) {
     187                GuiHelper.runInEDT(() -> {
     188                    tree.updateTree(wms);
     189                    Collection<String> wmsFormats = wms.getFormats();
     190                    formats.setModel(new DefaultComboBoxModel<>(wmsFormats.toArray(new String[0])));
     191                    formats.setSelectedItem(wms.getPreferredFormat());
     192                });
     193            }
     194            if (actionEvent.getSource() instanceof JComponent) {
     195                GuiHelper.runInEDT(() -> ((JComponent) actionEvent.getSource()).setEnabled(true));
     196            }
     197        }
     198    }
     199
    154200    protected final void onLayerSelectionChanged() {
    155201        if (wms != null && wms.buildRootUrl() != null) {
    156202            wmsUrl.setText(wms.buildGetMapUrl(
  • src/org/openstreetmap/josm/io/imagery/WMSImagery.java

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    diff --git a/src/org/openstreetmap/josm/io/imagery/WMSImagery.java b/src/org/openstreetmap/josm/io/imagery/WMSImagery.java
    a b  
    1111import java.net.URL;
    1212import java.nio.file.InvalidPathException;
    1313import java.util.ArrayList;
     14import java.util.Arrays;
    1415import java.util.Collection;
    1516import java.util.Collections;
    1617import java.util.HashSet;
     
    3536import org.openstreetmap.josm.data.imagery.LayerDetails;
    3637import org.openstreetmap.josm.data.projection.Projection;
    3738import org.openstreetmap.josm.data.projection.Projections;
     39import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
     40import org.openstreetmap.josm.gui.progress.ProgressMonitor;
    3841import org.openstreetmap.josm.io.CachedFile;
    3942import org.openstreetmap.josm.tools.Logging;
    4043import org.openstreetmap.josm.tools.Utils;
     
    150153     * @throws InvalidPathException if a Path object cannot be constructed for the capabilities cached file
    151154     */
    152155    public WMSImagery(String url, Map<String, String> headers) throws IOException, WMSGetCapabilitiesException {
     156        this(url, headers, NullProgressMonitor.INSTANCE);
     157    }
     158
     159    /**
     160     * Make getCapabilities request towards given URL using headers
     161     * @param url service url
     162     * @param headers HTTP headers to be sent with request
     163     * @param monitor Feedback for which URL we are currently trying, the integer is the <i>total number of urls</i> we are going to try
     164     * @throws IOException when connection error when fetching get capabilities document
     165     * @throws WMSGetCapabilitiesException when there are errors when parsing get capabilities document
     166     * @throws InvalidPathException if a Path object cannot be constructed for the capabilities cached file
     167     */
     168    public WMSImagery(String url, Map<String, String> headers, ProgressMonitor monitor)
     169            throws IOException, WMSGetCapabilitiesException {
    153170        if (headers != null) {
    154171            this.headers.putAll(headers);
    155172        }
    156173
    157174        IOException savedExc = null;
    158175        String workingAddress = null;
    159         url_search:
    160         for (String z: new String[]{
    161                 normalizeUrl(url),
    162                 url,
    163                 url + CAPABILITIES_QUERY_STRING,
    164         }) {
    165             for (String ver: new String[]{"", "&VERSION=1.3.0", "&VERSION=1.1.1"}) {
     176        final String[] baseAdditions = {
     177            normalizeUrl(url),
     178            url,
     179            url + CAPABILITIES_QUERY_STRING,
     180        };
     181        final String[] versionAdditions = {"", "&VERSION=1.3.0", "&VERSION=1.1.1"};
     182        final int totalNumberOfUrlsToTry = baseAdditions.length * versionAdditions.length;
     183        monitor.setTicksCount(totalNumberOfUrlsToTry);
     184        url_search:
     185        for (String z : baseAdditions) {
     186            for (String ver : versionAdditions) {
     187                if (monitor.isCanceled()) {
     188                    break url_search;
     189                }
    166190                try {
     191                    monitor.setCustomText(z + ver);
     192                    monitor.worked(1);
    167193                    attemptGetCapabilities(z + ver);
    168194                    workingAddress = z;
    169195                    calculateChildren();
     
    192218                }
    193219            }
    194220        }
    195 
    196221        if (savedExc != null) {
    197222            throw savedExc;
    198223        }
     
    615640    }
    616641
    617642    private static Bounds parseBBox(Projection conv, String miny, String minx, String maxy, String maxx) {
    618         if (miny == null || minx == null || maxy == null || maxx == null) {
     643        if (miny == null || minx == null || maxy == null || maxx == null || Arrays.asList(miny, minx, maxy, maxx).contains("nan")) {
    619644            return null;
    620645        }
    621646        if (conv != null) {