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 = new Runnable() { 064 @Override public void run() { 065 ChangesetCache.getInstance().update(downloadedChangesets); 066 } 067 }; 068 if (SwingUtilities.isEventDispatchThread()) { 069 r.run(); 070 } else { 071 try { 072 SwingUtilities.invokeAndWait(r); 073 } catch (InterruptedException e) { 074 Main.warn("InterruptedException in "+getClass().getSimpleName()+" while updating changeset cache"); 075 } catch (InvocationTargetException e) { 076 Throwable t = e.getTargetException(); 077 if (t instanceof RuntimeException) { 078 BugReportExceptionHandler.handleException(t); 079 } else if (t instanceof Exception) { 080 ExceptionUtil.explainException(e); 081 } else { 082 BugReportExceptionHandler.handleException(t); 083 } 084 } 085 } 086 } 087 } 088 089 private RunnableDownloadTask downloadTaskRunnable; 090 091 protected final void setDownloadTask(RunnableDownloadTask downloadTask) { 092 this.downloadTaskRunnable = downloadTask; 093 } 094 095 @Override 096 public final Future<?> download(boolean newLayer, Bounds downloadArea, ProgressMonitor progressMonitor) { 097 return download(); 098 } 099 100 /** 101 * Asynchronously launches the changeset download task. This is equivalent to {@code download(false, null, null)}. 102 * 103 * You can wait for the asynchronous download task to finish by synchronizing on the returned 104 * {@link Future}, but make sure not to freeze up JOSM. Example: 105 * <pre> 106 * Future<?> future = task.download(); 107 * // DON'T run this on the Swing EDT or JOSM will freeze 108 * future.get(); // waits for the dowload task to complete 109 * </pre> 110 * 111 * The following example uses a pattern which is better suited if a task is launched from the Swing EDT: 112 * <pre> 113 * final Future<?> future = task.download(); 114 * Runnable runAfterTask = new Runnable() { 115 * public void run() { 116 * // this is not strictly necessary because of the type of executor service 117 * // Main.worker is initialized with, but it doesn't harm either 118 * // 119 * future.get(); // wait for the download task to complete 120 * doSomethingAfterTheTaskCompleted(); 121 * } 122 * } 123 * Main.worker.submit(runAfterTask); 124 * </pre> 125 * 126 * @return the future representing the asynchronous task 127 */ 128 public final Future<?> download() { 129 return downloadTaskRunnable != null ? Main.worker.submit(downloadTaskRunnable) : null; 130 } 131 132 @Override 133 public final Future<?> loadUrl(boolean newLayer, String url, ProgressMonitor progressMonitor) { 134 return downloadTaskRunnable != null ? Main.worker.submit(downloadTaskRunnable) : null; 135 } 136 137 @Override 138 public final void cancel() { 139 if (downloadTaskRunnable != null) { 140 downloadTaskRunnable.cancel(); 141 } 142 } 143 144 @Override 145 public String getConfirmationMessage(URL url) { 146 return null; 147 } 148}