source: josm/trunk/src/org/openstreetmap/josm/actions/UploadAction.java@ 2480

Last change on this file since 2480 was 2480, checked in by Gubaer, 14 years ago

fixed #3937: Closed changeset (due to timeout) remains in the upload dialog

  • Property svn:eol-style set to native
File size: 27.1 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.actions;
3
4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.event.ActionEvent;
8import java.awt.event.KeyEvent;
9import java.io.IOException;
10import java.net.HttpURLConnection;
11import java.text.SimpleDateFormat;
12import java.util.Collection;
13import java.util.Date;
14import java.util.HashSet;
15import java.util.LinkedList;
16import java.util.logging.Logger;
17import java.util.regex.Matcher;
18import java.util.regex.Pattern;
19
20import javax.swing.JOptionPane;
21
22import org.openstreetmap.josm.Main;
23import org.openstreetmap.josm.actions.upload.ApiPreconditionCheckerHook;
24import org.openstreetmap.josm.actions.upload.RelationUploadOrderHook;
25import org.openstreetmap.josm.actions.upload.UploadHook;
26import org.openstreetmap.josm.actions.upload.UploadParameterHook;
27import org.openstreetmap.josm.data.APIDataSet;
28import org.openstreetmap.josm.data.conflict.ConflictCollection;
29import org.openstreetmap.josm.data.osm.Changeset;
30import org.openstreetmap.josm.data.osm.OsmPrimitive;
31import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
32import org.openstreetmap.josm.gui.DefaultNameFormatter;
33import org.openstreetmap.josm.gui.ExceptionDialogUtil;
34import org.openstreetmap.josm.gui.HelpAwareOptionPane;
35import org.openstreetmap.josm.gui.PleaseWaitRunnable;
36import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
37import org.openstreetmap.josm.gui.io.UploadDialog;
38import org.openstreetmap.josm.gui.layer.OsmDataLayer;
39import org.openstreetmap.josm.gui.progress.ProgressMonitor;
40import org.openstreetmap.josm.io.ChangesetClosedException;
41import org.openstreetmap.josm.io.OsmApi;
42import org.openstreetmap.josm.io.OsmApiException;
43import org.openstreetmap.josm.io.OsmApiInitializationException;
44import org.openstreetmap.josm.io.OsmApiPrimitiveGoneException;
45import org.openstreetmap.josm.io.OsmServerWriter;
46import org.openstreetmap.josm.io.OsmTransferException;
47import org.openstreetmap.josm.tools.DateUtils;
48import org.openstreetmap.josm.tools.ImageProvider;
49import org.openstreetmap.josm.tools.Shortcut;
50import org.xml.sax.SAXException;
51
52
53/**
54 * Action that opens a connection to the osm server and uploads all changes.
55 *
56 * An dialog is displayed asking the user to specify a rectangle to grab.
57 * The url and account settings from the preferences are used.
58 *
59 * If the upload fails this action offers various options to resolve conflicts.
60 *
61 * @author imi
62 */
63public class UploadAction extends JosmAction{
64 static private Logger logger = Logger.getLogger(UploadAction.class.getName());
65 /**
66 * The list of upload hooks. These hooks will be called one after the other
67 * when the user wants to upload data. Plugins can insert their own hooks here
68 * if they want to be able to veto an upload.
69 *
70 * Be default, the standard upload dialog is the only element in the list.
71 * Plugins should normally insert their code before that, so that the upload
72 * dialog is the last thing shown before upload really starts; on occasion
73 * however, a plugin might also want to insert something after that.
74 */
75 private static final LinkedList<UploadHook> uploadHooks = new LinkedList<UploadHook>();
76 static {
77 /**
78 * Checks server capabilities before upload.
79 */
80 uploadHooks.add(new ApiPreconditionCheckerHook());
81
82 /**
83 * Adjusts the upload order of new relations
84 */
85 uploadHooks.add(new RelationUploadOrderHook());
86
87 /**
88 * Displays a screen where the actions that would be taken are displayed and
89 * give the user the possibility to cancel the upload.
90 */
91 uploadHooks.add(new UploadParameterHook());
92 }
93
94 /**
95 * Registers an upload hook. Adds the hook at the first position of the upload hooks.
96 *
97 * @param hook the upload hook. Ignored if null.
98 */
99 public static void registerUploadHook(UploadHook hook) {
100 if(hook == null) return;
101 if (!uploadHooks.contains(hook)) {
102 uploadHooks.add(0,hook);
103 }
104 }
105
106 /**
107 * Unregisters an upload hook. Removes the hook from the list of upload hooks.
108 *
109 * @param hook the upload hook. Ignored if null.
110 */
111 public static void unregisterUploadHook(UploadHook hook) {
112 if(hook == null) return;
113 if (uploadHooks.contains(hook)) {
114 uploadHooks.remove(hook);
115 }
116 }
117
118 public UploadAction() {
119 super(tr("Upload data"), "upload", tr("Upload all changes in the active data layer to the OSM server"),
120 Shortcut.registerShortcut("file:upload", tr("File: {0}", tr("Upload data")), KeyEvent.VK_U, Shortcut.GROUPS_ALT1+Shortcut.GROUP_HOTKEY), true);
121 putValue("help", ht("/Action/Upload"));
122 }
123
124 /**
125 * Refreshes the enabled state
126 *
127 */
128 @Override
129 protected void updateEnabledState() {
130 setEnabled(getEditLayer() != null);
131 }
132
133 public boolean checkPreUploadConditions(OsmDataLayer layer) {
134 return checkPreUploadConditions(layer, new APIDataSet(layer.data));
135 }
136
137 public boolean checkPreUploadConditions(OsmDataLayer layer, APIDataSet apiData) {
138 ConflictCollection conflicts = layer.getConflicts();
139 if (conflicts !=null && !conflicts.isEmpty()) {
140 JOptionPane.showMessageDialog(
141 Main.parent,
142 tr("<html>There are unresolved conflicts in layer ''{0}''.<br>"
143 + "You have to resolve them first.</html>", layer.getName()),
144 tr("Warning"),
145 JOptionPane.WARNING_MESSAGE
146 );
147 return false;
148 }
149 // Call all upload hooks in sequence. The upload confirmation dialog
150 // is one of these.
151 for(UploadHook hook : uploadHooks)
152 if(!hook.checkUpload(apiData))
153 return false;
154
155 return true;
156 }
157
158 public void uploadData(OsmDataLayer layer, APIDataSet apiData) {
159 if (apiData.isEmpty()) {
160 JOptionPane.showMessageDialog(
161 Main.parent,
162 tr("No changes to upload."),
163 tr("Warning"),
164 JOptionPane.INFORMATION_MESSAGE
165 );
166 return;
167 }
168 if (!checkPreUploadConditions(layer, apiData))
169 return;
170 Main.worker.execute(
171 createUploadTask(
172 layer,
173 apiData.getPrimitives(),
174 UploadDialog.getUploadDialog().getChangeset(),
175 UploadDialog.getUploadDialog().isDoCloseAfterUpload()
176 )
177 );
178 }
179
180 public void actionPerformed(ActionEvent e) {
181 if (!isEnabled())
182 return;
183 if (Main.map == null) {
184 JOptionPane.showMessageDialog(
185 Main.parent,
186 tr("Nothing to upload. Get some data first."),
187 tr("Warning"),
188 JOptionPane.WARNING_MESSAGE
189 );
190 return;
191 }
192 APIDataSet apiData = new APIDataSet(Main.main.getCurrentDataSet());
193 uploadData(Main.map.mapView.getEditLayer(), apiData);
194 }
195
196 /**
197 * Synchronizes the local state of an {@see OsmPrimitive} with its state on the
198 * server. The method uses an individual GET for the primitive.
199 *
200 * @param id the primitive ID
201 */
202 protected void synchronizePrimitive(final OsmPrimitiveType type, final long id) {
203 Main.worker.execute(new UpdatePrimitiveTask(type, id));
204 }
205
206 /**
207 * Synchronizes the local state of the dataset with the state on the server.
208 *
209 * Reuses the functionality of {@see UpdateDataAction}.
210 *
211 * @see UpdateDataAction#actionPerformed(ActionEvent)
212 */
213 protected void synchronizeDataSet() {
214 UpdateDataAction act = new UpdateDataAction();
215 act.actionPerformed(new ActionEvent(this,0,""));
216 }
217
218 /**
219 * Handles the case that a conflict in a specific {@see OsmPrimitive} was detected while
220 * uploading
221 *
222 * @param primitiveType the type of the primitive, either <code>node</code>, <code>way</code> or
223 * <code>relation</code>
224 * @param id the id of the primitive
225 * @param serverVersion the version of the primitive on the server
226 * @param myVersion the version of the primitive in the local dataset
227 */
228 protected void handleUploadConflictForKnownConflict(final OsmPrimitiveType primitiveType, final long id, String serverVersion, String myVersion) {
229 String lbl = "";
230 switch(primitiveType) {
231 case NODE: lbl = tr("Synchronize node {0} only", id); break;
232 case WAY: lbl = tr("Synchronize way {0} only", id); break;
233 case RELATION: lbl = tr("Synchronize relation {0} only", id); break;
234 }
235 ButtonSpec[] spec = new ButtonSpec[] {
236 new ButtonSpec(
237 lbl,
238 ImageProvider.get("updatedata"),
239 null,
240 null
241 ),
242 new ButtonSpec(
243 tr("Synchronize entire dataset"),
244 ImageProvider.get("updatedata"),
245 null,
246 null
247 ),
248 new ButtonSpec(
249 tr("Cancel"),
250 ImageProvider.get("cancel"),
251 null,
252 null
253 )
254 };
255 String msg = tr("<html>Uploading <strong>failed</strong> because the server has a newer version of one<br>"
256 + "of your nodes, ways, or relations.<br>"
257 + "The conflict is caused by the <strong>{0}</strong> with id <strong>{1}</strong>,<br>"
258 + "the server has version {2}, your version is {3}.<br>"
259 + "<br>"
260 + "Click <strong>{4}</strong> to synchronize the conflicting primitive only.<br>"
261 + "Click <strong>{5}</strong> to synchronize the entire local dataset with the server.<br>"
262 + "Click <strong>{6}</strong> to abort and continue editing.<br></html>",
263 tr(primitiveType.getAPIName()), id, serverVersion, myVersion,
264 spec[0].text, spec[1].text, spec[2].text
265 );
266 int ret = HelpAwareOptionPane.showOptionDialog(
267 Main.parent,
268 msg,
269 tr("Conflicts detected"),
270 JOptionPane.ERROR_MESSAGE,
271 null,
272 spec,
273 spec[0],
274 "/Concepts/Conflict"
275 );
276 switch(ret) {
277 case 0: synchronizePrimitive(primitiveType, id); break;
278 case 1: synchronizeDataSet(); break;
279 default: return;
280 }
281 }
282
283 /**
284 * Handles the case that a conflict was detected while uploading where we don't
285 * know what {@see OsmPrimitive} actually caused the conflict (for whatever reason)
286 *
287 */
288 protected void handleUploadConflictForUnknownConflict() {
289 ButtonSpec[] spec = new ButtonSpec[] {
290 new ButtonSpec(
291 tr("Synchronize entire dataset"),
292 ImageProvider.get("updatedata"),
293 null,
294 null
295 ),
296 new ButtonSpec(
297 tr("Cancel"),
298 ImageProvider.get("cancel"),
299 null,
300 null
301 )
302 };
303 String msg = tr("<html>Uploading <strong>failed</strong> because the server has a newer version of one<br>"
304 + "of your nodes, ways, or relations.<br>"
305 + "<br>"
306 + "Click <strong>{0}</strong> to synchronize the entire local dataset with the server.<br>"
307 + "Click <strong>{1}</strong> to abort and continue editing.<br></html>",
308 spec[0].text, spec[1].text
309 );
310 int ret = HelpAwareOptionPane.showOptionDialog(
311 Main.parent,
312 msg,
313 tr("Conflicts detected"),
314 JOptionPane.ERROR_MESSAGE,
315 null,
316 spec,
317 spec[0],
318 "Concepts/Conflict"
319 );
320 if (ret == 0) {
321 synchronizeDataSet();
322 }
323 }
324
325 /**
326 * Handles the case that a conflict was detected while uploading where we don't
327 * know what {@see OsmPrimitive} actually caused the conflict (for whatever reason)
328 *
329 */
330 protected void handleUploadConflictForClosedChangeset(long changsetId, Date d) {
331 String msg = tr("<html>Uploading <strong>failed</strong> because you''ve been using<br>"
332 + "changeset {0} which was already closed at {1}.<br>"
333 + "Please upload again with a new or an existing open changeset.</html>",
334 changsetId, new SimpleDateFormat().format(d)
335 );
336 JOptionPane.showMessageDialog(
337 Main.parent,
338 msg,
339 tr("Changeset closed"),
340 JOptionPane.ERROR_MESSAGE
341 );
342 }
343
344
345 /**
346 * Handles the case where deleting a node failed because it is still in use in
347 * a non-deleted way on the server.
348 */
349 protected void handleUploadConflictForNodeStillInUse(long nodeId, long wayId) {
350 ButtonSpec[] options = new ButtonSpec[] {
351 new ButtonSpec(
352 tr("Prepare conflict resolution"),
353 ImageProvider.get("ok"),
354 tr("Click to download all parent ways for node {0}", nodeId),
355 null /* no specific help context */
356 ),
357 new ButtonSpec(
358 tr("Cancel"),
359 ImageProvider.get("cancel"),
360 tr("Click to cancel and to resume editing the map", nodeId),
361 null /* no specific help context */
362 )
363 };
364 String msg = tr("<html>Uploading <strong>failed</strong> because you tried "
365 + "to delete node {0} which is still in use in way {1}.<br><br>"
366 + "Click <strong>{2}</strong> to download all parent ways of node {0}.<br>"
367 + "If necessary JOSM will create conflicts which you can resolve in the Conflict Resolution Dialog."
368 + "</html>",
369 nodeId, wayId, options[0].text
370 );
371
372 int ret = HelpAwareOptionPane.showOptionDialog(
373 Main.parent,
374 msg,
375 tr("Node still in use"),
376 JOptionPane.ERROR_MESSAGE,
377 null,
378 options,
379 options[0],
380 "/Action/Upload#NodeStillInUseInWay"
381 );
382 if (ret != 0) return;
383 DownloadReferrersAction.downloadReferrers(Main.map.mapView.getEditLayer(), nodeId, OsmPrimitiveType.NODE);
384 }
385
386 /**
387 * handles an upload conflict, i.e. an error indicated by a HTTP return code 409.
388 *
389 * @param e the exception
390 */
391 protected void handleUploadConflict(OsmApiException e) {
392 String pattern = "Version mismatch: Provided (\\d+), server had: (\\d+) of (\\S+) (\\d+)";
393 Pattern p = Pattern.compile(pattern);
394 Matcher m = p.matcher(e.getErrorHeader());
395 if (m.matches()) {
396 handleUploadConflictForKnownConflict(OsmPrimitiveType.from(m.group(3)), Long.parseLong(m.group(4)), m.group(2),m.group(1));
397 return;
398 }
399 pattern ="The changeset (\\d+) was closed at (.*)";
400 p = Pattern.compile(pattern);
401 m = p.matcher(e.getErrorHeader());
402 if (m.matches()) {
403 handleUploadConflictForClosedChangeset(Long.parseLong(m.group(1)), DateUtils.fromString(m.group(2)));
404 return;
405 }
406 pattern = "Node (\\d+) is still used by way (\\d+).";
407 p = Pattern.compile(pattern);
408 m = p.matcher(e.getErrorHeader());
409 if (m.matches()) {
410 handleUploadConflictForNodeStillInUse(Long.parseLong(m.group(1)), Long.parseLong(m.group(2)));
411 return;
412 }
413 logger.warning(tr("Warning: error header \"{0}\" did not match with an expected pattern", e.getErrorHeader()));
414 handleUploadConflictForUnknownConflict();
415 }
416
417 /**
418 * handles an precondition failed conflict, i.e. an error indicated by a HTTP return code 412.
419 *
420 * @param e the exception
421 */
422 protected void handlePreconditionFailed(OsmApiException e) {
423 String pattern = "Precondition failed: Node (\\d+) is still used by way (\\d+).";
424 Pattern p = Pattern.compile(pattern);
425 Matcher m = p.matcher(e.getErrorHeader());
426 if (m.matches()) {
427 handleUploadConflictForNodeStillInUse(Long.parseLong(m.group(1)), Long.parseLong(m.group(2)));
428 return;
429 }
430 logger.warning(tr("Warning: error header \"{0}\" did not match with an expected pattern", e.getErrorHeader()));
431 ExceptionDialogUtil.explainPreconditionFailed(e);
432 }
433
434 /**
435 * Handles an error due to a delete request on an already deleted
436 * {@see OsmPrimitive}, i.e. a HTTP response code 410, where we know what
437 * {@see OsmPrimitive} is responsible for the error.
438 *
439 * Reuses functionality of the {@see UpdateSelectionAction} to resolve
440 * conflicts due to mismatches in the deleted state.
441 *
442 * @param primitiveType the type of the primitive
443 * @param id the id of the primitive
444 *
445 * @see UpdateSelectionAction#handlePrimitiveGoneException(long)
446 */
447 protected void handleGoneForKnownPrimitive(OsmPrimitiveType primitiveType, long id) {
448 UpdateSelectionAction act = new UpdateSelectionAction();
449 act.handlePrimitiveGoneException(id,primitiveType);
450 }
451
452 /**
453 * Handles an error which is caused by a delete request for an already deleted
454 * {@see OsmPrimitive} on the server, i.e. a HTTP response code of 410.
455 * Note that an <strong>update</strong> on an already deleted object results
456 * in a 409, not a 410.
457 *
458 * @param e the exception
459 */
460 protected void handleGone(OsmApiPrimitiveGoneException e) {
461 if (e.isKnownPrimitive()) {
462 handleGoneForKnownPrimitive(e.getPrimitiveType(), e.getPrimitiveId());
463 } else {
464 ExceptionDialogUtil.explainGoneForUnknownPrimitive(e);
465 }
466 }
467
468
469 /**
470 * error handler for any exception thrown during upload
471 *
472 * @param e the exception
473 */
474 protected void handleFailedUpload(Exception e) {
475 // API initialization failed. Notify the user and return.
476 //
477 if (e instanceof OsmApiInitializationException) {
478 ExceptionDialogUtil.explainOsmApiInitializationException((OsmApiInitializationException)e);
479 return;
480 }
481
482 if (e instanceof OsmApiPrimitiveGoneException) {
483 handleGone((OsmApiPrimitiveGoneException)e);
484 return;
485 }
486 if (e instanceof OsmApiException) {
487 OsmApiException ex = (OsmApiException)e;
488 // There was an upload conflict. Let the user decide whether
489 // and how to resolve it
490 //
491 if(ex.getResponseCode() == HttpURLConnection.HTTP_CONFLICT) {
492 handleUploadConflict(ex);
493 return;
494 }
495 // There was a precondition failed. Notify the user.
496 //
497 else if (ex.getResponseCode() == HttpURLConnection.HTTP_PRECON_FAILED) {
498 handlePreconditionFailed(ex);
499 return;
500 }
501 // Tried to update or delete a primitive which never existed on
502 // the server?
503 //
504 else if (ex.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
505 ExceptionDialogUtil.explainNotFound(ex);
506 return;
507 }
508 }
509
510 ExceptionDialogUtil.explainException(e);
511 }
512
513 /**
514 * The asynchronous task to update a specific id
515 *
516 */
517 class UpdatePrimitiveTask extends PleaseWaitRunnable {
518
519 private boolean uploadCancelled = false;
520 private boolean uploadFailed = false;
521 private Exception lastException = null;
522 private long id;
523 private OsmPrimitiveType type;
524
525 public UpdatePrimitiveTask(OsmPrimitiveType type, long id) {
526 super(tr("Updating primitive"),false /* don't ignore exceptions */);
527 this.id = id;
528 this.type = type;
529 }
530
531 @Override protected void realRun() throws SAXException, IOException {
532 try {
533 UpdateSelectionAction act = new UpdateSelectionAction();
534 act.updatePrimitive(type, id);
535 } catch (Exception sxe) {
536 if (uploadCancelled) {
537 System.out.println("Ignoring exception caught because upload is canceled. Exception is: " + sxe.toString());
538 return;
539 }
540 uploadFailed = true;
541 lastException = sxe;
542 }
543 }
544
545 @Override protected void finish() {
546 if (uploadFailed) {
547 handleFailedUpload(lastException);
548 }
549 }
550
551 @Override protected void cancel() {
552 OsmApi.getOsmApi().cancel();
553 uploadCancelled = true;
554 }
555 }
556
557 public UploadPrimitivesTask createUploadTask(OsmDataLayer layer, Collection<OsmPrimitive> toUpload, Changeset changeset, boolean closeChangesetAfterUpload) {
558 return new UploadPrimitivesTask(layer, toUpload, changeset, closeChangesetAfterUpload);
559 }
560
561 /**
562 * The task for uploading a collection of primitives
563 *
564 */
565 public class UploadPrimitivesTask extends PleaseWaitRunnable {
566 private boolean uploadCancelled = false;
567 private Exception lastException = null;
568 private Collection <OsmPrimitive> toUpload;
569 private OsmServerWriter writer;
570 private OsmDataLayer layer;
571 private Changeset changeset;
572 private boolean closeChangesetAfterUpload;
573 private HashSet<OsmPrimitive> processedPrimitives;
574
575 /**
576 *
577 * @param layer the OSM data layer for which data is uploaded
578 * @param toUpload the collection of primitives to upload
579 * @param changeset the changeset to use for uploading
580 * @param closeChangesetAfterUpload true, if the changeset is to be closed after uploading
581 */
582 private UploadPrimitivesTask(OsmDataLayer layer, Collection <OsmPrimitive> toUpload, Changeset changeset, boolean closeChangesetAfterUpload) {
583 super(tr("Uploading data for layer ''{0}''", layer.getName()),false /* don't ignore exceptions */);
584 this.toUpload = toUpload;
585 this.layer = layer;
586 this.changeset = changeset;
587 this.closeChangesetAfterUpload = closeChangesetAfterUpload;
588 this.processedPrimitives = new HashSet<OsmPrimitive>();
589 }
590
591 protected OsmPrimitive getPrimitive(OsmPrimitiveType type, long id) {
592 for (OsmPrimitive p: toUpload) {
593 if (OsmPrimitiveType.from(p).equals(type) && p.getId() == id)
594 return p;
595 }
596 return null;
597 }
598
599 /**
600 * Retries to recover the upload operation from an exception which was thrown because
601 * an uploaded primitive was already deleted on the server.
602 *
603 * @param e the exception throw by the API
604 * @param monitor a progress monitor
605 * @throws OsmTransferException thrown if we can't recover from the exception
606 */
607 protected void recoverFromGoneOnServer(OsmApiPrimitiveGoneException e, ProgressMonitor monitor) throws OsmTransferException{
608 if (!e.isKnownPrimitive()) throw e;
609 OsmPrimitive p = getPrimitive(e.getPrimitiveType(), e.getPrimitiveId());
610 if (p == null) throw e;
611 if (p.isDeleted()) {
612 // we tried to delete an already deleted primitive.
613 //
614 System.out.println(tr("Warning: object ''{0}'' is already deleted on the server. Skipping this object and retrying to upload.", p.getDisplayName(DefaultNameFormatter.getInstance())));
615 monitor.appendLogMessage(tr("Object ''{0}'' is already deleted. Skipping object in upload.",p.getDisplayName(DefaultNameFormatter.getInstance())));
616 processedPrimitives.addAll(writer.getProcessedPrimitives());
617 processedPrimitives.add(p);
618 toUpload.removeAll(processedPrimitives);
619 return;
620 }
621 // exception was thrown because we tried to *update* an already deleted
622 // primitive. We can't resolve this automatically. Re-throw exception,
623 // a conflict is going to be created later.
624 throw e;
625 }
626
627 @Override protected void realRun() throws SAXException, IOException {
628 writer = new OsmServerWriter();
629 try {
630 while(true) {
631 try {
632 getProgressMonitor().subTask(tr("Uploading {0} objects ...", toUpload.size()));
633 writer.uploadOsm(layer.data.getVersion(), toUpload, changeset, getProgressMonitor().createSubTaskMonitor(1, false));
634 processedPrimitives.addAll(writer.getProcessedPrimitives());
635 // if we get here we've successfully uploaded the data. Exit the loop.
636 //
637 break;
638 } catch(OsmApiPrimitiveGoneException e) {
639 // try to recover from the 410 Gone
640 recoverFromGoneOnServer(e, getProgressMonitor());
641 }
642 }
643 // if required close the changeset
644 //
645 if (closeChangesetAfterUpload) {
646 if (changeset != null && changeset.getId() > 0) {
647 OsmApi.getOsmApi().closeChangeset(changeset, progressMonitor.createSubTaskMonitor(0,false));
648 }
649 }
650 } catch (Exception e) {
651 if (uploadCancelled) {
652 System.out.println(tr("Ignoring caught exception because upload is canceled. Exception is: {0}", e.toString()));
653 return;
654 }
655 lastException = e;
656 }
657 }
658
659 @Override protected void finish() {
660 if (uploadCancelled)
661 return;
662
663 // we always clean up the data, even in case of errors. It's possible the data was
664 // partially uploaded
665 //
666 layer.cleanupAfterUpload(processedPrimitives);
667 layer.fireDataChange();
668 if (lastException != null) {
669 handleFailedUpload(lastException);
670 }
671 layer.onPostUploadToServer();
672 if (lastException != null && lastException instanceof ChangesetClosedException) {
673 UploadDialog.getUploadDialog().removeChangeset(changeset);
674 } else {
675 UploadDialog.getUploadDialog().setOrUpdateChangeset(changeset);
676 }
677 }
678
679 @Override protected void cancel() {
680 uploadCancelled = true;
681 if (writer != null) {
682 writer.cancel();
683 }
684 }
685 }
686}
Note: See TracBrowser for help on using the repository browser.