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.awt.GraphicsEnvironment; 007import java.util.Optional; 008 009import javax.swing.JOptionPane; 010 011import org.openstreetmap.josm.data.APIDataSet; 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 if (!GraphicsEnvironment.isHeadless()) { 089 GuiHelper.runInEDTAndWait(() -> 090 JOptionPane.showMessageDialog(MainApplication.parent, 091 tr("A background upload is already in progress. " + 092 "Kindly wait for it to finish before uploading new changes"))); 093 } 094 return Optional.empty(); 095 } else { 096 // Create an asynchronous upload task 097 asynchronousUploadPrimitivesTask = new AsynchronousUploadPrimitivesTask( 098 uploadStrategySpecification, 099 dataLayer, 100 apiDataSet, 101 changeset); 102 return Optional.ofNullable(asynchronousUploadPrimitivesTask); 103 } 104 } 105 } 106 107 /** 108 * Get the current upload task 109 * @return {@literal Optional<AsynchronousUploadPrimitivesTask> } 110 */ 111 public static Optional<AsynchronousUploadPrimitivesTask> getCurrentAsynchronousUploadTask() { 112 return Optional.ofNullable(asynchronousUploadPrimitivesTask); 113 } 114 115 @Override 116 public ProgressTaskId canRunInBackground() { 117 return taskId; 118 } 119 120 @Override 121 protected void realRun() { 122 // Lock the data layer before upload in EDT 123 GuiHelper.runInEDTAndWait(() -> { 124 // Remove the commands from the undo stack 125 MainApplication.undoRedo.clean(uploadDataLayer.data); 126 MainApplication.getLayerManager().prepareLayerForUpload(uploadDataLayer); 127 128 // Repainting the Layer List dialog to update the icon of the active layer 129 LayerListDialog.getInstance().repaint(); 130 }); 131 super.realRun(); 132 } 133 134 @Override 135 protected void cancel() { 136 super.cancel(); 137 asynchronousUploadPrimitivesTask = null; 138 } 139 140 @Override 141 protected void finish() { 142 try { 143 // Unlock the data layer in EDT 144 GuiHelper.runInEDTAndWait(() -> { 145 MainApplication.getLayerManager().processLayerAfterUpload(uploadDataLayer); 146 LayerListDialog.getInstance().repaint(); 147 }); 148 super.finish(); 149 } finally { 150 asynchronousUploadPrimitivesTask = null; 151 } 152 } 153}