// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.actions.downloadtasks; import java.awt.Component; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.util.HashSet; import java.util.Set; import java.util.concurrent.Future; import javax.swing.SwingUtilities; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.data.Bounds; import org.openstreetmap.josm.data.osm.Changeset; import org.openstreetmap.josm.data.osm.ChangesetCache; import org.openstreetmap.josm.gui.PleaseWaitRunnable; import org.openstreetmap.josm.gui.progress.ProgressMonitor; import org.openstreetmap.josm.io.OsmServerChangesetReader; import org.openstreetmap.josm.tools.ExceptionUtil; import org.openstreetmap.josm.tools.bugreport.BugReportExceptionHandler; /** * Common abstract implementation of other changeset download tasks. * @since 10124 */ public abstract class AbstractChangesetDownloadTask extends AbstractDownloadTask<Set<Changeset>> { abstract class RunnableDownloadTask extends PleaseWaitRunnable { /** the reader object used to read changesets from the API */ protected final OsmServerChangesetReader reader = new OsmServerChangesetReader(); /** the set of downloaded changesets */ protected final Set<Changeset> downloadedChangesets = new HashSet<>(); /** keeps the last exception thrown in the task, if any */ protected Exception lastException; RunnableDownloadTask(Component parent, String title) { super(parent, title, false /* don't ignore exceptions */); } @Override protected void cancel() { setCanceled(true); synchronized (this) { if (reader != null) { reader.cancel(); } } } protected final void rememberLastException(Exception e) { lastException = e; setFailed(true); } protected final void updateChangesets() { // update the global changeset cache with the downloaded changesets. // this will trigger change events which views are listening to. They // will update their views accordingly. // // Run on the EDT because UI updates are triggered. // Runnable r = () -> ChangesetCache.getInstance().update(downloadedChangesets); if (SwingUtilities.isEventDispatchThread()) { r.run(); } else { try { SwingUtilities.invokeAndWait(r); } catch (InterruptedException e) { Main.warn("InterruptedException in "+getClass().getSimpleName()+" while updating changeset cache"); Thread.currentThread().interrupt(); } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); if (t instanceof RuntimeException) { BugReportExceptionHandler.handleException(t); } else if (t instanceof Exception) { ExceptionUtil.explainException(e); } else { BugReportExceptionHandler.handleException(t); } } } } } private RunnableDownloadTask downloadTaskRunnable; protected final void setDownloadTask(RunnableDownloadTask downloadTask) { this.downloadTaskRunnable = downloadTask; } @Override public final Future<?> download(boolean newLayer, Bounds downloadArea, ProgressMonitor progressMonitor) { return download(); } /** * Asynchronously launches the changeset download task. This is equivalent to {@code download(false, null, null)}. * * You can wait for the asynchronous download task to finish by synchronizing on the returned * {@link Future}, but make sure not to freeze up JOSM. Example: * <pre> * Future<?> future = task.download(); * // DON'T run this on the Swing EDT or JOSM will freeze * future.get(); // waits for the dowload task to complete * </pre> * * The following example uses a pattern which is better suited if a task is launched from the Swing EDT: * <pre> * final Future<?> future = task.download(); * Runnable runAfterTask = new Runnable() { * public void run() { * // this is not strictly necessary because of the type of executor service * // Main.worker is initialized with, but it doesn't harm either * // * future.get(); // wait for the download task to complete * doSomethingAfterTheTaskCompleted(); * } * } * Main.worker.submit(runAfterTask); * </pre> * * @return the future representing the asynchronous task */ public final Future<?> download() { return downloadTaskRunnable != null ? Main.worker.submit(downloadTaskRunnable) : null; } @Override public final Future<?> loadUrl(boolean newLayer, String url, ProgressMonitor progressMonitor) { return downloadTaskRunnable != null ? Main.worker.submit(downloadTaskRunnable) : null; } @Override public final void cancel() { if (downloadTaskRunnable != null) { downloadTaskRunnable.cancel(); } } @Override public String getConfirmationMessage(URL url) { return null; } }