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

Last change on this file since 2990 was 2990, checked in by jttt, 14 years ago

Fix some eclipse warnings

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