source: josm/trunk/src/org/openstreetmap/josm/gui/io/UploadPrimitivesTask.java@ 4932

Last change on this file since 4932 was 4534, checked in by Don-vip, 13 years ago

Fixed a bunch of compile warnings

  • Property svn:eol-style set to native
File size: 16.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.io;
3
4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.CheckParameterUtil.ensureParameterNotNull;
6import static org.openstreetmap.josm.tools.I18n.tr;
7import static org.openstreetmap.josm.tools.I18n.trn;
8
9import java.io.IOException;
10import java.lang.reflect.InvocationTargetException;
11import java.util.HashSet;
12
13import javax.swing.JOptionPane;
14import javax.swing.SwingUtilities;
15
16import org.openstreetmap.josm.Main;
17import org.openstreetmap.josm.data.APIDataSet;
18import org.openstreetmap.josm.data.osm.Changeset;
19import org.openstreetmap.josm.data.osm.ChangesetCache;
20import org.openstreetmap.josm.data.osm.IPrimitive;
21import org.openstreetmap.josm.data.osm.OsmPrimitive;
22import org.openstreetmap.josm.gui.DefaultNameFormatter;
23import org.openstreetmap.josm.gui.HelpAwareOptionPane;
24import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
25import org.openstreetmap.josm.gui.layer.OsmDataLayer;
26import org.openstreetmap.josm.gui.progress.ProgressMonitor;
27import org.openstreetmap.josm.io.ChangesetClosedException;
28import org.openstreetmap.josm.io.OsmApi;
29import org.openstreetmap.josm.io.OsmApiPrimitiveGoneException;
30import org.openstreetmap.josm.io.OsmServerWriter;
31import org.openstreetmap.josm.io.OsmTransferCanceledException;
32import org.openstreetmap.josm.io.OsmTransferException;
33import org.openstreetmap.josm.tools.ImageProvider;
34import org.xml.sax.SAXException;
35
36/**
37 * The task for uploading a collection of primitives
38 *
39 */
40public class UploadPrimitivesTask extends AbstractUploadTask {
41 private boolean uploadCanceled = false;
42 private Exception lastException = null;
43 private APIDataSet toUpload;
44 private OsmServerWriter writer;
45 private OsmDataLayer layer;
46 private Changeset changeset;
47 private HashSet<IPrimitive> processedPrimitives;
48 private UploadStrategySpecification strategy;
49
50 /**
51 * Creates the task
52 *
53 * @param strategy the upload strategy. Must not be null.
54 * @param layer the OSM data layer for which data is uploaded. Must not be null.
55 * @param toUpload the collection of primitives to upload. Set to the empty collection if null.
56 * @param changeset the changeset to use for uploading. Must not be null. changeset.getId()
57 * can be 0 in which case the upload task creates a new changeset
58 * @throws IllegalArgumentException thrown if layer is null
59 * @throws IllegalArgumentException thrown if toUpload is null
60 * @throws IllegalArgumentException thrown if strategy is null
61 * @throws IllegalArgumentException thrown if changeset is null
62 */
63 public UploadPrimitivesTask(UploadStrategySpecification strategy, OsmDataLayer layer, APIDataSet toUpload, Changeset changeset) {
64 super(tr("Uploading data for layer ''{0}''", layer.getName()),false /* don't ignore exceptions */);
65 ensureParameterNotNull(layer,"layer");
66 ensureParameterNotNull(strategy, "strategy");
67 ensureParameterNotNull(changeset, "changeset");
68 this.toUpload = toUpload;
69 this.layer = layer;
70 this.changeset = changeset;
71 this.strategy = strategy;
72 this.processedPrimitives = new HashSet<IPrimitive>();
73 }
74
75 protected MaxChangesetSizeExceededPolicy askMaxChangesetSizeExceedsPolicy() {
76 ButtonSpec[] specs = new ButtonSpec[] {
77 new ButtonSpec(
78 tr("Continue uploading"),
79 ImageProvider.get("upload"),
80 tr("Click to continue uploading to additional new changesets"),
81 null /* no specific help text */
82 ),
83 new ButtonSpec(
84 tr("Go back to Upload Dialog"),
85 ImageProvider.get("dialogs", "uploadproperties"),
86 tr("Click to return to the Upload Dialog"),
87 null /* no specific help text */
88 ),
89 new ButtonSpec(
90 tr("Abort"),
91 ImageProvider.get("cancel"),
92 tr("Click to abort uploading"),
93 null /* no specific help text */
94 )
95 };
96 int numObjectsToUploadLeft = toUpload.getSize() - processedPrimitives.size();
97 String msg1 = tr("The server reported that the current changeset was closed.<br>"
98 + "This is most likely because the changesets size exceeded the max. size<br>"
99 + "of {0} objects on the server ''{1}''.",
100 OsmApi.getOsmApi().getCapabilities().getMaxChangesetSize(),
101 OsmApi.getOsmApi().getBaseUrl()
102 );
103 String msg2 = trn(
104 "There is {0} object left to upload.",
105 "There are {0} objects left to upload.",
106 numObjectsToUploadLeft,
107 numObjectsToUploadLeft
108 );
109 String msg3 = tr(
110 "Click ''<strong>{0}</strong>'' to continue uploading to additional new changesets.<br>"
111 + "Click ''<strong>{1}</strong>'' to return to the upload dialog.<br>"
112 + "Click ''<strong>{2}</strong>'' to abort uploading and return to map editing.<br>",
113 specs[0].text,
114 specs[1].text,
115 specs[2].text
116 );
117 String msg = "<html>" + msg1 + "<br><br>" + msg2 +"<br><br>" + msg3 + "</html>";
118 int ret = HelpAwareOptionPane.showOptionDialog(
119 Main.parent,
120 msg,
121 tr("Changeset is full"),
122 JOptionPane.WARNING_MESSAGE,
123 null, /* no special icon */
124 specs,
125 specs[0],
126 ht("/Action/Upload#ChangesetFull")
127 );
128 switch(ret) {
129 case 0: return MaxChangesetSizeExceededPolicy.AUTOMATICALLY_OPEN_NEW_CHANGESETS;
130 case 1: return MaxChangesetSizeExceededPolicy.FILL_ONE_CHANGESET_AND_RETURN_TO_UPLOAD_DIALOG;
131 case 2: return MaxChangesetSizeExceededPolicy.ABORT;
132 case JOptionPane.CLOSED_OPTION: return MaxChangesetSizeExceededPolicy.ABORT;
133 }
134 // should not happen
135 return null;
136 }
137
138 protected void openNewChangeset() {
139 // make sure the current changeset is removed from the upload dialog.
140 //
141 ChangesetCache.getInstance().update(changeset);
142 Changeset newChangeSet = new Changeset();
143 newChangeSet.setKeys(this.changeset.getKeys());
144 this.changeset = newChangeSet;
145 }
146
147 protected boolean recoverFromChangesetFullException() {
148 if (toUpload.getSize() - processedPrimitives.size() == 0) {
149 strategy.setPolicy(MaxChangesetSizeExceededPolicy.ABORT);
150 return false;
151 }
152 if (strategy.getPolicy() == null || strategy.getPolicy().equals(MaxChangesetSizeExceededPolicy.ABORT)) {
153 MaxChangesetSizeExceededPolicy policy = askMaxChangesetSizeExceedsPolicy();
154 strategy.setPolicy(policy);
155 }
156 switch(strategy.getPolicy()) {
157 case ABORT:
158 // don't continue - finish() will send the user back to map editing
159 //
160 return false;
161 case FILL_ONE_CHANGESET_AND_RETURN_TO_UPLOAD_DIALOG:
162 // don't continue - finish() will send the user back to the upload dialog
163 //
164 return false;
165 case AUTOMATICALLY_OPEN_NEW_CHANGESETS:
166 // prepare the state of the task for a next iteration in uploading.
167 //
168 openNewChangeset();
169 toUpload.removeProcessed(processedPrimitives);
170 return true;
171 }
172 // should not happen
173 return false;
174 }
175
176 /**
177 * Retries to recover the upload operation from an exception which was thrown because
178 * an uploaded primitive was already deleted on the server.
179 *
180 * @param e the exception throw by the API
181 * @param monitor a progress monitor
182 * @throws OsmTransferException thrown if we can't recover from the exception
183 */
184 protected void recoverFromGoneOnServer(OsmApiPrimitiveGoneException e, ProgressMonitor monitor) throws OsmTransferException{
185 if (!e.isKnownPrimitive()) throw e;
186 OsmPrimitive p = layer.data.getPrimitiveById(e.getPrimitiveId(), e.getPrimitiveType());
187 if (p == null) throw e;
188 if (p.isDeleted()) {
189 // we tried to delete an already deleted primitive.
190 //
191 System.out.println(tr("Warning: object ''{0}'' is already deleted on the server. Skipping this object and retrying to upload.", p.getDisplayName(DefaultNameFormatter.getInstance())));
192 monitor.appendLogMessage(tr("Object ''{0}'' is already deleted. Skipping object in upload.",p.getDisplayName(DefaultNameFormatter.getInstance())));
193 processedPrimitives.addAll(writer.getProcessedPrimitives());
194 processedPrimitives.add(p);
195 toUpload.removeProcessed(processedPrimitives);
196 return;
197 }
198 // exception was thrown because we tried to *update* an already deleted
199 // primitive. We can't resolve this automatically. Re-throw exception,
200 // a conflict is going to be created later.
201 throw e;
202 }
203
204 protected void cleanupAfterUpload() {
205 // we always clean up the data, even in case of errors. It's possible the data was
206 // partially uploaded. Better run on EDT.
207 //
208 Runnable r = new Runnable() {
209 public void run() {
210 layer.cleanupAfterUpload(processedPrimitives);
211 layer.onPostUploadToServer();
212 ChangesetCache.getInstance().update(changeset);
213 }
214 };
215
216 try {
217 SwingUtilities.invokeAndWait(r);
218 } catch(InterruptedException e) {
219 lastException = e;
220 } catch(InvocationTargetException e) {
221 lastException = new OsmTransferException(e.getCause());
222 }
223 }
224
225 @Override protected void realRun() throws SAXException, IOException {
226 try {
227 uploadloop:while(true) {
228 try {
229 getProgressMonitor().subTask(trn("Uploading {0} object...", "Uploading {0} objects...", toUpload.getSize(), toUpload.getSize()));
230 synchronized(this) {
231 writer = new OsmServerWriter();
232 }
233 writer.uploadOsm(strategy, toUpload.getPrimitives(), changeset, getProgressMonitor().createSubTaskMonitor(1, false));
234
235 // if we get here we've successfully uploaded the data. Exit the loop.
236 //
237 break;
238 } catch(OsmTransferCanceledException e) {
239 e.printStackTrace();
240 uploadCanceled = true;
241 break uploadloop;
242 } catch(OsmApiPrimitiveGoneException e) {
243 // try to recover from 410 Gone
244 //
245 recoverFromGoneOnServer(e, getProgressMonitor());
246 } catch(ChangesetClosedException e) {
247 processedPrimitives.addAll(writer.getProcessedPrimitives()); // OsmPrimitive in => OsmPrimitive out
248 changeset.setOpen(false);
249 switch(e.getSource()) {
250 case UNSPECIFIED:
251 throw e;
252 case UPDATE_CHANGESET:
253 // The changeset was closed when we tried to update it. Probably, our
254 // local list of open changesets got out of sync with the server state.
255 // The user will have to select another open changeset.
256 // Rethrow exception - this will be handled later.
257 //
258 throw e;
259 case UPLOAD_DATA:
260 // Most likely the changeset is full. Try to recover and continue
261 // with a new changeset, but let the user decide first (see
262 // recoverFromChangesetFullException)
263 //
264 if (recoverFromChangesetFullException()) {
265 continue;
266 }
267 lastException = e;
268 break uploadloop;
269 }
270 } finally {
271 if (writer != null) {
272 processedPrimitives.addAll(writer.getProcessedPrimitives());
273 }
274 synchronized(this) {
275 writer = null;
276 }
277 }
278 }
279 // if required close the changeset
280 //
281 if (strategy.isCloseChangesetAfterUpload() && changeset != null && !changeset.isNew() && changeset.isOpen()) {
282 OsmApi.getOsmApi().closeChangeset(changeset, progressMonitor.createSubTaskMonitor(0, false));
283 }
284 } catch (Exception e) {
285 if (uploadCanceled) {
286 System.out.println(tr("Ignoring caught exception because upload is canceled. Exception is: {0}", e.toString()));
287 } else {
288 lastException = e;
289 }
290 }
291 if (uploadCanceled && processedPrimitives.isEmpty()) return;
292 cleanupAfterUpload();
293 }
294
295 @Override protected void finish() {
296 if (uploadCanceled)
297 return;
298
299 // depending on the success of the upload operation and on the policy for
300 // multi changeset uploads this will sent the user back to the appropriate
301 // place in JOSM, either
302 // - to an error dialog
303 // - to the Upload Dialog
304 // - to map editing
305 Runnable r = new Runnable() {
306 public void run() {
307 // if the changeset is still open after this upload we want it to
308 // be selected on the next upload
309 //
310 ChangesetCache.getInstance().update(changeset);
311 if (changeset != null && changeset.isOpen()) {
312 UploadDialog.getUploadDialog().setSelectedChangesetForNextUpload(changeset);
313 }
314 if (lastException == null)
315 return;
316 if (lastException instanceof ChangesetClosedException) {
317 ChangesetClosedException e = (ChangesetClosedException)lastException;
318 if (e.getSource().equals(ChangesetClosedException.Source.UPDATE_CHANGESET)) {
319 handleFailedUpload(lastException);
320 return;
321 }
322 if (strategy.getPolicy() == null)
323 /* do nothing if unknown policy */
324 return;
325 if (e.getSource().equals(ChangesetClosedException.Source.UPLOAD_DATA)) {
326 switch(strategy.getPolicy()) {
327 case ABORT:
328 break; /* do nothing - we return to map editing */
329 case AUTOMATICALLY_OPEN_NEW_CHANGESETS:
330 break; /* do nothing - we return to map editing */
331 case FILL_ONE_CHANGESET_AND_RETURN_TO_UPLOAD_DIALOG:
332 // return to the upload dialog
333 //
334 toUpload.removeProcessed(processedPrimitives);
335 UploadDialog.getUploadDialog().setUploadedPrimitives(toUpload);
336 UploadDialog.getUploadDialog().setVisible(true);
337 break;
338 }
339 } else {
340 handleFailedUpload(lastException);
341 }
342 } else {
343 handleFailedUpload(lastException);
344 }
345 }
346 };
347 if (SwingUtilities.isEventDispatchThread()) {
348 r.run();
349 } else {
350 SwingUtilities.invokeLater(r);
351 }
352 }
353
354 @Override protected void cancel() {
355 uploadCanceled = true;
356 synchronized(this) {
357 if (writer != null) {
358 writer.cancel();
359 }
360 }
361 }
362}
Note: See TracBrowser for help on using the repository browser.