Index: applications/editors/josm/plugins/geochat/src/geochat/ChatMessage.java
===================================================================
--- applications/editors/josm/plugins/geochat/src/geochat/ChatMessage.java	(revision 36145)
+++ applications/editors/josm/plugins/geochat/src/geochat/ChatMessage.java	(revision 36146)
@@ -2,5 +2,5 @@
 package geochat;
 
-import java.util.Date;
+import java.time.Instant;
 
 import org.openstreetmap.josm.data.coor.LatLon;
@@ -12,14 +12,14 @@
  */
 public final class ChatMessage implements Comparable<ChatMessage> {
-    private LatLon pos;
-    private Date time;
-    private String author;
+    private final LatLon pos;
+    private final Instant time;
+    private final String author;
     private String recipient;
-    private String message;
-    private long id;
+    private final String message;
+    private final long id;
     private boolean priv;
-    private boolean incoming;
+    private final boolean incoming;
 
-    public ChatMessage(long id, LatLon pos, String author, boolean incoming, String message, Date time) {
+    public ChatMessage(long id, LatLon pos, String author, boolean incoming, String message, Instant time) {
         this.id = id;
         this.author = author;
@@ -72,5 +72,5 @@
     }
 
-    public Date getTime() {
+    public Instant getTime() {
         return time;
     }
Index: applications/editors/josm/plugins/geochat/src/geochat/ChatPaneManager.java
===================================================================
--- applications/editors/josm/plugins/geochat/src/geochat/ChatPaneManager.java	(revision 36145)
+++ applications/editors/josm/plugins/geochat/src/geochat/ChatPaneManager.java	(revision 36146)
@@ -32,7 +32,7 @@
     private static final String PUBLIC_PANE = "Public Pane";
 
-    private GeoChatPanel panel;
-    private JTabbedPane tabs;
-    private Map<String, ChatPane> chatPanes;
+    private final GeoChatPanel panel;
+    private final JTabbedPane tabs;
+    private final Map<String, ChatPane> chatPanes;
     private boolean collapsed;
 
@@ -80,5 +80,5 @@
         int idx = tabs.indexOfComponent(entry.component);
         if (idx >= 0)
-            GuiHelper.runInEDT(() -> ((ChatTabTitleComponent) tabs.getTabComponentAt(idx)).updateAlarm());
+            GuiHelper.runInEDT(((ChatTabTitleComponent) tabs.getTabComponentAt(idx))::updateAlarm);
     }
 
@@ -177,7 +177,7 @@
         if (c == null)
             return null;
-        for (String user : chatPanes.keySet()) {
-            if (c.equals(chatPanes.get(user).component))
-                return user;
+        for (Map.Entry<String, ChatPaneManager.ChatPane> entry : chatPanes.entrySet()) {
+            if (c.equals(entry.getValue().component))
+                return entry.getKey();
         }
         return null;
@@ -226,5 +226,5 @@
 
     private class ChatTabTitleComponent extends JLabel {
-        private ChatPane entry;
+        private final ChatPane entry;
 
         ChatTabTitleComponent(ChatPane entry) {
Index: applications/editors/josm/plugins/geochat/src/geochat/ChatServerConnection.java
===================================================================
--- applications/editors/josm/plugins/geochat/src/geochat/ChatServerConnection.java	(revision 36145)
+++ applications/editors/josm/plugins/geochat/src/geochat/ChatServerConnection.java	(revision 36146)
@@ -7,6 +7,7 @@
 import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
+import java.net.UnknownHostException;
+import java.time.Instant;
 import java.util.ArrayList;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -14,4 +15,6 @@
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
 
 import jakarta.json.JsonArray;
@@ -22,10 +25,14 @@
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.coor.conversion.DecimalDegreesCoordinateFormat;
+import org.openstreetmap.josm.data.preferences.JosmUrls;
 import org.openstreetmap.josm.data.projection.Projection;
 import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.io.NetworkManager;
+import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.spi.preferences.Config;
 import org.openstreetmap.josm.tools.Logging;
+import org.openstreetmap.josm.tools.Utils;
 
 /**
@@ -37,4 +44,5 @@
     public static final String TOKEN_PREFIX = "=";
     private static final String TOKEN_PATTERN = "^[a-zA-Z0-9]{10}$";
+    private static final ScheduledThreadPoolExecutor EXECUTOR = new ScheduledThreadPoolExecutor(1);
 
     private int userId;
@@ -42,5 +50,4 @@
     private static ChatServerConnection instance;
     private final Set<ChatServerConnectionListener> listeners;
-    private final LogRequest requestThread;
 
     private ChatServerConnection() {
@@ -48,6 +55,7 @@
         userName = null;
         listeners = new HashSet<>();
-        requestThread = new LogRequest();
-        new Thread(requestThread).start();
+        LogRequest requestThread = new LogRequest();
+        final int interval = Config.getPref().getInt("geochat.interval", 2);
+        EXECUTOR.scheduleAtFixedRate(requestThread, interval, interval, TimeUnit.SECONDS);
     }
 
@@ -116,5 +124,6 @@
             return;
         }
-        new Thread(() -> {
+        // Blocking the geochat executor here isn't a big deal, since we need to be logged in for chat anyway.
+        EXECUTOR.schedule(() -> {
             try {
                 int cnt = 10;
@@ -127,5 +136,5 @@
             }
             autoLogin(userName);
-        }).start();
+        }, 200, TimeUnit.MILLISECONDS);
     }
 
@@ -315,27 +324,14 @@
         private long lastId;
         private boolean lastStatus;
-        private boolean stopping;
 
         @Override
         public void run() {
             //            lastId = Config.getPref().getLong("geochat.lastid", 0);
-            int interval = Config.getPref().getInt("geochat.interval", 2);
-            while (!stopping) {
+            if (!NetworkManager.isOffline(OnlineResource.JOSM_WEBSITE) || !Utils.isRunningWebStart()) {
                 process();
-                try {
-                    Thread.sleep(interval * 1000L);
-                } catch (InterruptedException e) {
-                    Thread.currentThread().interrupt();
-                    stopping = true;
-                    Logging.trace(e);
-                }
-            }
-        }
-
-        public void stop() {
-            stopping = true;
-        }
-
-        public void process() {
+            }
+        }
+
+        private void process() {
             if (!isLoggedIn()) {
                 fireStatusChanged(false);
@@ -371,4 +367,12 @@
                 Logging.trace(ex);
                 json = null; // ?
+                final Throwable root = Utils.getRootCause(ex);
+                if (root instanceof UnknownHostException) {
+                    UnknownHostException uhe = (UnknownHostException) root;
+                    NetworkManager.addNetworkError(uhe.getMessage(), uhe);
+                    if (JosmUrls.getInstance().getJOSMWebsite().endsWith(uhe.getMessage())) {
+                        NetworkManager.setOffline(OnlineResource.JOSM_WEBSITE);
+                    }
+                }
             }
             if (json == null) {
@@ -424,5 +428,5 @@
                     boolean incoming = msg.getBoolean("incoming");
                     ChatMessage cm = new ChatMessage(id, new LatLon(lat, lon), author,
-                            incoming, message, new Date(timeStamp * 1000));
+                            incoming, message, Instant.ofEpochSecond(timeStamp));
                     cm.setPrivate(priv);
                     if (msg.get("recipient") != null && !incoming)
Index: applications/editors/josm/plugins/geochat/src/geochat/GeoChatPanel.java
===================================================================
--- applications/editors/josm/plugins/geochat/src/geochat/GeoChatPanel.java	(revision 36145)
+++ applications/editors/josm/plugins/geochat/src/geochat/GeoChatPanel.java	(revision 36146)
@@ -18,5 +18,7 @@
 import java.awt.RenderingHints;
 import java.io.IOException;
-import java.text.SimpleDateFormat;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.format.FormatStyle;
 import java.util.List;
 import java.util.Map;
@@ -51,10 +53,10 @@
  */
 public class GeoChatPanel extends ToggleDialog implements ChatServerConnectionListener, MapViewPaintable {
-    private JTextField input;
-    private JTabbedPane tabs;
-    private JComponent noData;
-    private JPanel loginPanel;
-    private JPanel gcPanel;
-    private ChatServerConnection connection;
+    private final JTextField input;
+    private final JTabbedPane tabs;
+    private final JComponent noData;
+    private final JPanel loginPanel;
+    private final JPanel gcPanel;
+    private final ChatServerConnection connection;
     // those fields should be visible to popup menu actions
     Map<String, LatLon> users;
@@ -62,4 +64,7 @@
     boolean userLayerActive;
 
+    /**
+     * Create a new {@link GeoChatPanel}
+     */
     public GeoChatPanel() {
         super(tr("GeoChat"), "geochat", tr("Open GeoChat panel"), null, 200, true);
@@ -94,10 +99,10 @@
         connection = ChatServerConnection.getInstance();
         connection.addListener(this);
-        boolean autoLogin = Config.getPref().get("geochat.username", null) == null ? false : Config.getPref().getBoolean("geochat.autologin", true);
+        boolean autoLogin = Config.getPref().get("geochat.username", null) != null && Config.getPref().getBoolean("geochat.autologin", true);
         connection.autoLoginWithDelay(autoLogin ? defaultUserName : null);
         updateTitleAlarm();
     }
 
-    private String constructUserName() {
+    private static String constructUserName() {
         String userName = Config.getPref().get("geochat.username", null); // so the default is null
         if (userName == null)
@@ -187,8 +192,8 @@
         FontMetrics fm = g2d.getFontMetrics();
 
-        for (String user : users.keySet()) {
-            int stringWidth = fm.stringWidth(user);
+        for (Map.Entry<String, LatLon> entry : users.entrySet()) {
+            int stringWidth = fm.stringWidth(entry.getKey());
             int radius = stringWidth / 2 + 10;
-            Point p = mv.getPoint(users.get(user));
+            Point p = mv.getPoint(entry.getValue());
 
             g2d.setComposite(ac04);
@@ -198,5 +203,5 @@
             g2d.setComposite(ac10);
             g2d.setColor(Color.black);
-            g2d.drawString(user, p.x - stringWidth / 2, p.y + fm.getDescent());
+            g2d.drawString(entry.getKey(), p.x - stringWidth / 2, p.y + fm.getDescent());
         }
     }
@@ -214,5 +219,5 @@
         String comment;
         if (connection.isLoggedIn()) {
-            comment = trn("{0} user", "{0} users", users.size() + 1, users.size() + 1);
+            comment = trn("{0} user", "{0} users", users.size() + 1L, users.size() + 1);
         } else {
             comment = tr("not logged in");
@@ -293,7 +298,7 @@
     }
 
-    private final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm");
-
-    private void formatMessage(StringBuilder sb, ChatMessage msg) {
+    private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).withZone(ZoneId.systemDefault());
+
+    private static void formatMessage(StringBuilder sb, ChatMessage msg) {
         sb.append("\n");
         sb.append('[').append(TIME_FORMAT.format(msg.getTime())).append("] ");
@@ -338,4 +343,5 @@
                 sb.append(first ? " " : ", ");
                 sb.append(user);
+                first = false;
             }
             chatPanes.addLineToPublic(sb.toString(), ChatPaneManager.MESSAGE_TYPE_INFORMATION);
Index: applications/editors/josm/plugins/geochat/src/geochat/GeoChatPopupAdapter.java
===================================================================
--- applications/editors/josm/plugins/geochat/src/geochat/GeoChatPopupAdapter.java	(revision 36145)
+++ applications/editors/josm/plugins/geochat/src/geochat/GeoChatPopupAdapter.java	(revision 36146)
@@ -20,5 +20,5 @@
  */
 class GeoChatPopupAdapter extends MouseAdapter {
-    private GeoChatPanel panel;
+    private final GeoChatPanel panel;
 
     GeoChatPopupAdapter(GeoChatPanel panel) {
@@ -62,5 +62,5 @@
 
     private class PrivateChatAction extends AbstractAction {
-        private String userName;
+        private final String userName;
 
         PrivateChatAction(String userName) {
Index: applications/editors/josm/plugins/geochat/src/geochat/JPanelTextField.java
===================================================================
--- applications/editors/josm/plugins/geochat/src/geochat/JPanelTextField.java	(revision 36145)
+++ applications/editors/josm/plugins/geochat/src/geochat/JPanelTextField.java	(revision 36146)
@@ -21,6 +21,9 @@
 public class JPanelTextField extends DisableShortcutsOnFocusGainedTextField {
 
+    /**
+     * Create a new {@link JPanelTextField}
+     */
     public JPanelTextField() {
-        setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, new HashSet<KeyStroke>());
+        setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, new HashSet<>());
         standardKeys = getInputMap(JComponent.WHEN_FOCUSED).allKeys();
     }
@@ -51,9 +54,9 @@
                 if (start < caret) {
                     String word = text.substring(start, caret);
-                    String complete = word == null ? null : autoComplete(word, start == 0);
+                    String complete = autoComplete(word, start == 0);
                     if (complete != null && !complete.equals(word)) {
                         StringBuilder sb = new StringBuilder();
                         if (start > 0)
-                            sb.append(text.substring(0, start));
+                            sb.append(text, 0, start);
                         sb.append(complete);
                         if (caret < text.length())
@@ -63,7 +66,6 @@
                     }
                 }
-            } else if (code == KeyEvent.VK_ESCAPE) {
-                if (MainApplication.isDisplayingMapView())
-                    MainApplication.getMap().mapView.requestFocus();
+            } else if (code == KeyEvent.VK_ESCAPE && MainApplication.isDisplayingMapView()) {
+                MainApplication.getMap().mapView.requestFocus();
             }
 
@@ -89,5 +91,7 @@
      * @param text Contents of the text field.
      */
-    protected void processEnter(String text) { }
+    protected void processEnter(String text) {
+        // Overridden where needed
+    }
 
     /**
Index: applications/editors/josm/plugins/geochat/src/geochat/JsonQueryUtil.java
===================================================================
--- applications/editors/josm/plugins/geochat/src/geochat/JsonQueryUtil.java	(revision 36145)
+++ applications/editors/josm/plugins/geochat/src/geochat/JsonQueryUtil.java	(revision 36146)
@@ -6,4 +6,5 @@
 import java.io.InputStream;
 import java.net.URI;
+import java.util.ServiceConfigurationError;
 
 import jakarta.json.Json;
@@ -52,7 +53,7 @@
         if (inp == null)
             throw new IOException("Empty response");
-        try (JsonReader reader = Json.createReader(inp)){
+        try (JsonReader reader = Json.createReader(inp)) {
             return reader.readObject();
-        } catch (JsonException e) {
+        } catch (ServiceConfigurationError | JsonException e) {
             throw new IOException("Failed to parse JSON: " + e.getMessage(), e);
         } finally {
@@ -63,8 +64,6 @@
     // Asynchronous operation
 
-    private String query;
-    private JsonQueryCallback callback;
-
-    private JsonQueryUtil() {}
+    private final String query;
+    private final JsonQueryCallback callback;
 
     private JsonQueryUtil(String query, JsonQueryCallback callback) {
