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

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

fixed #3725: JOSM doesn't provide indication during upload that the server is sending 410 Gone errors
Progress dialog now includes a small log window for use cases like this. Use appendLogMessage() on the progress monitor.

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