Index: /trunk/src/org/openstreetmap/josm/actions/CloseChangesetAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/CloseChangesetAction.java	(revision 14325)
+++ /trunk/src/org/openstreetmap/josm/actions/CloseChangesetAction.java	(revision 14326)
@@ -23,4 +23,5 @@
 import org.openstreetmap.josm.gui.io.CloseChangesetTask;
 import org.openstreetmap.josm.io.ChangesetQuery;
+import org.openstreetmap.josm.io.ChangesetUpdater;
 import org.openstreetmap.josm.io.NetworkManager;
 import org.openstreetmap.josm.io.OnlineResource;
@@ -62,4 +63,5 @@
 
     protected void onPostDownloadOpenChangesets() {
+        ChangesetUpdater.check();
         List<Changeset> openChangesets = ChangesetCache.getInstance().getOpenChangesetsForCurrentUser();
         if (openChangesets.isEmpty()) {
Index: /trunk/src/org/openstreetmap/josm/actions/UploadAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/UploadAction.java	(revision 14325)
+++ /trunk/src/org/openstreetmap/josm/actions/UploadAction.java	(revision 14326)
@@ -31,4 +31,5 @@
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.io.ChangesetUpdater;
 import org.openstreetmap.josm.io.UploadStrategySpecification;
 import org.openstreetmap.josm.spi.preferences.Config;
@@ -242,4 +243,6 @@
             return;
 
+        ChangesetUpdater.check();
+
         final UploadDialog dialog = UploadDialog.getUploadDialog();
         dialog.setChangesetTags(layer.getDataSet());
Index: /trunk/src/org/openstreetmap/josm/gui/MainApplication.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 14325)
+++ /trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 14326)
@@ -130,4 +130,5 @@
 import org.openstreetmap.josm.io.CachedFile;
 import org.openstreetmap.josm.io.CertificateAmendment;
+import org.openstreetmap.josm.io.ChangesetUpdater;
 import org.openstreetmap.josm.io.DefaultProxySelector;
 import org.openstreetmap.josm.io.FileWatcher;
@@ -952,4 +953,6 @@
         }
 
+        ChangesetUpdater.start();
+
         if (Config.getPref().getBoolean("debug.edt-checker.enable", Version.getInstance().isLocalBuild())) {
             // Repaint manager is registered so late for a reason - there is lots of violation during startup process
Index: /trunk/src/org/openstreetmap/josm/gui/MainTermination.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/MainTermination.java	(revision 14325)
+++ /trunk/src/org/openstreetmap/josm/gui/MainTermination.java	(revision 14326)
@@ -9,4 +9,7 @@
 import org.openstreetmap.josm.data.Preferences;
 import org.openstreetmap.josm.data.cache.JCSCacheManager;
+import org.openstreetmap.josm.io.ChangesetUpdater;
+import org.openstreetmap.josm.io.MessageNotifier;
+import org.openstreetmap.josm.io.remotecontrol.RemoteControl;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.Logging;
@@ -20,4 +23,8 @@
     @Override
     public void run() {
+        ChangesetUpdater.stop();
+        MessageNotifier.stop();
+        RemoteControl.stop();
+
         try {
             MainApplication.worker.shutdown();
Index: /trunk/src/org/openstreetmap/josm/io/ChangesetUpdater.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/ChangesetUpdater.java	(revision 14326)
+++ /trunk/src/org/openstreetmap/josm/io/ChangesetUpdater.java	(revision 14326)
@@ -0,0 +1,103 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.io;
+
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import org.openstreetmap.josm.data.osm.Changeset;
+import org.openstreetmap.josm.data.osm.ChangesetCache;
+import org.openstreetmap.josm.data.preferences.IntegerProperty;
+import org.openstreetmap.josm.tools.Logging;
+import org.openstreetmap.josm.tools.Utils;
+
+/**
+ * Checks periodically if open changesets have been closed on server side.
+ * @since 14326
+ */
+public final class ChangesetUpdater {
+
+    private ChangesetUpdater() {
+        // Hide default constructor for utils classes
+    }
+
+    /** Property defining the update interval in minutes */
+    public static final IntegerProperty PROP_INTERVAL = new IntegerProperty("changeset.updater.interval", 60);
+
+    private static final ScheduledExecutorService EXECUTOR =
+            Executors.newSingleThreadScheduledExecutor(Utils.newThreadFactory("changeset-updater-%d", Thread.NORM_PRIORITY));
+
+    private static final Runnable WORKER = new Worker();
+
+    private static volatile ScheduledFuture<?> task;
+
+    private static class Worker implements Runnable {
+
+        private long lastTimeInMillis;
+
+        @Override
+        public void run() {
+            long currentTime = System.currentTimeMillis();
+            // See #14671 - Make sure we don't run the API call many times after system wakeup
+            if (currentTime >= lastTimeInMillis + TimeUnit.MINUTES.toMillis(PROP_INTERVAL.get())) {
+                lastTimeInMillis = currentTime;
+                check();
+            }
+        }
+    }
+
+    /**
+     * Checks for open changesets that have been closed on server side, and update Changeset cache if needed.
+     */
+    public static void check() {
+        long now = System.currentTimeMillis();
+        List<Long> changesetIds = ChangesetCache.getInstance().getOpenChangesets().stream()
+            .filter(x -> x.getCreatedAt() != null
+                && now - x.getCreatedAt().getTime() > TimeUnit.HOURS.toMillis(1))
+            .map(Changeset::getId)
+            .map(Integer::longValue)
+            .collect(Collectors.toList());
+        if (!changesetIds.isEmpty()) {
+            try {
+                ChangesetCache.getInstance().update(new OsmServerChangesetReader().queryChangesets(
+                        new ChangesetQuery().forChangesetIds(changesetIds), null));
+            } catch (OsmTransferException e) {
+                Logging.warn(e);
+            }
+        }
+    }
+
+    /**
+     * Starts the changeset updater task if not already started
+     */
+    public static void start() {
+        int interval = PROP_INTERVAL.get();
+        if (!isRunning() && interval > 0) {
+            task = EXECUTOR.scheduleAtFixedRate(WORKER, 0, interval, TimeUnit.MINUTES);
+            Logging.info("Changeset updater active (checks every "+interval+" minute"+(interval > 1 ? "s" : "") +
+                    " if open changesets have been closed)");
+        }
+    }
+
+    /**
+     * Stops the changeset updater task if started
+     */
+    public static void stop() {
+        if (isRunning()) {
+            task.cancel(false);
+            Logging.info("Changeset updater inactive");
+            task = null;
+        }
+    }
+
+    /**
+     * Determines if the changeset updater is currently running
+     * @return {@code true} if the updater is running, {@code false} otherwise
+     */
+    public static boolean isRunning() {
+        return task != null;
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/io/MessageNotifier.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/MessageNotifier.java	(revision 14325)
+++ /trunk/src/org/openstreetmap/josm/io/MessageNotifier.java	(revision 14326)
@@ -102,5 +102,5 @@
             Logging.info(tr("{0} not available (offline mode)", tr("Message notifier")));
         } else if (!isRunning() && interval > 0 && isUserEnoughIdentified()) {
-            task = EXECUTOR.scheduleAtFixedRate(WORKER, 0, TimeUnit.MINUTES.toSeconds(interval), TimeUnit.SECONDS);
+            task = EXECUTOR.scheduleAtFixedRate(WORKER, 0, interval, TimeUnit.MINUTES);
             Logging.info("Message notifier active (checks every "+interval+" minute"+(interval > 1 ? "s" : "")+')');
         }
