Ticket #8509: async_v4.patch
File async_v4.patch, 15.7 KB (added by , 6 years ago) |
---|
-
src/org/openstreetmap/josm/actions/UploadAction.java
9 9 import java.util.LinkedList; 10 10 import java.util.List; 11 11 import java.util.Map; 12 import java.util.Optional; 12 13 13 14 import javax.swing.JOptionPane; 14 15 … … 24 25 import org.openstreetmap.josm.data.osm.Changeset; 25 26 import org.openstreetmap.josm.gui.HelpAwareOptionPane; 26 27 import org.openstreetmap.josm.gui.MainApplication; 28 import org.openstreetmap.josm.gui.io.AsynchronousUploadPrimitivesTask; 27 29 import org.openstreetmap.josm.gui.io.UploadDialog; 28 30 import org.openstreetmap.josm.gui.io.UploadPrimitivesTask; 29 31 import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer; … … 57 59 private static final List<UploadHook> UPLOAD_HOOKS = new LinkedList<>(); 58 60 private static final List<UploadHook> LATE_UPLOAD_HOOKS = new LinkedList<>(); 59 61 62 private static final String IS_ASYNC_UPLOAD_ENABLED = "asynchronous.upload"; 63 60 64 static { 61 65 /** 62 66 * Calls validator before upload. … … 257 261 hook.modifyChangesetTags(changesetTags); 258 262 } 259 263 260 MainApplication.worker.execute( 261 new UploadPrimitivesTask( 262 UploadDialog.getUploadDialog().getUploadStrategySpecification(), 263 layer, 264 apiData, 265 cs 266 ) 267 ); 264 if (Main.pref.get(IS_ASYNC_UPLOAD_ENABLED).isEmpty() || 265 !Main.pref.get(IS_ASYNC_UPLOAD_ENABLED).equalsIgnoreCase("disabled")) { 266 Optional <AsynchronousUploadPrimitivesTask> asyncUploadTask = AsynchronousUploadPrimitivesTask.createAsynchronousUploadTask( 267 UploadDialog.getUploadDialog().getUploadStrategySpecification(), 268 layer, 269 apiData, 270 cs); 271 272 if (asyncUploadTask.isPresent()) { 273 MainApplication.worker.execute(asyncUploadTask.get()); 274 } 275 } else { 276 MainApplication.worker.execute( 277 new UploadPrimitivesTask( 278 UploadDialog.getUploadDialog().getUploadStrategySpecification(), 279 layer, 280 apiData, 281 cs)); 282 } 268 283 } 269 284 270 285 @Override -
src/org/openstreetmap/josm/gui/io/AsynchronousUploadPrimitivesTask.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.io; 3 4 import org.openstreetmap.josm.data.APIDataSet; 5 import org.openstreetmap.josm.data.osm.Changeset; 6 import org.openstreetmap.josm.gui.MainApplication; 7 import org.openstreetmap.josm.gui.dialogs.LayerListDialog; 8 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 9 import org.openstreetmap.josm.gui.progress.ProgressTaskId; 10 import org.openstreetmap.josm.gui.util.GuiHelper; 11 import org.openstreetmap.josm.io.UploadStrategySpecification; 12 import org.openstreetmap.josm.tools.I18n; 13 14 import javax.swing.*; 15 import java.awt.*; 16 import java.util.Optional; 17 18 /** 19 * Task for uploading primitives using background worker threads 20 * @author udit 21 */ 22 public class AsynchronousUploadPrimitivesTask extends UploadPrimitivesTask { 23 24 /** 25 * Static instance 26 */ 27 private static AsynchronousUploadPrimitivesTask asynchronousUploadPrimitivesTask = null; 28 29 /** 30 * Member fields 31 */ 32 private final ProgressTaskId taskId; 33 private final OsmDataLayer uploadDataLayer; 34 35 /** 36 * Private constructor to restrict creating more Asynchronous upload tasks 37 * 38 * @param uploadStrategySpecification 39 * @param osmDataLayer 40 * @param apiDataSet 41 * @param changeset 42 */ 43 private AsynchronousUploadPrimitivesTask(UploadStrategySpecification uploadStrategySpecification, OsmDataLayer osmDataLayer, APIDataSet apiDataSet, Changeset changeset) { 44 super(uploadStrategySpecification, 45 osmDataLayer, 46 apiDataSet, 47 changeset); 48 49 uploadDataLayer = osmDataLayer; 50 // Create a ProgressTaskId for background upload 51 taskId = new ProgressTaskId("core", "async-upload"); 52 } 53 54 /** 55 * Creates an instance of AsynchronousUploadPrimitiveTask 56 * 57 * @param uploadStrategySpecification 58 * @param dataLayer 59 * @param apiDataSet 60 * @param changeset 61 * @return Optional<AsynchronousUploadPrimitivesTask> 62 */ 63 public static Optional<AsynchronousUploadPrimitivesTask> createAsynchronousUploadTask 64 (UploadStrategySpecification uploadStrategySpecification, 65 OsmDataLayer dataLayer, APIDataSet apiDataSet, Changeset changeset) { 66 synchronized (AsynchronousUploadPrimitivesTask.class) { 67 if (asynchronousUploadPrimitivesTask != null) { 68 if (!GraphicsEnvironment.isHeadless()) { 69 GuiHelper.runInEDTAndWait(() -> 70 JOptionPane.showMessageDialog(MainApplication.parent, 71 I18n.tr("A background upload is already in progress. Kindly wait for it to finish before uploading new changes"))); 72 } 73 return Optional.empty(); 74 } else { 75 // Create an asynchronous upload task 76 asynchronousUploadPrimitivesTask = new AsynchronousUploadPrimitivesTask( 77 uploadStrategySpecification, 78 dataLayer, 79 apiDataSet, 80 changeset); 81 return Optional.ofNullable(asynchronousUploadPrimitivesTask); 82 } 83 } 84 } 85 86 /** 87 * Get the current upload task 88 * @return Optional<AsynchronousUploadPrimitivesTask> 89 */ 90 public static Optional<AsynchronousUploadPrimitivesTask> getCurrentAsynchronousUploadTask () { 91 return Optional.ofNullable(asynchronousUploadPrimitivesTask); 92 } 93 94 @Override 95 public ProgressTaskId canRunInBackground() { 96 return taskId; 97 } 98 99 @Override 100 protected void realRun() { 101 // Lock the data layer before upload in EDT 102 GuiHelper.runInEDTAndWait(() -> { 103 // Remove the commands from the undo stack 104 MainApplication.undoRedo.clean(uploadDataLayer.data); 105 MainApplication.getLayerManager().prepareLayerForUpload(uploadDataLayer); 106 107 // Repainting the Layer List dialog to update the icon of the active layer 108 LayerListDialog.getInstance().repaint(); 109 }); 110 super.realRun(); 111 } 112 113 @Override 114 protected void cancel() { 115 super.cancel(); 116 asynchronousUploadPrimitivesTask = null; 117 } 118 119 @Override 120 protected void finish() { 121 try { 122 // Unlock the data layer in EDT 123 GuiHelper.runInEDTAndWait(() -> { 124 MainApplication.getLayerManager().processLayerAfterUpload(uploadDataLayer); 125 LayerListDialog.getInstance().repaint(); 126 }); 127 super.finish(); 128 } finally { 129 asynchronousUploadPrimitivesTask = null; 130 } 131 } 132 } 133 No newline at end of file -
src/org/openstreetmap/josm/gui/layer/MainLayerManager.java
1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.gui.layer; 3 3 4 import org.openstreetmap.josm.data.osm.DataSet; 5 import org.openstreetmap.josm.gui.MainApplication; 6 import org.openstreetmap.josm.gui.util.GuiHelper; 7 8 import javax.swing.*; 4 9 import java.util.ArrayList; 5 10 import java.util.Collection; 6 11 import java.util.List; … … 7 12 import java.util.ListIterator; 8 13 import java.util.concurrent.CopyOnWriteArrayList; 9 14 10 import org.openstreetmap.josm.data.osm.DataSet; 11 import org.openstreetmap.josm.gui.util.GuiHelper; 15 import static org.openstreetmap.josm.tools.I18n.tr; 12 16 13 17 /** 14 18 * This class extends the layer manager by adding an active and an edit layer. … … 205 209 } 206 210 207 211 /** 208 * Set the active layer. If the layer is an OsmDataLayer, the edit layer is also changed. 212 * Set the active layer iff the layer is not read only. 213 * If the layer is an OsmDataLayer, the edit layer is also changed. 209 214 * @param layer The active layer. 210 215 */ 211 216 public void setActiveLayer(final Layer layer) { 212 217 // we force this on to the EDT Thread to make events fire from there. 213 218 // The synchronization lock needs to be held by the EDT. 214 GuiHelper.runInEDTAndWaitWithException(() -> realSetActiveLayer(layer)); 219 if (layer instanceof OsmDataLayer && ((OsmDataLayer) layer).isReadOnly()) { 220 GuiHelper.runInEDT(() -> 221 JOptionPane.showMessageDialog( 222 MainApplication.parent, 223 tr("Trying to set a read only data layer as edit layer"), 224 tr("Warning"), 225 JOptionPane.WARNING_MESSAGE)); 226 } else { 227 GuiHelper.runInEDTAndWaitWithException(() -> realSetActiveLayer(layer)); 228 } 215 229 } 216 230 217 231 protected synchronized void realSetActiveLayer(final Layer layer) { … … 309 323 * @return the currently active layer (may be null) 310 324 */ 311 325 public synchronized Layer getActiveLayer() { 312 return activeLayer; 326 if (activeLayer instanceof OsmDataLayer) { 327 if (!((OsmDataLayer)activeLayer).isReadOnly()) { 328 return activeLayer; 329 } else { 330 return null; 331 } 332 } else { 333 return activeLayer; 334 } 313 335 } 314 336 315 337 /** … … 318 340 * @return the current edit layer. May be null. 319 341 */ 320 342 public synchronized OsmDataLayer getEditLayer() { 321 return editLayer; 343 if (editLayer != null && !editLayer.isReadOnly()) 344 return editLayer; 345 else 346 return null; 322 347 } 323 348 324 349 /** … … 378 403 activeLayerChangeListeners.clear(); 379 404 layerAvailabilityListeners.clear(); 380 405 } 406 407 public void prepareLayerForUpload (OsmDataLayer layer) { 408 409 GuiHelper.assertCallFromEdt(); 410 layer.setReadOnly(); 411 412 // Reset only the edit layer as empty 413 if (editLayer == layer) { 414 ActiveLayerChangeEvent layerChangeEvent = new ActiveLayerChangeEvent(this, editLayer, activeLayer); 415 editLayer = null; 416 fireActiveLayerChange(layerChangeEvent); 417 } 418 } 419 420 public void processLayerAfterUpload (OsmDataLayer layer) { 421 GuiHelper.assertCallFromEdt(); 422 layer.unsetReadOnly(); 423 424 // Set the layer as edit layer 425 // iff the edit layer is empty. 426 if (editLayer == null) { 427 ActiveLayerChangeEvent layerChangeEvent = new ActiveLayerChangeEvent(this, editLayer, activeLayer); 428 editLayer = layer; 429 fireActiveLayerChange(layerChangeEvent); 430 } 431 } 381 432 } -
src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
32 32 import java.util.Set; 33 33 import java.util.concurrent.CopyOnWriteArrayList; 34 34 import java.util.concurrent.atomic.AtomicInteger; 35 import java.util.concurrent.locks.ReentrantReadWriteLock; 35 36 import java.util.regex.Pattern; 36 37 37 38 import javax.swing.AbstractAction; … … 129 130 130 131 private boolean requiresSaveToFile; 131 132 private boolean requiresUploadToServer; 133 private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); 132 134 133 135 /** 134 136 * List of validation errors in this layer. … … 420 422 if (isUploadDiscouraged() || data.getUploadPolicy() == UploadPolicy.BLOCKED) { 421 423 base.addOverlay(new ImageOverlay(new ImageProvider("warning-small"), 0.5, 0.5, 1.0, 1.0)); 422 424 } 425 426 if (isReadOnly()) { 427 // If the layer is read only then change the default icon to a clock 428 base = new ImageProvider("clock").setMaxSize(ImageSizes.LAYER); 429 } 423 430 return base.get(); 424 431 } 425 432 … … 1142 1149 } 1143 1150 super.setName(name); 1144 1151 } 1152 1153 public void setReadOnly() { 1154 lock.writeLock().lock(); 1155 } 1156 1157 public void unsetReadOnly() { 1158 lock.writeLock().unlock(); 1159 } 1160 1161 public boolean isReadOnly() { 1162 return lock.isWriteLocked(); 1163 } 1145 1164 } -
test/unit/org/openstreetmap/josm/gui/io/AsynchronousUploadPrimitivesTaskTest.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.io; 3 4 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 5 import org.junit.*; 6 import org.openstreetmap.josm.data.APIDataSet; 7 import org.openstreetmap.josm.data.coor.LatLon; 8 import org.openstreetmap.josm.data.osm.Changeset; 9 import org.openstreetmap.josm.data.osm.DataSet; 10 import org.openstreetmap.josm.data.osm.Node; 11 import org.openstreetmap.josm.data.osm.Way; 12 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 13 import org.openstreetmap.josm.io.UploadStrategySpecification; 14 import org.openstreetmap.josm.testutils.JOSMTestRules; 15 16 import java.util.Optional; 17 18 public class AsynchronousUploadPrimitivesTaskTest { 19 20 private UploadStrategySpecification strategy; 21 private OsmDataLayer layer; 22 private APIDataSet toUpload; 23 private Changeset changeset; 24 private AsynchronousUploadPrimitivesTask uploadPrimitivesTask; 25 26 /** 27 * Setup tests 28 */ 29 @Rule 30 @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD") 31 public JOSMTestRules test = new JOSMTestRules(); 32 33 @Before 34 public void bootStrap() { 35 DataSet dataSet = new DataSet(); 36 Node node1 = new Node(); 37 Node node2 = new Node(); 38 node1.setCoor(new LatLon(0, 0)); 39 node2.setCoor(new LatLon(30, 30)); 40 Way way = new Way(); 41 way.addNode(node1); 42 way.addNode(node2); 43 dataSet.addPrimitive(node1); 44 dataSet.addPrimitive(node2); 45 dataSet.addPrimitive(way); 46 47 toUpload = new APIDataSet(dataSet); 48 layer = new OsmDataLayer(dataSet, "uploadTest", null); 49 strategy = new UploadStrategySpecification(); 50 changeset = new Changeset(); 51 uploadPrimitivesTask = AsynchronousUploadPrimitivesTask.createAsynchronousUploadTask(strategy, layer, toUpload, changeset).get(); 52 } 53 54 @After 55 public void tearDown () { 56 toUpload = null; 57 layer = null; 58 strategy = null; 59 changeset = null; 60 uploadPrimitivesTask = null; 61 } 62 63 @Test 64 public void testSingleUploadInstance () { 65 Optional<AsynchronousUploadPrimitivesTask> task = AsynchronousUploadPrimitivesTask.createAsynchronousUploadTask(strategy, layer, toUpload, changeset); 66 Assert.assertNotNull(uploadPrimitivesTask); 67 Assert.assertFalse(task.isPresent()); 68 } 69 }