Ticket #8509: async_v3.patch
| File async_v3.patch, 16.4 KB (added by , 8 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("true")) { 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.layer.OsmDataLayer; 8 import org.openstreetmap.josm.gui.progress.ProgressTaskId; 9 import org.openstreetmap.josm.gui.util.GuiHelper; 10 import org.openstreetmap.josm.io.UploadStrategySpecification; 11 import org.openstreetmap.josm.tools.I18n; 12 13 import javax.swing.*; 14 import java.awt.*; 15 import java.util.Optional; 16 17 /** 18 * Task for uploading primitives using background worker threads 19 * @author udit 20 */ 21 public class AsynchronousUploadPrimitivesTask extends UploadPrimitivesTask { 22 23 /** 24 * Static instance 25 */ 26 private static AsynchronousUploadPrimitivesTask asynchronousUploadPrimitivesTask = null; 27 28 /** 29 * Member fields 30 */ 31 private final ProgressTaskId taskId; 32 private final OsmDataLayer uploadDataLayer; 33 34 /** 35 * Private constructor to restrict creating more Asynchronous upload tasks 36 * 37 * @param uploadStrategySpecification 38 * @param osmDataLayer 39 * @param apiDataSet 40 * @param changeset 41 */ 42 private AsynchronousUploadPrimitivesTask(UploadStrategySpecification uploadStrategySpecification, OsmDataLayer osmDataLayer, APIDataSet apiDataSet, Changeset changeset) { 43 super(uploadStrategySpecification, 44 osmDataLayer, 45 apiDataSet, 46 changeset); 47 48 uploadDataLayer = osmDataLayer; 49 // Create a ProgressTaskId for background upload 50 taskId = new ProgressTaskId("core", "async-upload"); 51 } 52 53 /** 54 * Creates an instance of AsynchronousUploadPrimitiveTask 55 * 56 * @param uploadStrategySpecification 57 * @param dataLayer 58 * @param apiDataSet 59 * @param changeset 60 * @return Optional<AsynchronousUploadPrimitivesTask> 61 */ 62 public static Optional<AsynchronousUploadPrimitivesTask> createAsynchronousUploadTask 63 (UploadStrategySpecification uploadStrategySpecification, 64 OsmDataLayer dataLayer, APIDataSet apiDataSet, Changeset changeset) { 65 synchronized (AsynchronousUploadPrimitivesTask.class) { 66 if (asynchronousUploadPrimitivesTask != null) { 67 if (!GraphicsEnvironment.isHeadless()) { 68 GuiHelper.runInEDTAndWait(() -> 69 JOptionPane.showMessageDialog(MainApplication.parent, 70 I18n.tr("A background upload is already in progress. Kindly wait for it to finish before uploading new changes"))); 71 } 72 return Optional.empty(); 73 } else { 74 // Create an asynchronous upload task 75 asynchronousUploadPrimitivesTask = new AsynchronousUploadPrimitivesTask( 76 uploadStrategySpecification, 77 dataLayer, 78 apiDataSet, 79 changeset); 80 return Optional.ofNullable(asynchronousUploadPrimitivesTask); 81 } 82 } 83 } 84 85 /** 86 * Get the current upload task 87 * @return Optional<AsynchronousUploadPrimitivesTask> 88 */ 89 public static Optional<AsynchronousUploadPrimitivesTask> getCurrentAsynchronousUploadTask () { 90 return Optional.ofNullable(asynchronousUploadPrimitivesTask); 91 } 92 93 @Override 94 public ProgressTaskId canRunInBackground() { 95 return taskId; 96 } 97 98 @Override 99 protected void realRun() { 100 // Lock the data layer before upload in EDT 101 GuiHelper.runInEDTAndWait(() -> { 102 // Remove the commands from the undo stack 103 MainApplication.undoRedo.clean(uploadDataLayer.data); 104 MainApplication.getLayerManager().prepareLayerForUpload(uploadDataLayer); 105 }); 106 super.realRun(); 107 } 108 109 @Override 110 protected void cancel() { 111 super.cancel(); 112 asynchronousUploadPrimitivesTask = null; 113 } 114 115 @Override 116 protected void finish() { 117 try { 118 // Unlock the data layer in EDT 119 GuiHelper.runInEDTAndWait(() -> 120 MainApplication.getLayerManager().processLayerAfterUpload(uploadDataLayer)); 121 super.finish(); 122 } finally { 123 asynchronousUploadPrimitivesTask = null; 124 } 125 } 126 } 127 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 // If the upload layer is the same as active layer 413 // (it implies that edit layer is the same as active layer) 414 if (activeLayer == layer && editLayer == layer) { 415 Layer suggestedLayer = suggestNextActiveLayer(layer); 416 if (suggestedLayer instanceof OsmDataLayer) { 417 // We have another data layer 418 setActiveLayer(suggestedLayer); 419 420 } else if (suggestedLayer != null) { 421 // We have suggested layer but not datalayer (imagery layer etc) 422 // Reset only the edit layer to empty 423 ActiveLayerChangeEvent layerChangeEvent = new ActiveLayerChangeEvent(this, editLayer, activeLayer); 424 activeLayer = suggestedLayer; 425 editLayer = null; 426 fireActiveLayerChange(layerChangeEvent); 427 428 } else { 429 // Don't have a suggested active layer. 430 // Reset active and edit layer to empty 431 ActiveLayerChangeEvent layerChangeEvent = new ActiveLayerChangeEvent(this, editLayer, activeLayer); 432 activeLayer = null; 433 editLayer = null; 434 fireActiveLayerChange(layerChangeEvent); 435 } 436 437 } else if (activeLayer != layer && editLayer == layer) { 438 // Reset only the edit layer as empty 439 ActiveLayerChangeEvent layerChangeEvent = new ActiveLayerChangeEvent(this, editLayer, activeLayer); 440 editLayer = null; 441 fireActiveLayerChange(layerChangeEvent); 442 } 443 } 444 445 public void processLayerAfterUpload (OsmDataLayer layer) { 446 GuiHelper.assertCallFromEdt(); 447 layer.unsetReadOnly(); 448 449 // Set the layer as active and edit layer 450 // iff the active layer is empty. 451 if (activeLayer == null && editLayer == null) { 452 setActiveLayer(layer); 453 } else if (activeLayer != null && editLayer == null) { 454 ActiveLayerChangeEvent layerChangeEvent = new ActiveLayerChangeEvent(this, editLayer, activeLayer); 455 editLayer = layer; 456 fireActiveLayerChange(layerChangeEvent); 457 } 458 } 381 459 } -
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. … … 1142 1144 } 1143 1145 super.setName(name); 1144 1146 } 1147 1148 public void setReadOnly() { 1149 lock.writeLock().lock(); 1150 } 1151 1152 public void unsetReadOnly() { 1153 lock.writeLock().unlock(); 1154 } 1155 1156 public boolean isReadOnly() { 1157 return lock.isWriteLocked(); 1158 } 1145 1159 } -
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 }
