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

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

fixed #3684: Add "chunked" upload mode
Removed support for API "0.5" when uploading (there are still 0.5-files around, but I'm not aware of any 0.5-servers)

  • Property svn:eol-style set to native
File size: 27.2 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.io.UploadStrategySpecification;
39import org.openstreetmap.josm.gui.layer.OsmDataLayer;
40import org.openstreetmap.josm.gui.progress.ProgressMonitor;
41import org.openstreetmap.josm.io.ChangesetClosedException;
42import org.openstreetmap.josm.io.OsmApi;
43import org.openstreetmap.josm.io.OsmApiException;
44import org.openstreetmap.josm.io.OsmApiInitializationException;
45import org.openstreetmap.josm.io.OsmApiPrimitiveGoneException;
46import org.openstreetmap.josm.io.OsmServerWriter;
47import org.openstreetmap.josm.io.OsmTransferException;
48import org.openstreetmap.josm.tools.DateUtils;
49import org.openstreetmap.josm.tools.ImageProvider;
50import org.openstreetmap.josm.tools.Shortcut;
51import org.xml.sax.SAXException;
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 new UploadPrimitivesTask(
172 UploadDialog.getUploadDialog().getUploadStrategySpecification(),
173 layer,
174 apiData.getPrimitives(),
175 UploadDialog.getUploadDialog().getChangeset(),
176 UploadDialog.getUploadDialog().isDoCloseAfterUpload()
177 )
178 );
179 }
180
181 public void actionPerformed(ActionEvent e) {
182 if (!isEnabled())
183 return;
184 if (Main.map == null) {
185 JOptionPane.showMessageDialog(
186 Main.parent,
187 tr("Nothing to upload. Get some data first."),
188 tr("Warning"),
189 JOptionPane.WARNING_MESSAGE
190 );
191 return;
192 }
193 APIDataSet apiData = new APIDataSet(Main.main.getCurrentDataSet());
194 uploadData(Main.map.mapView.getEditLayer(), apiData);
195 }
196
197 /**
198 * Synchronizes the local state of an {@see OsmPrimitive} with its state on the
199 * server. The method uses an individual GET for the primitive.
200 *
201 * @param id the primitive ID
202 */
203 protected void synchronizePrimitive(final OsmPrimitiveType type, final long id) {
204 Main.worker.execute(new UpdatePrimitiveTask(type, id));
205 }
206
207 /**
208 * Synchronizes the local state of the dataset with the state on the server.
209 *
210 * Reuses the functionality of {@see UpdateDataAction}.
211 *
212 * @see UpdateDataAction#actionPerformed(ActionEvent)
213 */
214 protected void synchronizeDataSet() {
215 UpdateDataAction act = new UpdateDataAction();
216 act.actionPerformed(new ActionEvent(this,0,""));
217 }
218
219 /**
220 * Handles the case that a conflict in a specific {@see OsmPrimitive} was detected while
221 * uploading
222 *
223 * @param primitiveType the type of the primitive, either <code>node</code>, <code>way</code> or
224 * <code>relation</code>
225 * @param id the id of the primitive
226 * @param serverVersion the version of the primitive on the server
227 * @param myVersion the version of the primitive in the local dataset
228 */
229 protected void handleUploadConflictForKnownConflict(final OsmPrimitiveType primitiveType, final long id, String serverVersion, String myVersion) {
230 String lbl = "";
231 switch(primitiveType) {
232 case NODE: lbl = tr("Synchronize node {0} only", id); break;
233 case WAY: lbl = tr("Synchronize way {0} only", id); break;
234 case RELATION: lbl = tr("Synchronize relation {0} only", id); break;
235 }
236 ButtonSpec[] spec = new ButtonSpec[] {
237 new ButtonSpec(
238 lbl,
239 ImageProvider.get("updatedata"),
240 null,
241 null
242 ),
243 new ButtonSpec(
244 tr("Synchronize entire dataset"),
245 ImageProvider.get("updatedata"),
246 null,
247 null
248 ),
249 new ButtonSpec(
250 tr("Cancel"),
251 ImageProvider.get("cancel"),
252 null,
253 null
254 )
255 };
256 String msg = tr("<html>Uploading <strong>failed</strong> because the server has a newer version of one<br>"
257 + "of your nodes, ways, or relations.<br>"
258 + "The conflict is caused by the <strong>{0}</strong> with id <strong>{1}</strong>,<br>"
259 + "the server has version {2}, your version is {3}.<br>"
260 + "<br>"
261 + "Click <strong>{4}</strong> to synchronize the conflicting primitive only.<br>"
262 + "Click <strong>{5}</strong> to synchronize the entire local dataset with the server.<br>"
263 + "Click <strong>{6}</strong> to abort and continue editing.<br></html>",
264 tr(primitiveType.getAPIName()), id, serverVersion, myVersion,
265 spec[0].text, spec[1].text, spec[2].text
266 );
267 int ret = HelpAwareOptionPane.showOptionDialog(
268 Main.parent,
269 msg,
270 tr("Conflicts detected"),
271 JOptionPane.ERROR_MESSAGE,
272 null,
273 spec,
274 spec[0],
275 "/Concepts/Conflict"
276 );
277 switch(ret) {
278 case 0: synchronizePrimitive(primitiveType, id); break;
279 case 1: synchronizeDataSet(); break;
280 default: return;
281 }
282 }
283
284 /**
285 * Handles the case that a conflict was detected while uploading where we don't
286 * know what {@see OsmPrimitive} actually caused the conflict (for whatever reason)
287 *
288 */
289 protected void handleUploadConflictForUnknownConflict() {
290 ButtonSpec[] spec = new ButtonSpec[] {
291 new ButtonSpec(
292 tr("Synchronize entire dataset"),
293 ImageProvider.get("updatedata"),
294 null,
295 null
296 ),
297 new ButtonSpec(
298 tr("Cancel"),
299 ImageProvider.get("cancel"),
300 null,
301 null
302 )
303 };
304 String msg = tr("<html>Uploading <strong>failed</strong> because the server has a newer version of one<br>"
305 + "of your nodes, ways, or relations.<br>"
306 + "<br>"
307 + "Click <strong>{0}</strong> to synchronize the entire local dataset with the server.<br>"
308 + "Click <strong>{1}</strong> to abort and continue editing.<br></html>",
309 spec[0].text, spec[1].text
310 );
311 int ret = HelpAwareOptionPane.showOptionDialog(
312 Main.parent,
313 msg,
314 tr("Conflicts detected"),
315 JOptionPane.ERROR_MESSAGE,
316 null,
317 spec,
318 spec[0],
319 "Concepts/Conflict"
320 );
321 if (ret == 0) {
322 synchronizeDataSet();
323 }
324 }
325
326 /**
327 * Handles the case that a conflict was detected while uploading where we don't
328 * know what {@see OsmPrimitive} actually caused the conflict (for whatever reason)
329 *
330 */
331 protected void handleUploadConflictForClosedChangeset(long changsetId, Date d) {
332 String msg = tr("<html>Uploading <strong>failed</strong> because you''ve been using<br>"
333 + "changeset {0} which was already closed at {1}.<br>"
334 + "Please upload again with a new or an existing open changeset.</html>",
335 changsetId, new SimpleDateFormat().format(d)
336 );
337 JOptionPane.showMessageDialog(
338 Main.parent,
339 msg,
340 tr("Changeset closed"),
341 JOptionPane.ERROR_MESSAGE
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 * error handler for any exception thrown during upload
470 *
471 * @param e the exception
472 */
473 protected void handleFailedUpload(Exception e) {
474 // API initialization failed. Notify the user and return.
475 //
476 if (e instanceof OsmApiInitializationException) {
477 ExceptionDialogUtil.explainOsmApiInitializationException((OsmApiInitializationException)e);
478 return;
479 }
480
481 if (e instanceof OsmApiPrimitiveGoneException) {
482 handleGone((OsmApiPrimitiveGoneException)e);
483 return;
484 }
485 if (e instanceof OsmApiException) {
486 OsmApiException ex = (OsmApiException)e;
487 // There was an upload conflict. Let the user decide whether
488 // and how to resolve it
489 //
490 if(ex.getResponseCode() == HttpURLConnection.HTTP_CONFLICT) {
491 handleUploadConflict(ex);
492 return;
493 }
494 // There was a precondition failed. Notify the user.
495 //
496 else if (ex.getResponseCode() == HttpURLConnection.HTTP_PRECON_FAILED) {
497 handlePreconditionFailed(ex);
498 return;
499 }
500 // Tried to update or delete a primitive which never existed on
501 // the server?
502 //
503 else if (ex.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
504 ExceptionDialogUtil.explainNotFound(ex);
505 return;
506 }
507 }
508
509 ExceptionDialogUtil.explainException(e);
510 }
511
512 /**
513 * The asynchronous task to update a specific id
514 *
515 */
516 class UpdatePrimitiveTask extends PleaseWaitRunnable {
517
518 private boolean uploadCancelled = false;
519 private boolean uploadFailed = false;
520 private Exception lastException = null;
521 private long id;
522 private OsmPrimitiveType type;
523
524 public UpdatePrimitiveTask(OsmPrimitiveType type, long id) {
525 super(tr("Updating primitive"),false /* don't ignore exceptions */);
526 this.id = id;
527 this.type = type;
528 }
529
530 @Override protected void realRun() throws SAXException, IOException {
531 try {
532 UpdateSelectionAction act = new UpdateSelectionAction();
533 act.updatePrimitive(type, id);
534 } catch (Exception sxe) {
535 if (uploadCancelled) {
536 System.out.println("Ignoring exception caught because upload is canceled. Exception is: " + sxe.toString());
537 return;
538 }
539 uploadFailed = true;
540 lastException = sxe;
541 }
542 }
543
544 @Override protected void finish() {
545 if (uploadFailed) {
546 handleFailedUpload(lastException);
547 }
548 }
549
550 @Override protected void cancel() {
551 OsmApi.getOsmApi().cancel();
552 uploadCancelled = true;
553 }
554 }
555
556 /**
557 * The task for uploading a collection of primitives
558 *
559 */
560 public class UploadPrimitivesTask extends PleaseWaitRunnable {
561 private boolean uploadCancelled = false;
562 private Exception lastException = null;
563 private Collection <OsmPrimitive> toUpload;
564 private OsmServerWriter writer;
565 private OsmDataLayer layer;
566 private Changeset changeset;
567 private boolean closeChangesetAfterUpload;
568 private HashSet<OsmPrimitive> processedPrimitives;
569 private UploadStrategySpecification strategy;
570
571 /**
572 * Creates the task
573 * @param strategy the upload strategy
574 * @param layer the OSM data layer for which data is uploaded
575 * @param toUpload the collection of primitives to upload
576 * @param changeset the changeset to use for uploading
577 * @param closeChangesetAfterUpload true, if the changeset is to be closed after uploading
578 */
579 private UploadPrimitivesTask(UploadStrategySpecification strategy, OsmDataLayer layer, Collection <OsmPrimitive> toUpload, Changeset changeset, boolean closeChangesetAfterUpload) {
580 super(tr("Uploading data for layer ''{0}''", layer.getName()),false /* don't ignore exceptions */);
581 this.toUpload = toUpload;
582 this.layer = layer;
583 this.changeset = changeset;
584 this.strategy = strategy;
585 this.closeChangesetAfterUpload = closeChangesetAfterUpload;
586 this.processedPrimitives = new HashSet<OsmPrimitive>();
587 }
588
589 protected OsmPrimitive getPrimitive(OsmPrimitiveType type, long id) {
590 for (OsmPrimitive p: toUpload) {
591 if (OsmPrimitiveType.from(p).equals(type) && p.getId() == id)
592 return p;
593 }
594 return null;
595 }
596
597 /**
598 * Retries to recover the upload operation from an exception which was thrown because
599 * an uploaded primitive was already deleted on the server.
600 *
601 * @param e the exception throw by the API
602 * @param monitor a progress monitor
603 * @throws OsmTransferException thrown if we can't recover from the exception
604 */
605 protected void recoverFromGoneOnServer(OsmApiPrimitiveGoneException e, ProgressMonitor monitor) throws OsmTransferException{
606 if (!e.isKnownPrimitive()) throw e;
607 OsmPrimitive p = getPrimitive(e.getPrimitiveType(), e.getPrimitiveId());
608 if (p == null) throw e;
609 if (p.isDeleted()) {
610 // we tried to delete an already deleted primitive.
611 //
612 System.out.println(tr("Warning: object ''{0}'' is already deleted on the server. Skipping this object and retrying to upload.", p.getDisplayName(DefaultNameFormatter.getInstance())));
613 monitor.appendLogMessage(tr("Object ''{0}'' is already deleted. Skipping object in upload.",p.getDisplayName(DefaultNameFormatter.getInstance())));
614 processedPrimitives.addAll(writer.getProcessedPrimitives());
615 processedPrimitives.add(p);
616 toUpload.removeAll(processedPrimitives);
617 return;
618 }
619 // exception was thrown because we tried to *update* an already deleted
620 // primitive. We can't resolve this automatically. Re-throw exception,
621 // a conflict is going to be created later.
622 throw e;
623 }
624
625 @Override protected void realRun() throws SAXException, IOException {
626 writer = new OsmServerWriter();
627 try {
628 while(true) {
629 try {
630 getProgressMonitor().subTask(tr("Uploading {0} objects ...", toUpload.size()));
631 writer.uploadOsm(strategy, toUpload, changeset, getProgressMonitor().createSubTaskMonitor(1, false));
632 processedPrimitives.addAll(writer.getProcessedPrimitives());
633 // if we get here we've successfully uploaded the data. Exit the loop.
634 //
635 break;
636 } catch(OsmApiPrimitiveGoneException e) {
637 // try to recover from the 410 Gone
638 recoverFromGoneOnServer(e, getProgressMonitor());
639 }
640 }
641 // if required close the changeset
642 //
643 if (closeChangesetAfterUpload) {
644 if (changeset != null && changeset.getId() > 0) {
645 OsmApi.getOsmApi().closeChangeset(changeset, progressMonitor.createSubTaskMonitor(0,false));
646 }
647 }
648 } catch (Exception e) {
649 if (uploadCancelled) {
650 System.out.println(tr("Ignoring caught exception because upload is canceled. Exception is: {0}", e.toString()));
651 return;
652 }
653 lastException = e;
654 }
655 }
656
657 @Override protected void finish() {
658 if (uploadCancelled)
659 return;
660
661 // we always clean up the data, even in case of errors. It's possible the data was
662 // partially uploaded
663 //
664 layer.cleanupAfterUpload(processedPrimitives);
665 layer.fireDataChange();
666 if (lastException != null) {
667 handleFailedUpload(lastException);
668 }
669 layer.onPostUploadToServer();
670 if (lastException != null && lastException instanceof ChangesetClosedException) {
671 UploadDialog.getUploadDialog().removeChangeset(changeset);
672 } else {
673 UploadDialog.getUploadDialog().setOrUpdateChangeset(changeset);
674 }
675 }
676
677 @Override protected void cancel() {
678 uploadCancelled = true;
679 if (writer != null) {
680 writer.cancel();
681 }
682 }
683 }
684}
Note: See TracBrowser for help on using the repository browser.