Changeset 18752 in josm for trunk/src/org/openstreetmap


Ignore:
Timestamp:
2023-06-13T21:32:22+02:00 (13 months ago)
Author:
taylor.smock
Message:

Fix #9446: Show progress on upload validation (patch updated by gaben)

This does the following:

  • Use ValidationTask in more locations (ValidatorCLI and ValidateUploadHook specifically)
  • Add an async method for checking upload conditions
Location:
trunk/src/org/openstreetmap/josm
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/UploadAction.java

    r18467 r18752  
    1212import java.util.Map;
    1313import java.util.Optional;
     14import java.util.concurrent.CompletableFuture;
     15import java.util.concurrent.ExecutionException;
     16import java.util.concurrent.Future;
     17import java.util.function.Consumer;
    1418
    1519import javax.swing.JOptionPane;
     
    3741import org.openstreetmap.josm.spi.preferences.Config;
    3842import org.openstreetmap.josm.tools.ImageProvider;
     43import org.openstreetmap.josm.tools.JosmRuntimeException;
    3944import org.openstreetmap.josm.tools.Logging;
    4045import org.openstreetmap.josm.tools.Shortcut;
     
    4348/**
    4449 * Action that opens a connection to the osm server and uploads all changes.
    45  *
     50 * <p>
    4651 * A dialog is displayed asking the user to specify a rectangle to grab.
    4752 * The url and account settings from the preferences are used.
    48  *
    49  * If the upload fails this action offers various options to resolve conflicts.
     53 * <p>
     54 * If the upload fails, this action offers various options to resolve conflicts.
    5055 *
    5156 * @author imi
     
    5661     * when the user wants to upload data. Plugins can insert their own hooks here
    5762     * if they want to be able to veto an upload.
    58      *
     63     * <p>
    5964     * Be default, the standard upload dialog is the only element in the list.
    6065     * Plugins should normally insert their code before that, so that the upload
     
    195200
    196201    /**
    197      * Check whether the preconditions are met to upload data in <code>apiData</code>.
    198      * Makes sure upload is allowed, primitives in <code>apiData</code> don't participate in conflicts and
     202     * Check whether the preconditions are met to upload data in {@code apiData}.
     203     * Makes sure upload is allowed, primitives in {@code apiData} don't participate in conflicts and
    199204     * runs the installed {@link UploadHook}s.
    200205     *
     
    204209     */
    205210    public static boolean checkPreUploadConditions(AbstractModifiableLayer layer, APIDataSet apiData) {
     211        try {
     212            return checkPreUploadConditionsAsync(layer, apiData, null).get();
     213        } catch (InterruptedException e) {
     214            Thread.currentThread().interrupt();
     215            throw new JosmRuntimeException(e);
     216        } catch (ExecutionException e) {
     217            throw new JosmRuntimeException(e);
     218        }
     219    }
     220
     221    /**
     222     * Check whether the preconditions are met to upload data in {@code apiData}.
     223     * Makes sure upload is allowed, primitives in {@code apiData} don't participate in conflicts and
     224     * runs the installed {@link UploadHook}s.
     225     *
     226     * @param layer the source layer of the data to be uploaded
     227     * @param apiData the data to be uploaded
     228     * @param onFinish {@code true} if the preconditions are met; {@code false}, otherwise
     229     * @return A future that completes when the checks are finished
     230     * @since 18752
     231     */
     232    private static Future<Boolean> checkPreUploadConditionsAsync(AbstractModifiableLayer layer, APIDataSet apiData, Consumer<Boolean> onFinish) {
     233        final CompletableFuture<Boolean> future = new CompletableFuture<>();
     234        if (onFinish != null) {
     235            future.thenAccept(onFinish);
     236        }
    206237        if (layer.isUploadDiscouraged() && warnUploadDiscouraged(layer)) {
    207             return false;
    208         }
    209         if (layer instanceof OsmDataLayer) {
     238            future.complete(false);
     239        } else if (layer instanceof OsmDataLayer) {
    210240            OsmDataLayer osmLayer = (OsmDataLayer) layer;
    211241            ConflictCollection conflicts = osmLayer.getConflicts();
    212242            if (apiData.participatesInConflict(conflicts)) {
    213243                alertUnresolvedConflicts(osmLayer);
    214                 return false;
     244                future.complete(false);
    215245            }
    216246        }
    217247        // Call all upload hooks in sequence.
    218         // FIXME: this should become an asynchronous task
    219         //
    220         if (apiData != null) {
    221             return UPLOAD_HOOKS.stream().allMatch(hook -> hook.checkUpload(apiData));
    222         }
    223 
    224         return true;
     248        if (!future.isDone()) {
     249            MainApplication.worker.execute(() -> {
     250                boolean hooks = true;
     251                if (apiData != null) {
     252                    hooks = UPLOAD_HOOKS.stream().allMatch(hook -> hook.checkUpload(apiData));
     253                }
     254                future.complete(hooks);
     255            });
     256        }
     257        return future;
    225258    }
    226259
     
    236269            return;
    237270        }
    238         if (!checkPreUploadConditions(layer, apiData))
    239             return;
     271        checkPreUploadConditionsAsync(layer, apiData, passed -> GuiHelper.runInEDT(() -> {
     272            if (Boolean.TRUE.equals(passed)) {
     273                realUploadData(layer, apiData);
     274            } else {
     275                new Notification(tr("One of the upload verification processes failed")).show();
     276            }
     277        }));
     278    }
     279
     280    /**
     281     * Uploads data to the OSM API.
     282     *
     283     * @param layer the source layer for the data to upload
     284     * @param apiData the primitives to be added, updated, or deleted
     285     */
     286    private static void realUploadData(final OsmDataLayer layer, final APIDataSet apiData) {
    240287
    241288        ChangesetUpdater.check();
     
    284331                    uploadStrategySpecification, layer, apiData, cs);
    285332
    286             if (asyncUploadTask.isPresent()) {
    287                 MainApplication.worker.execute(asyncUploadTask.get());
    288             }
     333            asyncUploadTask.ifPresent(MainApplication.worker::execute);
    289334        } else {
    290335            MainApplication.worker.execute(new UploadPrimitivesTask(uploadStrategySpecification, layer, apiData, cs));
  • trunk/src/org/openstreetmap/josm/actions/upload/ValidateUploadHook.java

    r18318 r18752  
    66import java.awt.Dimension;
    77import java.awt.GridBagLayout;
    8 import java.util.ArrayList;
    98import java.util.Collection;
    109import java.util.List;
     10import java.util.concurrent.atomic.AtomicBoolean;
    1111
    1212import javax.swing.JPanel;
     
    1515import org.openstreetmap.josm.data.APIDataSet;
    1616import org.openstreetmap.josm.data.osm.OsmPrimitive;
    17 import org.openstreetmap.josm.data.preferences.sources.ValidatorPrefHelper;
    1817import org.openstreetmap.josm.data.validation.OsmValidator;
    19 import org.openstreetmap.josm.data.validation.Severity;
    20 import org.openstreetmap.josm.data.validation.Test;
    2118import org.openstreetmap.josm.data.validation.TestError;
     19import org.openstreetmap.josm.data.validation.ValidationTask;
    2220import org.openstreetmap.josm.data.validation.util.AggregatePrimitivesVisitor;
    2321import org.openstreetmap.josm.gui.ExtendedDialog;
    2422import org.openstreetmap.josm.gui.MainApplication;
    25 import org.openstreetmap.josm.gui.MapFrame;
    2623import org.openstreetmap.josm.gui.dialogs.validator.ValidatorTreePanel;
    27 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    2824import org.openstreetmap.josm.gui.layer.ValidatorLayer;
    2925import org.openstreetmap.josm.gui.util.GuiHelper;
     
    3430 * The action that does the validate thing.
    3531 * <p>
    36  * This action iterates through all active tests and give them the data, so that
     32 * This action iterates through all active tests and gives them the data, so that
    3733 * each one can test it.
    3834 *
     
    4541     * Validate the modified data before uploading
    4642     * @param apiDataSet contains primitives to be uploaded
    47      * @return true if upload should continue, else false
     43     * @return {@code true} if upload should continue, else false
    4844     */
    4945    @Override
    5046    public boolean checkUpload(APIDataSet apiDataSet) {
    51 
    52         OsmValidator.initializeTests();
    53         Collection<Test> tests = OsmValidator.getEnabledTests(true);
    54         if (tests.isEmpty())
    55             return true;
    56 
     47        AtomicBoolean returnCode = new AtomicBoolean();
    5748        AggregatePrimitivesVisitor v = new AggregatePrimitivesVisitor();
    5849        v.visit(apiDataSet.getPrimitivesToAdd());
    59         Collection<OsmPrimitive> selection = v.visit(apiDataSet.getPrimitivesToUpdate());
     50        Collection<OsmPrimitive> visited = v.visit(apiDataSet.getPrimitivesToUpdate());
     51        OsmValidator.initializeTests();
     52        new ValidationTask(errors -> {
     53            if (errors.stream().allMatch(TestError::isIgnored)) {
     54                returnCode.set(true);
     55            } else {
     56                // Unfortunately, the progress monitor is not "finished" until after `finish` is called, so we will
     57                // have a ProgressMonitor open behind the error screen. Fortunately, the error screen appears in front
     58                // of the progress monitor.
     59                GuiHelper.runInEDTAndWait(() -> returnCode.set(displayErrorScreen(errors)));
     60            }
     61        }, null, OsmValidator.getEnabledTests(true), visited, null, true).run();
    6062
    61         List<TestError> errors = new ArrayList<>(30);
    62         for (Test test : tests) {
    63             test.setBeforeUpload(true);
    64             test.setPartialSelection(true);
    65             test.startTest(null);
    66             test.visit(selection);
    67             test.endTest();
    68             if (ValidatorPrefHelper.PREF_OTHER.get() && ValidatorPrefHelper.PREF_OTHER_UPLOAD.get()) {
    69                 errors.addAll(test.getErrors());
    70             } else {
    71                 for (TestError e : test.getErrors()) {
    72                     if (e.getSeverity() != Severity.OTHER) {
    73                         errors.add(e);
    74                     }
    75                 }
    76             }
    77             test.clear();
    78             test.setBeforeUpload(false);
    79         }
    80 
    81         if (Boolean.TRUE.equals(ValidatorPrefHelper.PREF_USE_IGNORE.get())) {
    82             errors.forEach(TestError::updateIgnored);
    83         }
    84 
    85         OsmDataLayer editLayer = MainApplication.getLayerManager().getEditLayer();
    86         if (editLayer != null) {
    87             editLayer.validationErrors.clear();
    88             editLayer.validationErrors.addAll(errors);
    89         }
    90         MapFrame map = MainApplication.getMap();
    91         if (map != null) {
    92             map.validatorDialog.tree.setErrors(errors);
    93         }
    94         if (errors.stream().allMatch(TestError::isIgnored))
    95             return true;
    96 
    97         return displayErrorScreen(errors);
     63        return returnCode.get();
    9864    }
    9965
     
    10268     * give the user the possibility to cancel the upload.
    10369     * @param errors The errors displayed in the screen
    104      * @return <code>true</code>, if the upload should continue. <code>false</code>
    105      *          if the user requested cancel.
     70     * @return {@code true}, if the upload should continue.<br>
     71     *         {@code false}, if the user requested cancel.
    10672     */
    10773    private static boolean displayErrorScreen(List<TestError> errors) {
  • trunk/src/org/openstreetmap/josm/data/validation/ValidationTask.java

    r18208 r18752  
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
     6import java.awt.GraphicsEnvironment;
    67import java.util.ArrayList;
    78import java.util.Collection;
    89import java.util.List;
     10import java.util.function.BiConsumer;
     11import java.util.function.Consumer;
    912
    1013import javax.swing.JOptionPane;
     
    2629 */
    2730public class ValidationTask extends PleaseWaitRunnable {
     31    private final Consumer<List<TestError>> onFinish;
    2832    private Collection<Test> tests;
    2933    private final Collection<OsmPrimitive> validatedPrimitives;
    3034    private final Collection<OsmPrimitive> formerValidatedPrimitives;
     35    private final boolean beforeUpload;
    3136    private boolean canceled;
    3237    private List<TestError> errors;
     38    private BiConsumer<ValidationTask, Test> testConsumer;
    3339
    3440    /**
     
    4551    }
    4652
    47     protected ValidationTask(ProgressMonitor progressMonitor,
    48                              Collection<Test> tests,
    49                              Collection<OsmPrimitive> validatedPrimitives,
    50                              Collection<OsmPrimitive> formerValidatedPrimitives) {
    51         super(tr("Validating"), progressMonitor, false /*don't ignore exceptions */);
     53    /**
     54     * Constructs a new {@code ValidationTask}
     55     *
     56     * @param onFinish                  called when the tests are finished
     57     * @param progressMonitor           the progress monitor to update with test progress
     58     * @param tests                     the tests to run
     59     * @param validatedPrimitives       the collection of primitives to validate.
     60     * @param formerValidatedPrimitives the last collection of primitives being validates. May be null.
     61     * @param beforeUpload              {@code true} if this is being run prior to upload
     62     * @since 18752
     63     */
     64    public ValidationTask(Consumer<List<TestError>> onFinish,
     65            ProgressMonitor progressMonitor,
     66            Collection<Test> tests,
     67            Collection<OsmPrimitive> validatedPrimitives,
     68            Collection<OsmPrimitive> formerValidatedPrimitives,
     69            boolean beforeUpload) {
     70        super(tr("Validating"),
     71                progressMonitor != null ? progressMonitor : new PleaseWaitProgressMonitor(tr("Validating")),
     72                false /*don't ignore exceptions */);
     73        this.onFinish = onFinish;
    5274        this.validatedPrimitives = validatedPrimitives;
    5375        this.formerValidatedPrimitives = formerValidatedPrimitives;
    5476        this.tests = tests;
     77        this.beforeUpload = beforeUpload;
     78    }
     79
     80    protected ValidationTask(ProgressMonitor progressMonitor,
     81            Collection<Test> tests,
     82            Collection<OsmPrimitive> validatedPrimitives,
     83            Collection<OsmPrimitive> formerValidatedPrimitives) {
     84        this(null, progressMonitor, tests, validatedPrimitives, formerValidatedPrimitives, false);
    5585    }
    5686
     
    6494        if (canceled) return;
    6595
    66         // update GUI on Swing EDT
    67         GuiHelper.runInEDT(() -> {
    68             MapFrame map = MainApplication.getMap();
    69             map.validatorDialog.unfurlDialog();
    70             map.validatorDialog.tree.setErrors(errors);
    71             //FIXME: nicer way to find / invalidate the corresponding error layer
    72             MainApplication.getLayerManager().getLayersOfType(ValidatorLayer.class).forEach(ValidatorLayer::invalidate);
    73             if (!errors.isEmpty()) {
    74                 OsmValidator.initializeErrorLayer();
    75             }
    76         });
     96        if (!GraphicsEnvironment.isHeadless() && MainApplication.getMap() != null) {
     97            // update GUI on Swing EDT
     98            GuiHelper.runInEDT(() -> {
     99                MapFrame map = MainApplication.getMap();
     100                map.validatorDialog.unfurlDialog();
     101                map.validatorDialog.tree.setErrors(errors);
     102                //FIXME: nicer way to find / invalidate the corresponding error layer
     103                MainApplication.getLayerManager().getLayersOfType(ValidatorLayer.class).forEach(ValidatorLayer::invalidate);
     104                if (!errors.isEmpty()) {
     105                    OsmValidator.initializeErrorLayer();
     106                }
     107            });
     108        }
     109        if (this.onFinish != null) {
     110            this.onFinish.accept(this.errors);
     111        }
    77112    }
    78113
     
    89124            testCounter++;
    90125            getProgressMonitor().setCustomText(tr("Test {0}/{1}: Starting {2}", testCounter, tests.size(), test.getName()));
    91             test.setBeforeUpload(false);
     126            test.setBeforeUpload(this.beforeUpload);
    92127            test.setPartialSelection(formerValidatedPrimitives != null);
    93128            test.startTest(getProgressMonitor().createSubTaskMonitor(validatedPrimitives.size(), false));
     
    95130            test.endTest();
    96131            errors.addAll(test.getErrors());
     132            if (this.testConsumer != null) {
     133                this.testConsumer.accept(this, test);
     134            }
    97135            test.clear();
     136            test.setBeforeUpload(false);
    98137        }
    99138        tests = null;
     
    125164        return errors;
    126165    }
     166
     167    /**
     168     * A test consumer to avoid filling up memory. A test consumer <i>may</i> remove tests it has consumed.
     169     * @param testConsumer The consumer which takes a {@link ValidationTask} ({@code this}) and the test that finished.
     170     * @since 18752
     171     */
     172    public void setTestConsumer(BiConsumer<ValidationTask, Test> testConsumer) {
     173        this.testConsumer = testConsumer;
     174    }
    127175}
  • trunk/src/org/openstreetmap/josm/data/validation/ValidatorCLI.java

    r18718 r18752  
    1111import java.nio.charset.StandardCharsets;
    1212import java.nio.file.Files;
     13import java.nio.file.Path;
    1314import java.nio.file.Paths;
    1415import java.util.ArrayList;
     
    2627import java.util.stream.Collectors;
    2728
     29import jakarta.json.JsonObject;
    2830import org.apache.commons.compress.utils.FileNameUtils;
    2931import org.openstreetmap.josm.actions.ExtensionFileFilter;
     
    177179            for (String inputFile : this.input) {
    178180                if (inputFile.endsWith(".validator.mapcss")) {
    179                     this.processValidatorFile(inputFile);
     181                    processValidatorFile(inputFile);
    180182                } else if (inputFile.endsWith(".mapcss")) {
    181                     this.processMapcssFile(inputFile);
     183                    processMapcssFile(inputFile);
    182184                } else {
    183185                    this.processFile(inputFile);
     
    198200     * @throws ParseException if the file does not match the mapcss syntax
    199201     */
    200     private void processMapcssFile(final String inputFile) throws ParseException {
     202    private static void processMapcssFile(final String inputFile) throws ParseException {
    201203        final MapCSSStyleSource styleSource = new MapCSSStyleSource(new File(inputFile).toURI().getPath(), inputFile, inputFile);
    202204        styleSource.loadStyleSource();
     
    215217     * @throws ParseException if the file does not match the validator mapcss syntax
    216218     */
    217     private void processValidatorFile(final String inputFile) throws ParseException, IOException {
     219    private static void processValidatorFile(final String inputFile) throws ParseException, IOException {
    218220        // Check asserts
    219221        Config.getPref().putBoolean("validator.check_assert_local_rules", true);
     
    257259        try {
    258260            Logging.info(task);
    259             OsmValidator.initializeTests();
    260261            dataLayer = MainApplication.getLayerManager().getLayersOfType(OsmDataLayer.class)
    261262                    .stream().filter(layer -> inputFileFile.equals(layer.getAssociatedFile()))
     
    270271                }
    271272            }
    272             Collection<Test> tests = OsmValidator.getEnabledTests(false);
    273             if (Files.isRegularFile(Paths.get(outputFile)) && !Files.deleteIfExists(Paths.get(outputFile))) {
     273            Path path = Paths.get(outputFile);
     274            if (path.toFile().isFile() && !Files.deleteIfExists(path)) {
    274275                Logging.error("Could not delete {0}, attempting to append", outputFile);
    275276            }
    276277            GeoJSONMapRouletteWriter geoJSONMapRouletteWriter = new GeoJSONMapRouletteWriter(dataSet);
    277             try (OutputStream fileOutputStream = Files.newOutputStream(Paths.get(outputFile))) {
    278                 tests.parallelStream().forEach(test -> runTest(test, geoJSONMapRouletteWriter, fileOutputStream, dataSet));
     278            OsmValidator.initializeTests();
     279
     280            try (OutputStream fileOutputStream = Files.newOutputStream(path)) {
     281                // The first writeErrors catches anything that was written, for whatever reason. This is probably never
     282                // going to be called.
     283                ValidationTask validationTask = new ValidationTask(errors -> writeErrors(geoJSONMapRouletteWriter, fileOutputStream, errors),
     284                        progressMonitorFactory.get(), OsmValidator.getEnabledTests(false),
     285                        dataSet.allPrimitives(), Collections.emptyList(), false);
     286                // This avoids keeping errors in memory
     287                validationTask.setTestConsumer((t, test) -> {
     288                    writeErrors(geoJSONMapRouletteWriter, fileOutputStream, test.getErrors());
     289                    t.getErrors().removeIf(test.getErrors()::contains);
     290                });
     291                validationTask.run();
    279292            }
    280293        } finally {
     
    283296            }
    284297            Logging.info(stopwatch.toString(task));
     298        }
     299    }
     300
     301    private void writeErrors(GeoJSONMapRouletteWriter geoJSONMapRouletteWriter, OutputStream fileOutputStream,
     302            Collection<TestError> errors) {
     303        for (TestError error : errors) {
     304            Optional<JsonObject> object = geoJSONMapRouletteWriter.write(error);
     305            if (object.isPresent()) {
     306                try {
     307                    writeToFile(fileOutputStream, object.get().toString().getBytes(StandardCharsets.UTF_8));
     308                } catch (IOException e) {
     309                    throw new JosmRuntimeException(e);
     310                }
     311            }
    285312        }
    286313    }
     
    300327        }
    301328        return FileNameUtils.getBaseName(FileNameUtils.getBaseName(inputString)) + ".geojson";
    302     }
    303 
    304     /**
    305      * Run a test
    306      * @param test The test to run
    307      * @param geoJSONMapRouletteWriter The object to use to create challenges
    308      * @param fileOutputStream The location to write data to
    309      * @param dataSet The dataset to check
    310      */
    311     private void runTest(final Test test, final GeoJSONMapRouletteWriter geoJSONMapRouletteWriter,
    312             final OutputStream fileOutputStream, DataSet dataSet) {
    313         test.startTest(progressMonitorFactory.get());
    314         test.visit(dataSet.allPrimitives());
    315         test.endTest();
    316         test.getErrors().stream().map(geoJSONMapRouletteWriter::write)
    317                 .filter(Optional::isPresent).map(Optional::get)
    318                 .map(jsonObject -> jsonObject.toString().getBytes(StandardCharsets.UTF_8)).forEach(bytes -> {
    319                     try {
    320                         writeToFile(fileOutputStream, bytes);
    321                     } catch (IOException e) {
    322                         throw new JosmRuntimeException(e);
    323                     }
    324                 });
    325         test.clear();
    326329    }
    327330
  • trunk/src/org/openstreetmap/josm/gui/io/SaveLayersDialog.java

    r18706 r18752  
    6464/**
    6565 * Dialog that pops up when the user closes a layer with modified data.
    66  *
    67  * It asks for confirmation that all modification should be discarded and offers
     66 * <p>
     67 * It asks for confirmation that all modifications should be discarded and offer
    6868 * to save the layers to file or upload to server, depending on the type of layer.
    6969 */
     
    450450                    closeDialog();
    451451                }
    452             } catch (UserCancelException ignore) {
    453                 Logging.trace(ignore);
     452            } catch (UserCancelException userCancelException) {
     453                Logging.trace(userCancelException);
    454454            }
    455455        }
     
    557557                AbstractModifiableLayer layer = layerInfo.getLayer();
    558558                if (canceled) {
     559                    GuiHelper.runInEDTAndWait(() -> model.setUploadState(layer, UploadOrSaveState.CANCELED));
     560                    continue;
     561                }
     562                GuiHelper.runInEDTAndWait(() -> monitor.subTask(tr("Preparing layer ''{0}'' for upload ...", layerInfo.getName())));
     563
     564                // checkPreUploadConditions must not be run in the EDT to avoid deadlocks
     565                if (!UploadAction.checkPreUploadConditions(layer)) {
     566                    GuiHelper.runInEDTAndWait(() -> model.setUploadState(layer, UploadOrSaveState.FAILED));
     567                    continue;
     568                }
     569
     570                GuiHelper.runInEDTAndWait(() -> uploadLayersUploadModelStateOnFinish(layer));
     571                currentTask = null;
     572            }
     573        }
     574
     575        /**
     576         * Update the {@link #model} state on upload finish
     577         * @param layer The layer that has been saved
     578         */
     579        private void uploadLayersUploadModelStateOnFinish(AbstractModifiableLayer layer) {
     580            AbstractUploadDialog dialog = layer.getUploadDialog();
     581            if (dialog != null) {
     582                dialog.setVisible(true);
     583                if (dialog.isCanceled()) {
    559584                    model.setUploadState(layer, UploadOrSaveState.CANCELED);
    560                     continue;
    561                 }
    562                 monitor.subTask(tr("Preparing layer ''{0}'' for upload ...", layerInfo.getName()));
    563 
    564                 if (!UploadAction.checkPreUploadConditions(layer)) {
    565                     model.setUploadState(layer, UploadOrSaveState.FAILED);
    566                     continue;
    567                 }
    568 
    569                 AbstractUploadDialog dialog = layer.getUploadDialog();
    570                 if (dialog != null) {
    571                     dialog.setVisible(true);
    572                     if (dialog.isCanceled()) {
    573                         model.setUploadState(layer, UploadOrSaveState.CANCELED);
    574                         continue;
    575                     }
    576                     dialog.rememberUserInput();
    577                 }
    578 
    579                 currentTask = layer.createUploadTask(monitor);
    580                 if (currentTask == null) {
    581                     model.setUploadState(layer, UploadOrSaveState.FAILED);
    582                     continue;
    583                 }
    584                 Future<?> currentFuture = worker.submit(currentTask);
    585                 try {
    586                     // wait for the asynchronous task to complete
    587                     currentFuture.get();
    588                 } catch (CancellationException e) {
    589                     Logging.trace(e);
    590                     model.setUploadState(layer, UploadOrSaveState.CANCELED);
    591                 } catch (InterruptedException | ExecutionException e) {
    592                     Logging.error(e);
    593                     model.setUploadState(layer, UploadOrSaveState.FAILED);
    594                     ExceptionDialogUtil.explainException(e);
    595                 }
    596                 if (currentTask.isCanceled()) {
    597                     model.setUploadState(layer, UploadOrSaveState.CANCELED);
    598                 } else if (currentTask.isFailed()) {
    599                     Logging.error(currentTask.getLastException());
    600                     ExceptionDialogUtil.explainException(currentTask.getLastException());
    601                     model.setUploadState(layer, UploadOrSaveState.FAILED);
    602                 } else {
    603                     model.setUploadState(layer, UploadOrSaveState.OK);
    604                 }
    605                 currentTask = null;
     585                    return;
     586                }
     587                dialog.rememberUserInput();
     588            }
     589
     590            currentTask = layer.createUploadTask(monitor);
     591            if (currentTask == null) {
     592                model.setUploadState(layer, UploadOrSaveState.FAILED);
     593                return;
     594            }
     595            Future<?> currentFuture = worker.submit(currentTask);
     596            try {
     597                // wait for the asynchronous task to complete
     598                currentFuture.get();
     599            } catch (CancellationException e) {
     600                Logging.trace(e);
     601                model.setUploadState(layer, UploadOrSaveState.CANCELED);
     602            } catch (InterruptedException e) {
     603                Thread.currentThread().interrupt();
     604                Logging.error(e);
     605                model.setUploadState(layer, UploadOrSaveState.FAILED);
     606                ExceptionDialogUtil.explainException(e);
     607            } catch (ExecutionException e) {
     608                Logging.error(e);
     609                model.setUploadState(layer, UploadOrSaveState.FAILED);
     610                ExceptionDialogUtil.explainException(e);
     611            }
     612            if (currentTask.isCanceled()) {
     613                model.setUploadState(layer, UploadOrSaveState.CANCELED);
     614            } else if (currentTask.isFailed()) {
     615                Logging.error(currentTask.getLastException());
     616                ExceptionDialogUtil.explainException(currentTask.getLastException());
     617                model.setUploadState(layer, UploadOrSaveState.FAILED);
     618            } else {
     619                model.setUploadState(layer, UploadOrSaveState.OK);
    606620            }
    607621        }
     
    673687        @Override
    674688        public void run() {
     689            GuiHelper.runInEDTAndWait(() -> model.setMode(SaveLayersModel.Mode.UPLOADING_AND_SAVING));
     690            // We very specifically do not want to block the EDT or the worker thread when validating
     691            List<SaveLayerInfo> toUpload = model.getLayersToUpload();
     692            if (!toUpload.isEmpty()) {
     693                uploadLayers(toUpload);
     694            }
    675695            GuiHelper.runInEDTAndWait(() -> {
    676                 model.setMode(SaveLayersModel.Mode.UPLOADING_AND_SAVING);
    677                 List<SaveLayerInfo> toUpload = model.getLayersToUpload();
    678                 if (!toUpload.isEmpty()) {
    679                     uploadLayers(toUpload);
    680                 }
    681696                List<SaveLayerInfo> toSave = model.getLayersToSave();
    682697                if (!toSave.isEmpty()) {
Note: See TracChangeset for help on using the changeset viewer.