001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.io; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.util.Optional; 007 008import javax.swing.JOptionPane; 009 010import org.openstreetmap.josm.data.APIDataSet; 011import org.openstreetmap.josm.data.UndoRedoHandler; 012import org.openstreetmap.josm.data.osm.Changeset; 013import org.openstreetmap.josm.gui.MainApplication; 014import org.openstreetmap.josm.gui.dialogs.LayerListDialog; 015import org.openstreetmap.josm.gui.layer.OsmDataLayer; 016import org.openstreetmap.josm.gui.progress.ProgressTaskId; 017import org.openstreetmap.josm.gui.util.GuiHelper; 018import org.openstreetmap.josm.io.UploadStrategySpecification; 019 020/** 021 * Task for uploading primitives using background worker threads. The actual upload is delegated to the 022 * {@link UploadPrimitivesTask}. This class is a wrapper over that to make the background upload process safe. There 023 * can only be one instance of this class, hence background uploads are limited to one at a time. This class also 024 * changes the editLayer of {@link org.openstreetmap.josm.gui.layer.MainLayerManager} to null during upload so that 025 * any changes to the uploading layer are prohibited. 026 * 027 * @author udit 028 * @since 13133 029 */ 030public final class AsynchronousUploadPrimitivesTask extends UploadPrimitivesTask { 031 032 /** 033 * Static instance 034 */ 035 private static AsynchronousUploadPrimitivesTask asynchronousUploadPrimitivesTask; 036 037 /** 038 * Member fields 039 */ 040 private final ProgressTaskId taskId; 041 private final OsmDataLayer uploadDataLayer; 042 043 /** 044 * Private constructor to restrict creating more Asynchronous upload tasks 045 * 046 * @param uploadStrategySpecification UploadStrategySpecification for the DataLayer 047 * @param osmDataLayer Datalayer to be uploaded 048 * @param apiDataSet ApiDataSet that contains the primitives to be uploaded 049 * @param changeset Changeset for the datalayer 050 * 051 * @throws IllegalArgumentException if layer is null 052 * @throws IllegalArgumentException if toUpload is null 053 * @throws IllegalArgumentException if strategy is null 054 * @throws IllegalArgumentException if changeset is null 055 */ 056 private AsynchronousUploadPrimitivesTask(UploadStrategySpecification uploadStrategySpecification, 057 OsmDataLayer osmDataLayer, APIDataSet apiDataSet, Changeset changeset) { 058 super(uploadStrategySpecification, 059 osmDataLayer, 060 apiDataSet, 061 changeset); 062 063 uploadDataLayer = osmDataLayer; 064 // Create a ProgressTaskId for background upload 065 taskId = new ProgressTaskId("core", "async-upload"); 066 } 067 068 /** 069 * Creates an instance of AsynchronousUploadPrimitiveTask 070 * 071 * @param uploadStrategySpecification UploadStrategySpecification for the DataLayer 072 * @param dataLayer Datalayer to be uploaded 073 * @param apiDataSet ApiDataSet that contains the primitives to be uploaded 074 * @param changeset Changeset for the datalayer 075 * @return Returns an {@literal Optional<AsynchronousUploadPrimitivesTask> } if there is no 076 * background upload in progress. Otherwise returns an {@literal Optional.empty()} 077 * 078 * @throws IllegalArgumentException if layer is null 079 * @throws IllegalArgumentException if toUpload is null 080 * @throws IllegalArgumentException if strategy is null 081 * @throws IllegalArgumentException if changeset is null 082 */ 083 public static Optional<AsynchronousUploadPrimitivesTask> createAsynchronousUploadTask( 084 UploadStrategySpecification uploadStrategySpecification, 085 OsmDataLayer dataLayer, APIDataSet apiDataSet, Changeset changeset) { 086 synchronized (AsynchronousUploadPrimitivesTask.class) { 087 if (asynchronousUploadPrimitivesTask != null) { 088 GuiHelper.runInEDTAndWait(() -> 089 JOptionPane.showMessageDialog(MainApplication.getMainFrame(), 090 tr("A background upload is already in progress. " + 091 "Kindly wait for it to finish before uploading new changes"))); 092 return Optional.empty(); 093 } else { 094 // Create an asynchronous upload task 095 asynchronousUploadPrimitivesTask = new AsynchronousUploadPrimitivesTask( 096 uploadStrategySpecification, 097 dataLayer, 098 apiDataSet, 099 changeset); 100 return Optional.ofNullable(asynchronousUploadPrimitivesTask); 101 } 102 } 103 } 104 105 /** 106 * Get the current upload task 107 * @return {@literal Optional<AsynchronousUploadPrimitivesTask> } 108 */ 109 public static Optional<AsynchronousUploadPrimitivesTask> getCurrentAsynchronousUploadTask() { 110 return Optional.ofNullable(asynchronousUploadPrimitivesTask); 111 } 112 113 @Override 114 public ProgressTaskId canRunInBackground() { 115 return taskId; 116 } 117 118 @Override 119 protected void realRun() { 120 // Lock the data layer before upload in EDT 121 GuiHelper.runInEDTAndWait(() -> { 122 // Remove the commands from the undo stack 123 UndoRedoHandler.getInstance().clean(uploadDataLayer.getDataSet()); 124 MainApplication.getLayerManager().prepareLayerForUpload(uploadDataLayer); 125 126 // Repainting the Layer List dialog to update the icon of the active layer 127 LayerListDialog.getInstance().repaint(); 128 }); 129 super.realRun(); 130 } 131 132 @Override 133 protected void cancel() { 134 super.cancel(); 135 asynchronousUploadPrimitivesTask = null; 136 } 137 138 @Override 139 protected void finish() { 140 try { 141 // Unlock the data layer in EDT 142 GuiHelper.runInEDTAndWait(() -> { 143 MainApplication.getLayerManager().processLayerAfterUpload(uploadDataLayer); 144 LayerListDialog.getInstance().repaint(); 145 }); 146 super.finish(); 147 } finally { 148 asynchronousUploadPrimitivesTask = null; 149 } 150 } 151}