Ignore:
Timestamp:
2023-09-20T17:53:20+02:00 (11 months ago)
Author:
taylor.smock
Message:

Fix #23171: UnknownHostException may cause issues in GeoChat

This appears to be something that can occur when running under WebStart.
To fix this, we check if (a) the JOSM website is offline and (b) if we are
running under webstart. If so, we don't check for messages.

Additionally, this patch converts geochat from the older time classes to the
newer java.time classes and fixes some lint issues.

Location:
applications/editors/josm/plugins/geochat/src/geochat
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • applications/editors/josm/plugins/geochat/src/geochat/ChatMessage.java

    r35162 r36146  
    22package geochat;
    33
    4 import java.util.Date;
     4import java.time.Instant;
    55
    66import org.openstreetmap.josm.data.coor.LatLon;
     
    1212 */
    1313public final class ChatMessage implements Comparable<ChatMessage> {
    14     private LatLon pos;
    15     private Date time;
    16     private String author;
     14    private final LatLon pos;
     15    private final Instant time;
     16    private final String author;
    1717    private String recipient;
    18     private String message;
    19     private long id;
     18    private final String message;
     19    private final long id;
    2020    private boolean priv;
    21     private boolean incoming;
     21    private final boolean incoming;
    2222
    23     public ChatMessage(long id, LatLon pos, String author, boolean incoming, String message, Date time) {
     23    public ChatMessage(long id, LatLon pos, String author, boolean incoming, String message, Instant time) {
    2424        this.id = id;
    2525        this.author = author;
     
    7272    }
    7373
    74     public Date getTime() {
     74    public Instant getTime() {
    7575        return time;
    7676    }
  • applications/editors/josm/plugins/geochat/src/geochat/ChatPaneManager.java

    r35162 r36146  
    3232    private static final String PUBLIC_PANE = "Public Pane";
    3333
    34     private GeoChatPanel panel;
    35     private JTabbedPane tabs;
    36     private Map<String, ChatPane> chatPanes;
     34    private final GeoChatPanel panel;
     35    private final JTabbedPane tabs;
     36    private final Map<String, ChatPane> chatPanes;
    3737    private boolean collapsed;
    3838
     
    8080        int idx = tabs.indexOfComponent(entry.component);
    8181        if (idx >= 0)
    82             GuiHelper.runInEDT(() -> ((ChatTabTitleComponent) tabs.getTabComponentAt(idx)).updateAlarm());
     82            GuiHelper.runInEDT(((ChatTabTitleComponent) tabs.getTabComponentAt(idx))::updateAlarm);
    8383    }
    8484
     
    177177        if (c == null)
    178178            return null;
    179         for (String user : chatPanes.keySet()) {
    180             if (c.equals(chatPanes.get(user).component))
    181                 return user;
     179        for (Map.Entry<String, ChatPaneManager.ChatPane> entry : chatPanes.entrySet()) {
     180            if (c.equals(entry.getValue().component))
     181                return entry.getKey();
    182182        }
    183183        return null;
     
    226226
    227227    private class ChatTabTitleComponent extends JLabel {
    228         private ChatPane entry;
     228        private final ChatPane entry;
    229229
    230230        ChatTabTitleComponent(ChatPane entry) {
  • applications/editors/josm/plugins/geochat/src/geochat/ChatServerConnection.java

    r36122 r36146  
    77import java.io.UnsupportedEncodingException;
    88import java.net.URLEncoder;
     9import java.net.UnknownHostException;
     10import java.time.Instant;
    911import java.util.ArrayList;
    10 import java.util.Date;
    1112import java.util.HashMap;
    1213import java.util.HashSet;
     
    1415import java.util.Map;
    1516import java.util.Set;
     17import java.util.concurrent.ScheduledThreadPoolExecutor;
     18import java.util.concurrent.TimeUnit;
    1619
    1720import jakarta.json.JsonArray;
     
    2225import org.openstreetmap.josm.data.coor.LatLon;
    2326import org.openstreetmap.josm.data.coor.conversion.DecimalDegreesCoordinateFormat;
     27import org.openstreetmap.josm.data.preferences.JosmUrls;
    2428import org.openstreetmap.josm.data.projection.Projection;
    2529import org.openstreetmap.josm.data.projection.ProjectionRegistry;
    2630import org.openstreetmap.josm.gui.MainApplication;
    2731import org.openstreetmap.josm.gui.MapView;
     32import org.openstreetmap.josm.io.NetworkManager;
     33import org.openstreetmap.josm.io.OnlineResource;
    2834import org.openstreetmap.josm.spi.preferences.Config;
    2935import org.openstreetmap.josm.tools.Logging;
     36import org.openstreetmap.josm.tools.Utils;
    3037
    3138/**
     
    3744    public static final String TOKEN_PREFIX = "=";
    3845    private static final String TOKEN_PATTERN = "^[a-zA-Z0-9]{10}$";
     46    private static final ScheduledThreadPoolExecutor EXECUTOR = new ScheduledThreadPoolExecutor(1);
    3947
    4048    private int userId;
     
    4250    private static ChatServerConnection instance;
    4351    private final Set<ChatServerConnectionListener> listeners;
    44     private final LogRequest requestThread;
    4552
    4653    private ChatServerConnection() {
     
    4855        userName = null;
    4956        listeners = new HashSet<>();
    50         requestThread = new LogRequest();
    51         new Thread(requestThread).start();
     57        LogRequest requestThread = new LogRequest();
     58        final int interval = Config.getPref().getInt("geochat.interval", 2);
     59        EXECUTOR.scheduleAtFixedRate(requestThread, interval, interval, TimeUnit.SECONDS);
    5260    }
    5361
     
    116124            return;
    117125        }
    118         new Thread(() -> {
     126        // Blocking the geochat executor here isn't a big deal, since we need to be logged in for chat anyway.
     127        EXECUTOR.schedule(() -> {
    119128            try {
    120129                int cnt = 10;
     
    127136            }
    128137            autoLogin(userName);
    129         }).start();
     138        }, 200, TimeUnit.MILLISECONDS);
    130139    }
    131140
     
    315324        private long lastId;
    316325        private boolean lastStatus;
    317         private boolean stopping;
    318326
    319327        @Override
    320328        public void run() {
    321329            //            lastId = Config.getPref().getLong("geochat.lastid", 0);
    322             int interval = Config.getPref().getInt("geochat.interval", 2);
    323             while (!stopping) {
     330            if (!NetworkManager.isOffline(OnlineResource.JOSM_WEBSITE) || !Utils.isRunningWebStart()) {
    324331                process();
    325                 try {
    326                     Thread.sleep(interval * 1000L);
    327                 } catch (InterruptedException e) {
    328                     Thread.currentThread().interrupt();
    329                     stopping = true;
    330                     Logging.trace(e);
    331                 }
    332             }
    333         }
    334 
    335         public void stop() {
    336             stopping = true;
    337         }
    338 
    339         public void process() {
     332            }
     333        }
     334
     335        private void process() {
    340336            if (!isLoggedIn()) {
    341337                fireStatusChanged(false);
     
    371367                Logging.trace(ex);
    372368                json = null; // ?
     369                final Throwable root = Utils.getRootCause(ex);
     370                if (root instanceof UnknownHostException) {
     371                    UnknownHostException uhe = (UnknownHostException) root;
     372                    NetworkManager.addNetworkError(uhe.getMessage(), uhe);
     373                    if (JosmUrls.getInstance().getJOSMWebsite().endsWith(uhe.getMessage())) {
     374                        NetworkManager.setOffline(OnlineResource.JOSM_WEBSITE);
     375                    }
     376                }
    373377            }
    374378            if (json == null) {
     
    424428                    boolean incoming = msg.getBoolean("incoming");
    425429                    ChatMessage cm = new ChatMessage(id, new LatLon(lat, lon), author,
    426                             incoming, message, new Date(timeStamp * 1000));
     430                            incoming, message, Instant.ofEpochSecond(timeStamp));
    427431                    cm.setPrivate(priv);
    428432                    if (msg.get("recipient") != null && !incoming)
  • applications/editors/josm/plugins/geochat/src/geochat/GeoChatPanel.java

    r35161 r36146  
    1818import java.awt.RenderingHints;
    1919import java.io.IOException;
    20 import java.text.SimpleDateFormat;
     20import java.time.ZoneId;
     21import java.time.format.DateTimeFormatter;
     22import java.time.format.FormatStyle;
    2123import java.util.List;
    2224import java.util.Map;
     
    5153 */
    5254public class GeoChatPanel extends ToggleDialog implements ChatServerConnectionListener, MapViewPaintable {
    53     private JTextField input;
    54     private JTabbedPane tabs;
    55     private JComponent noData;
    56     private JPanel loginPanel;
    57     private JPanel gcPanel;
    58     private ChatServerConnection connection;
     55    private final JTextField input;
     56    private final JTabbedPane tabs;
     57    private final JComponent noData;
     58    private final JPanel loginPanel;
     59    private final JPanel gcPanel;
     60    private final ChatServerConnection connection;
    5961    // those fields should be visible to popup menu actions
    6062    Map<String, LatLon> users;
     
    6264    boolean userLayerActive;
    6365
     66    /**
     67     * Create a new {@link GeoChatPanel}
     68     */
    6469    public GeoChatPanel() {
    6570        super(tr("GeoChat"), "geochat", tr("Open GeoChat panel"), null, 200, true);
     
    9499        connection = ChatServerConnection.getInstance();
    95100        connection.addListener(this);
    96         boolean autoLogin = Config.getPref().get("geochat.username", null) == null ? false : Config.getPref().getBoolean("geochat.autologin", true);
     101        boolean autoLogin = Config.getPref().get("geochat.username", null) != null && Config.getPref().getBoolean("geochat.autologin", true);
    97102        connection.autoLoginWithDelay(autoLogin ? defaultUserName : null);
    98103        updateTitleAlarm();
    99104    }
    100105
    101     private String constructUserName() {
     106    private static String constructUserName() {
    102107        String userName = Config.getPref().get("geochat.username", null); // so the default is null
    103108        if (userName == null)
     
    187192        FontMetrics fm = g2d.getFontMetrics();
    188193
    189         for (String user : users.keySet()) {
    190             int stringWidth = fm.stringWidth(user);
     194        for (Map.Entry<String, LatLon> entry : users.entrySet()) {
     195            int stringWidth = fm.stringWidth(entry.getKey());
    191196            int radius = stringWidth / 2 + 10;
    192             Point p = mv.getPoint(users.get(user));
     197            Point p = mv.getPoint(entry.getValue());
    193198
    194199            g2d.setComposite(ac04);
     
    198203            g2d.setComposite(ac10);
    199204            g2d.setColor(Color.black);
    200             g2d.drawString(user, p.x - stringWidth / 2, p.y + fm.getDescent());
     205            g2d.drawString(entry.getKey(), p.x - stringWidth / 2, p.y + fm.getDescent());
    201206        }
    202207    }
     
    214219        String comment;
    215220        if (connection.isLoggedIn()) {
    216             comment = trn("{0} user", "{0} users", users.size() + 1, users.size() + 1);
     221            comment = trn("{0} user", "{0} users", users.size() + 1L, users.size() + 1);
    217222        } else {
    218223            comment = tr("not logged in");
     
    293298    }
    294299
    295     private final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm");
    296 
    297     private void formatMessage(StringBuilder sb, ChatMessage msg) {
     300    private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).withZone(ZoneId.systemDefault());
     301
     302    private static void formatMessage(StringBuilder sb, ChatMessage msg) {
    298303        sb.append("\n");
    299304        sb.append('[').append(TIME_FORMAT.format(msg.getTime())).append("] ");
     
    338343                sb.append(first ? " " : ", ");
    339344                sb.append(user);
     345                first = false;
    340346            }
    341347            chatPanes.addLineToPublic(sb.toString(), ChatPaneManager.MESSAGE_TYPE_INFORMATION);
  • applications/editors/josm/plugins/geochat/src/geochat/GeoChatPopupAdapter.java

    r33545 r36146  
    2020 */
    2121class GeoChatPopupAdapter extends MouseAdapter {
    22     private GeoChatPanel panel;
     22    private final GeoChatPanel panel;
    2323
    2424    GeoChatPopupAdapter(GeoChatPanel panel) {
     
    6262
    6363    private class PrivateChatAction extends AbstractAction {
    64         private String userName;
     64        private final String userName;
    6565
    6666        PrivateChatAction(String userName) {
  • applications/editors/josm/plugins/geochat/src/geochat/JPanelTextField.java

    r35163 r36146  
    2121public class JPanelTextField extends DisableShortcutsOnFocusGainedTextField {
    2222
     23    /**
     24     * Create a new {@link JPanelTextField}
     25     */
    2326    public JPanelTextField() {
    24         setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, new HashSet<KeyStroke>());
     27        setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, new HashSet<>());
    2528        standardKeys = getInputMap(JComponent.WHEN_FOCUSED).allKeys();
    2629    }
     
    5154                if (start < caret) {
    5255                    String word = text.substring(start, caret);
    53                     String complete = word == null ? null : autoComplete(word, start == 0);
     56                    String complete = autoComplete(word, start == 0);
    5457                    if (complete != null && !complete.equals(word)) {
    5558                        StringBuilder sb = new StringBuilder();
    5659                        if (start > 0)
    57                             sb.append(text.substring(0, start));
     60                            sb.append(text, 0, start);
    5861                        sb.append(complete);
    5962                        if (caret < text.length())
     
    6366                    }
    6467                }
    65             } else if (code == KeyEvent.VK_ESCAPE) {
    66                 if (MainApplication.isDisplayingMapView())
    67                     MainApplication.getMap().mapView.requestFocus();
     68            } else if (code == KeyEvent.VK_ESCAPE && MainApplication.isDisplayingMapView()) {
     69                MainApplication.getMap().mapView.requestFocus();
    6870            }
    6971
     
    8991     * @param text Contents of the text field.
    9092     */
    91     protected void processEnter(String text) { }
     93    protected void processEnter(String text) {
     94        // Overridden where needed
     95    }
    9296
    9397    /**
  • applications/editors/josm/plugins/geochat/src/geochat/JsonQueryUtil.java

    r36122 r36146  
    66import java.io.InputStream;
    77import java.net.URI;
     8import java.util.ServiceConfigurationError;
    89
    910import jakarta.json.Json;
     
    5253        if (inp == null)
    5354            throw new IOException("Empty response");
    54         try (JsonReader reader = Json.createReader(inp)){
     55        try (JsonReader reader = Json.createReader(inp)) {
    5556            return reader.readObject();
    56         } catch (JsonException e) {
     57        } catch (ServiceConfigurationError | JsonException e) {
    5758            throw new IOException("Failed to parse JSON: " + e.getMessage(), e);
    5859        } finally {
     
    6364    // Asynchronous operation
    6465
    65     private String query;
    66     private JsonQueryCallback callback;
    67 
    68     private JsonQueryUtil() {}
     66    private final String query;
     67    private final JsonQueryCallback callback;
    6968
    7069    private JsonQueryUtil(String query, JsonQueryCallback callback) {
Note: See TracChangeset for help on using the changeset viewer.