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