Ticket #8509: async_v2.patch

File async_v2.patch, 13.4 KB (added by udit, 8 years ago)

Patch for Asynchronous uploads in JOSM. This is a version_2.

  • src/org/openstreetmap/josm/actions/UploadAction.java

     
    99import java.util.LinkedList;
    1010import java.util.List;
    1111import java.util.Map;
     12import java.util.Optional;
    1213
    1314import javax.swing.JOptionPane;
    1415
     
    2425import org.openstreetmap.josm.data.osm.Changeset;
    2526import org.openstreetmap.josm.gui.HelpAwareOptionPane;
    2627import org.openstreetmap.josm.gui.MainApplication;
     28import org.openstreetmap.josm.gui.io.AsynchronousUploadPrimitivesTask;
    2729import org.openstreetmap.josm.gui.io.UploadDialog;
    28 import org.openstreetmap.josm.gui.io.UploadPrimitivesTask;
    2930import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
    3031import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    3132import org.openstreetmap.josm.gui.util.GuiHelper;
     
    257258            hook.modifyChangesetTags(changesetTags);
    258259        }
    259260
    260         MainApplication.worker.execute(
    261                 new UploadPrimitivesTask(
    262                         UploadDialog.getUploadDialog().getUploadStrategySpecification(),
    263                         layer,
    264                         apiData,
    265                         cs
    266                 )
    267         );
     261        Optional <AsynchronousUploadPrimitivesTask> asyncUploadTask = AsynchronousUploadPrimitivesTask.createAsynchronousUploadTask(
     262                UploadDialog.getUploadDialog().getUploadStrategySpecification(),
     263                layer,
     264                apiData,
     265                cs);
     266
     267        if (asyncUploadTask.isPresent()) {
     268            MainApplication.worker.execute(asyncUploadTask.get());
     269        }
    268270    }
    269271
    270272    @Override
  • src/org/openstreetmap/josm/gui/io/AsynchronousUploadPrimitivesTask.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.gui.io;
     3
     4import org.openstreetmap.josm.data.APIDataSet;
     5import org.openstreetmap.josm.data.osm.Changeset;
     6import org.openstreetmap.josm.gui.MainApplication;
     7import org.openstreetmap.josm.gui.layer.OsmDataLayer;
     8import org.openstreetmap.josm.gui.progress.ProgressTaskId;
     9import org.openstreetmap.josm.gui.util.GuiHelper;
     10import org.openstreetmap.josm.io.UploadStrategySpecification;
     11import org.openstreetmap.josm.tools.I18n;
     12
     13import javax.swing.*;
     14import java.awt.*;
     15import java.util.Optional;
     16
     17/**
     18 * Task for uploading primitives using background worker threads
     19 * @author udit
     20 */
     21public 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(() -> uploadDataLayer.setReadOnly());
     102        super.realRun();
     103    }
     104
     105    @Override
     106    protected void cancel() {
     107        super.cancel();
     108        asynchronousUploadPrimitivesTask = null;
     109    }
     110
     111    @Override
     112    protected void finish() {
     113        try {
     114            // Unlock the data layer in EDT
     115            GuiHelper.runInEDTAndWait(() -> uploadDataLayer.unsetReadOnly());
     116            super.finish();
     117        } finally {
     118            asynchronousUploadPrimitivesTask = null;
     119        }
     120    }
     121}
     122 No newline at end of file
  • src/org/openstreetmap/josm/gui/layer/MainLayerManager.java

     
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.gui.layer;
    33
     4import org.openstreetmap.josm.data.osm.DataSet;
     5import org.openstreetmap.josm.gui.MainApplication;
     6import org.openstreetmap.josm.gui.util.GuiHelper;
     7
     8import javax.swing.*;
    49import java.util.ArrayList;
    510import java.util.Collection;
    611import java.util.List;
     
    712import java.util.ListIterator;
    813import java.util.concurrent.CopyOnWriteArrayList;
    914
    10 import org.openstreetmap.josm.data.osm.DataSet;
    11 import org.openstreetmap.josm.gui.util.GuiHelper;
     15import static org.openstreetmap.josm.tools.I18n.tr;
    1216
    1317/**
    1418 * This class extends the layer manager by adding an active and an edit layer.
     
    205209    }
    206210
    207211    /**
    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.
    209214     * @param layer The active layer.
    210215     */
    211216    public void setActiveLayer(final Layer layer) {
    212217        // we force this on to the EDT Thread to make events fire from there.
    213218        // 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        }
    215229    }
    216230
    217231    protected synchronized void realSetActiveLayer(final Layer layer) {
     
    309323     * @return the currently active layer (may be null)
    310324     */
    311325    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        }
    313335    }
    314336
    315337    /**
     
    318340     * @return the current edit layer. May be null.
    319341     */
    320342    public synchronized OsmDataLayer getEditLayer() {
    321         return editLayer;
     343        if (editLayer != null && !editLayer.isReadOnly())
     344            return editLayer;
     345        else
     346            return null;
    322347    }
    323348
    324349    /**
     
    326351     * @return That data set, <code>null</code> if there is no edit layer.
    327352     */
    328353    public synchronized DataSet getEditDataSet() {
    329         if (editLayer != null) {
     354        if (editLayer != null && !editLayer.isReadOnly()) {
    330355            return editLayer.data;
    331356        } else {
    332357            return null;
  • src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java

     
    3232import java.util.Set;
    3333import java.util.concurrent.CopyOnWriteArrayList;
    3434import java.util.concurrent.atomic.AtomicInteger;
     35import java.util.concurrent.locks.ReentrantReadWriteLock;
    3536import java.util.regex.Pattern;
    3637
    3738import javax.swing.AbstractAction;
     
    129130
    130131    private boolean requiresSaveToFile;
    131132    private boolean requiresUploadToServer;
     133    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    132134
    133135    /**
    134136     * List of validation errors in this layer.
     
    11421144        }
    11431145        super.setName(name);
    11441146    }
     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    }
    11451159}
  • test/unit/org/openstreetmap/josm/gui/io/AsynchronousUploadPrimitivesTaskTest.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.gui.io;
     3
     4import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
     5import org.junit.*;
     6import org.openstreetmap.josm.data.APIDataSet;
     7import org.openstreetmap.josm.data.coor.LatLon;
     8import org.openstreetmap.josm.data.osm.Changeset;
     9import org.openstreetmap.josm.data.osm.DataSet;
     10import org.openstreetmap.josm.data.osm.Node;
     11import org.openstreetmap.josm.data.osm.Way;
     12import org.openstreetmap.josm.gui.layer.OsmDataLayer;
     13import org.openstreetmap.josm.io.UploadStrategySpecification;
     14import org.openstreetmap.josm.testutils.JOSMTestRules;
     15
     16import java.util.Optional;
     17
     18public 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}