001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.actions.downloadtasks; 003 004import java.awt.Component; 005import java.lang.reflect.InvocationTargetException; 006import java.net.URL; 007import java.util.HashSet; 008import java.util.Set; 009import java.util.concurrent.Future; 010 011import javax.swing.SwingUtilities; 012 013import org.openstreetmap.josm.Main; 014import org.openstreetmap.josm.data.Bounds; 015import org.openstreetmap.josm.data.osm.Changeset; 016import org.openstreetmap.josm.data.osm.ChangesetCache; 017import org.openstreetmap.josm.gui.PleaseWaitRunnable; 018import org.openstreetmap.josm.gui.progress.ProgressMonitor; 019import org.openstreetmap.josm.io.OsmServerChangesetReader; 020import org.openstreetmap.josm.tools.ExceptionUtil; 021import org.openstreetmap.josm.tools.bugreport.BugReportExceptionHandler; 022 023/** 024 * Common abstract implementation of other changeset download tasks. 025 * @since 10124 026 */ 027public abstract class AbstractChangesetDownloadTask extends AbstractDownloadTask<Set<Changeset>> { 028 029 abstract class RunnableDownloadTask extends PleaseWaitRunnable { 030 /** the reader object used to read changesets from the API */ 031 protected final OsmServerChangesetReader reader = new OsmServerChangesetReader(); 032 /** the set of downloaded changesets */ 033 protected final Set<Changeset> downloadedChangesets = new HashSet<>(); 034 /** keeps the last exception thrown in the task, if any */ 035 protected Exception lastException; 036 037 RunnableDownloadTask(Component parent, String title) { 038 super(parent, title, false /* don't ignore exceptions */); 039 } 040 041 @Override 042 protected void cancel() { 043 setCanceled(true); 044 synchronized (this) { 045 if (reader != null) { 046 reader.cancel(); 047 } 048 } 049 } 050 051 protected final void rememberLastException(Exception e) { 052 lastException = e; 053 setFailed(true); 054 } 055 056 protected final void updateChangesets() { 057 // update the global changeset cache with the downloaded changesets. 058 // this will trigger change events which views are listening to. They 059 // will update their views accordingly. 060 // 061 // Run on the EDT because UI updates are triggered. 062 // 063 Runnable r = () -> ChangesetCache.getInstance().update(downloadedChangesets); 064 if (SwingUtilities.isEventDispatchThread()) { 065 r.run(); 066 } else { 067 try { 068 SwingUtilities.invokeAndWait(r); 069 } catch (InterruptedException e) { 070 Main.warn("InterruptedException in "+getClass().getSimpleName()+" while updating changeset cache"); 071 } catch (InvocationTargetException e) { 072 Throwable t = e.getTargetException(); 073 if (t instanceof RuntimeException) { 074 BugReportExceptionHandler.handleException(t); 075 } else if (t instanceof Exception) { 076 ExceptionUtil.explainException(e); 077 } else { 078 BugReportExceptionHandler.handleException(t); 079 } 080 } 081 } 082 } 083 } 084 085 private RunnableDownloadTask downloadTaskRunnable; 086 087 protected final void setDownloadTask(RunnableDownloadTask downloadTask) { 088 this.downloadTaskRunnable = downloadTask; 089 } 090 091 @Override 092 public final Future<?> download(boolean newLayer, Bounds downloadArea, ProgressMonitor progressMonitor) { 093 return download(); 094 } 095 096 /** 097 * Asynchronously launches the changeset download task. This is equivalent to {@code download(false, null, null)}. 098 * 099 * You can wait for the asynchronous download task to finish by synchronizing on the returned 100 * {@link Future}, but make sure not to freeze up JOSM. Example: 101 * <pre> 102 * Future<?> future = task.download(); 103 * // DON'T run this on the Swing EDT or JOSM will freeze 104 * future.get(); // waits for the dowload task to complete 105 * </pre> 106 * 107 * The following example uses a pattern which is better suited if a task is launched from the Swing EDT: 108 * <pre> 109 * final Future<?> future = task.download(); 110 * Runnable runAfterTask = new Runnable() { 111 * public void run() { 112 * // this is not strictly necessary because of the type of executor service 113 * // Main.worker is initialized with, but it doesn't harm either 114 * // 115 * future.get(); // wait for the download task to complete 116 * doSomethingAfterTheTaskCompleted(); 117 * } 118 * } 119 * Main.worker.submit(runAfterTask); 120 * </pre> 121 * 122 * @return the future representing the asynchronous task 123 */ 124 public final Future<?> download() { 125 return downloadTaskRunnable != null ? Main.worker.submit(downloadTaskRunnable) : null; 126 } 127 128 @Override 129 public final Future<?> loadUrl(boolean newLayer, String url, ProgressMonitor progressMonitor) { 130 return downloadTaskRunnable != null ? Main.worker.submit(downloadTaskRunnable) : null; 131 } 132 133 @Override 134 public final void cancel() { 135 if (downloadTaskRunnable != null) { 136 downloadTaskRunnable.cancel(); 137 } 138 } 139 140 @Override 141 public String getConfirmationMessage(URL url) { 142 return null; 143 } 144}