Ignore:
Timestamp:
2009-09-27T16:29:21+02:00 (15 years ago)
Author:
Gubaer
Message:

fixed #3249: Resolve conflicts between invisible and deleted primitives automatically

File:
1 edited

Legend:

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

    r2189 r2198  
    1111import java.util.Collection;
    1212import java.util.Date;
     13import java.util.HashSet;
    1314import java.util.LinkedList;
    1415import java.util.logging.Logger;
     
    2930import org.openstreetmap.josm.data.osm.OsmPrimitive;
    3031import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
     32import org.openstreetmap.josm.gui.DefaultNameFormatter;
    3133import org.openstreetmap.josm.gui.ExceptionDialogUtil;
    3234import org.openstreetmap.josm.gui.PleaseWaitRunnable;
     
    3739import org.openstreetmap.josm.io.OsmApiException;
    3840import org.openstreetmap.josm.io.OsmApiInitializationException;
     41import org.openstreetmap.josm.io.OsmApiPrimitiveGoneException;
    3942import org.openstreetmap.josm.io.OsmChangesetCloseException;
    4043import org.openstreetmap.josm.io.OsmServerWriter;
     44import org.openstreetmap.josm.io.OsmTransferException;
    4145import org.openstreetmap.josm.tools.DateUtils;
    4246import org.openstreetmap.josm.tools.Shortcut;
     
    350354     * @see UpdateSelectionAction#handlePrimitiveGoneException(long)
    351355     */
    352     protected void handleGoneForKnownPrimitive(OsmPrimitiveType primitiveType, String id) {
     356    protected void handleGoneForKnownPrimitive(OsmPrimitiveType primitiveType, long id) {
    353357        UpdateSelectionAction act = new UpdateSelectionAction();
    354         act.handlePrimitiveGoneException(Long.parseLong(id),primitiveType);
     358        act.handlePrimitiveGoneException(id,primitiveType);
    355359    }
    356360
     
    363367     * @param e the exception
    364368     */
    365     protected void handleGone(OsmApiException e) {
    366         String pattern = "The (\\S+) with the id (\\d+) has already been deleted";
    367         Pattern p = Pattern.compile(pattern);
    368         Matcher m = p.matcher(e.getErrorHeader());
    369         if (m.matches()) {
    370             handleGoneForKnownPrimitive(OsmPrimitiveType.from(m.group(1)), m.group(2));
     369    protected void handleGone(OsmApiPrimitiveGoneException e) {
     370        if (e.isKnownPrimitive()) {
     371            handleGoneForKnownPrimitive(e.getPrimitiveType(), e.getPrimitiveId());
    371372        } else {
    372             logger.warning(tr("Error header \"{0}\" does not match expected pattern \"{1}\"",e.getErrorHeader(), pattern));
    373373            ExceptionDialogUtil.explainGoneForUnknownPrimitive(e);
    374374        }
     
    391391        if (e instanceof OsmChangesetCloseException) {
    392392            ExceptionDialogUtil.explainOsmChangesetCloseException((OsmChangesetCloseException)e);
     393            return;
     394        }
     395        if (e instanceof OsmApiPrimitiveGoneException) {
     396            handleGone((OsmApiPrimitiveGoneException)e);
    393397            return;
    394398        }
     
    406410            else if (ex.getResponseCode() == HttpURLConnection.HTTP_PRECON_FAILED) {
    407411                ExceptionDialogUtil.explainPreconditionFailed(ex);
    408                 return;
    409             }
    410             // Tried to delete an already deleted primitive? Let the user
    411             // decide whether and how to resolve this conflict.
    412             //
    413             else if (ex.getResponseCode() == HttpURLConnection.HTTP_GONE) {
    414                 handleGone(ex);
    415412                return;
    416413            }
     
    489486    }
    490487
    491     public UploadDiffTask createUploadTask(OsmDataLayer layer, Collection<OsmPrimitive> toUpload, Changeset changeset, boolean closeChangesetAfterUpload) {
    492         return new UploadDiffTask(layer, toUpload, changeset, closeChangesetAfterUpload);
     488    public UploadPrimitivesTask createUploadTask(OsmDataLayer layer, Collection<OsmPrimitive> toUpload, Changeset changeset, boolean closeChangesetAfterUpload) {
     489        return new UploadPrimitivesTask(layer, toUpload, changeset, closeChangesetAfterUpload);
    493490    }
    494491
     
    497494     *
    498495     */
    499     public class UploadDiffTask extends  PleaseWaitRunnable {
     496    public class UploadPrimitivesTask extends  PleaseWaitRunnable {
    500497        private boolean uploadCancelled = false;
    501498        private Exception lastException = null;
     
    505502        private Changeset changeset;
    506503        private boolean closeChangesetAfterUpload;
     504        private HashSet<OsmPrimitive> processedPrimitives;
    507505
    508506        /**
     
    513511         * @param closeChangesetAfterUpload true, if the changeset is to be closed after uploading
    514512         */
    515         private UploadDiffTask(OsmDataLayer layer, Collection <OsmPrimitive> toUpload, Changeset changeset, boolean closeChangesetAfterUpload) {
     513        private UploadPrimitivesTask(OsmDataLayer layer, Collection <OsmPrimitive> toUpload, Changeset changeset, boolean closeChangesetAfterUpload) {
    516514            super(tr("Uploading data for layer ''{0}''", layer.getName()),false /* don't ignore exceptions */);
    517515            this.toUpload = toUpload;
     
    519517            this.changeset = changeset;
    520518            this.closeChangesetAfterUpload = closeChangesetAfterUpload;
     519            this.processedPrimitives = new HashSet<OsmPrimitive>();
     520        }
     521
     522        protected OsmPrimitive getPrimitive(OsmPrimitiveType type, long id) {
     523            for (OsmPrimitive p: toUpload) {
     524                if (OsmPrimitiveType.from(p).equals(type) && p.getId() == id)
     525                    return p;
     526            }
     527            return null;
     528        }
     529
     530        /**
     531         * Retries to recover the upload operation from an exception which was thrown because
     532         * an uploaded primitive was already deleted on the server.
     533         *
     534         * @param e the exception throw by the API
     535         * @param monitor a progress monitor
     536         * @throws OsmTransferException  thrown if we can't recover from the exception
     537         */
     538        protected void recoverFromGoneOnServer(OsmApiPrimitiveGoneException e, ProgressMonitor monitor) throws OsmTransferException{
     539            if (!e.isKnownPrimitive()) throw e;
     540            OsmPrimitive p = getPrimitive(e.getPrimitiveType(), e.getPrimitiveId());
     541            if (p == null) throw e;
     542            if (p.isDeleted()) {
     543                // we tried to delete an already deleted primitive.
     544                //
     545                System.out.println(tr("Warning: primitive ''{0}'' is already deleted on the server. Skipping this primitive and retrying to upload.", p.getDisplayName(DefaultNameFormatter.getInstance())));
     546                processedPrimitives.addAll(writer.getProcessedPrimitives());
     547                processedPrimitives.add(p);
     548                toUpload.removeAll(processedPrimitives);
     549                return;
     550            }
     551            // exception was thrown because we tried to *update* an already deleted
     552            // primitive. We can't resolve this automatically. Re-throw exception,
     553            // a conflict is going to be created later.
     554            throw e;
    521555        }
    522556
     
    524558            writer = new OsmServerWriter();
    525559            try {
    526                 ProgressMonitor monitor = progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false);
    527                 writer.uploadOsm(layer.data.version, toUpload, changeset,closeChangesetAfterUpload, monitor);
    528             } catch (Exception sxe) {
     560                //
     561                while(true) {
     562                    try {
     563                        ProgressMonitor monitor = progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false);
     564                        writer.uploadOsm(layer.data.version, toUpload, changeset, monitor);
     565                        processedPrimitives.addAll(writer.getProcessedPrimitives());
     566                        // if we get here we've successfully uploaded the data. We
     567                        // can exit the loop.
     568                        //
     569                        break;
     570                    } catch(OsmApiPrimitiveGoneException e) {
     571                        // try to recover from the 410 Gone
     572                        recoverFromGoneOnServer(e, getProgressMonitor());
     573                    }
     574                }
     575                // if required close the changeset
     576                //
     577                if (closeChangesetAfterUpload) {
     578                    if (changeset != null && changeset.getId() > 0) {
     579                        OsmApi.getOsmApi().closeChangeset(changeset, progressMonitor.createSubTaskMonitor(0,false));
     580                    }
     581                }
     582            } catch (Exception e) {
    529583                if (uploadCancelled) {
    530                     System.out.println("Ignoring exception caught because upload is cancelled. Exception is: " + sxe.toString());
     584                    System.out.println("Ignoring exception caught because upload is cancelled. Exception is: " + e.toString());
    531585                    return;
    532586                }
    533                 lastException = sxe;
     587                lastException = e;
    534588            }
    535589        }
     
    539593                return;
    540594
    541             // we always clean the data, even in case of errors. It's possible the data was
     595            // we always clean up the data, even in case of errors. It's possible the data was
    542596            // partially uploaded
    543597            //
    544             layer.cleanupAfterUpload(writer.getProcessedPrimitives());
     598            layer.cleanupAfterUpload(processedPrimitives);
    545599            DataSet.fireSelectionChanged(layer.data.getSelected());
    546600            layer.fireDataChange();
    547601            if (lastException != null) {
    548602                handleFailedUpload(lastException);
    549             } else {
    550                 // run post upload action on the layer
    551                 //
    552                 layer.onPostUploadToServer();
    553                 // refresh changeset dialog with the updated changeset
    554                 //
    555                 UploadDialog.getUploadDialog().setOrUpdateChangeset(changeset);
    556             }
     603            }
     604            layer.onPostUploadToServer();
     605            UploadDialog.getUploadDialog().setOrUpdateChangeset(changeset);
    557606        }
    558607
     
    563612            }
    564613        }
    565 
    566         public boolean isSuccessful() {
    567             return !isCancelled() && !isFailed();
    568         }
    569 
    570         public boolean isCancelled() {
    571             return uploadCancelled;
    572         }
    573 
    574         public boolean isFailed() {
    575             return lastException != null;
    576         }
    577614    }
    578615}
Note: See TracChangeset for help on using the changeset viewer.