source: josm/trunk/src/org/openstreetmap/josm/io/OsmServerWriter.java@ 729

Last change on this file since 729 was 679, checked in by stoecker, 16 years ago

finished XML based translations of presets fixes #960
correct bounding box illegal access fixes #1044
do no longer modify objects when unnessesary (can result in 0 objects
modified in Undo :-)
correct typo fixes #730
some I18N corrections

  • Property svn:eol-style set to native
File size: 15.2 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.io;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.BufferedReader;
7import java.io.ByteArrayOutputStream;
8import java.io.IOException;
9import java.io.InputStream;
10import java.io.InputStreamReader;
11import java.io.OutputStream;
12import java.net.HttpURLConnection;
13import java.net.URL;
14import java.net.UnknownHostException;
15import java.net.SocketTimeoutException;
16import java.util.Collection;
17import java.util.LinkedList;
18import javax.swing.JOptionPane;
19
20import org.openstreetmap.josm.Main;
21import org.openstreetmap.josm.data.osm.Relation;
22import org.openstreetmap.josm.data.osm.Node;
23import org.openstreetmap.josm.data.osm.OsmPrimitive;
24import org.openstreetmap.josm.data.osm.Way;
25import org.openstreetmap.josm.data.osm.Changeset;
26import org.openstreetmap.josm.data.osm.visitor.NameVisitor;
27import org.openstreetmap.josm.data.osm.visitor.Visitor;
28import org.xml.sax.SAXException;
29import org.openstreetmap.josm.io.XmlWriter.OsmWriterInterface;
30
31/**
32 * Class that uploads all changes to the osm server.
33 *
34 * This is done like this: - All objects with id = 0 are uploaded as new, except
35 * those in deleted, which are ignored - All objects in deleted list are
36 * deleted. - All remaining objects with modified flag set are updated.
37 *
38 * This class implements visitor and will perform the correct upload action on
39 * the visited element.
40 *
41 * @author imi
42 */
43public class OsmServerWriter extends OsmConnection implements Visitor {
44
45 /**
46 * This list contain all successful processed objects. The caller of
47 * upload* has to check this after the call and update its dataset.
48 *
49 * If a server connection error occurs, this may contain fewer entries
50 * than where passed in the list to upload*.
51 */
52 public Collection<OsmPrimitive> processed;
53
54 /**
55 * Whether the operation should be aborted as soon as possible.
56 */
57 private boolean cancel = false;
58
59 /**
60 * Object describing current changeset
61 */
62 private Changeset changeset;
63
64 /**
65 * Send the dataset to the server. Ask the user first and does nothing if he
66 * does not want to send the data.
67 */
68 private static final int MSECS_PER_SECOND = 1000;
69 private static final int SECONDS_PER_MINUTE = 60;
70 private static final int MSECS_PER_MINUTE = MSECS_PER_SECOND * SECONDS_PER_MINUTE;
71
72 long uploadStartTime;
73 public String timeLeft(int progress, int list_size) {
74 long now = System.currentTimeMillis();
75 long elapsed = now - uploadStartTime;
76 if (elapsed == 0)
77 elapsed = 1;
78 float uploads_per_ms = (float)progress / elapsed;
79 float uploads_left = list_size - progress;
80 int ms_left = (int)(uploads_left / uploads_per_ms);
81 int minutes_left = ms_left / MSECS_PER_MINUTE;
82 int seconds_left = (ms_left / MSECS_PER_SECOND) % SECONDS_PER_MINUTE ;
83 String time_left_str = Integer.toString(minutes_left) + ":";
84 if (seconds_left < 10)
85 time_left_str += "0";
86 time_left_str += Integer.toString(seconds_left);
87 return time_left_str;
88 }
89 public void uploadOsm(Collection<OsmPrimitive> list) throws SAXException {
90 processed = new LinkedList<OsmPrimitive>();
91 initAuthentication();
92
93 Main.pleaseWaitDlg.progress.setMaximum(list.size());
94 Main.pleaseWaitDlg.progress.setValue(0);
95
96 boolean useChangesets = Main.pref.get("osm-server.version", "0.5").equals("0.6");
97
98 String comment = null;
99 while( useChangesets && comment == null)
100 {
101 comment = JOptionPane.showInputDialog(Main.parent, tr("Provide a brief comment as to the changes to you are uploading:"),
102 tr("Commit comment"), JOptionPane.QUESTION_MESSAGE);
103 if( comment == null )
104 return;
105 /* Don't let people just hit enter */
106 if( comment.trim().length() >= 3 )
107 break;
108 comment = null;
109 }
110 if( useChangesets && !startChangeset(10, comment) )
111 return;
112
113 NameVisitor v = new NameVisitor();
114 try {
115 uploadStartTime = System.currentTimeMillis();
116 for (OsmPrimitive osm : list) {
117 if (cancel)
118 return;
119 osm.visit(v);
120 int progress = Main.pleaseWaitDlg.progress.getValue();
121 String time_left_str = timeLeft(progress, list.size());
122 Main.pleaseWaitDlg.currentAction.setText(tr("Upload {0} {1} (id: {2}) {3}% {4}/{5} ({6} left)...",
123 tr(v.className), v.name, osm.id, 100.0*progress/list.size(), progress, list.size(), time_left_str));
124 osm.visit(this);
125 Main.pleaseWaitDlg.progress.setValue(Main.pleaseWaitDlg.progress.getValue()+1);
126 Main.pleaseWaitDlg.progress.setValue(progress+1);
127 }
128 if( useChangesets ) stopChangeset(10);
129 } catch (RuntimeException e) {
130 if( useChangesets ) stopChangeset(10);
131 e.printStackTrace();
132 throw new SAXException(tr("An error occoured: {0}",e.getMessage()));
133 }
134 }
135
136 /* FIXME: This code is terrible, please fix it!!!! */
137
138 /* Ok, this needs some explanation: The problem is that the code for
139 * retrying requests is intertwined with the code that generates the
140 * actual request. This means that for the retry code for the
141 * changeset stuff, it's basically a copy/cut/change slightly
142 * process. What actually needs to happen is that the retrying needs
143 * to be split from the creation of the requests and the retry loop
144 * handled in one place (preferably without recursion). While at you
145 * can fix the issue where hitting cancel doesn't do anything while
146 * retrying. - Mv0 Apr 2008
147 */
148 private boolean startChangeset(int retries, String comment) {
149 Main.pleaseWaitDlg.currentAction.setText(tr("Opening changeset..."));
150 changeset = new Changeset();
151 changeset.put( "created_by", "JOSM" );
152 changeset.put( "comment", comment );
153 try {
154 if (cancel)
155 return false; // assume cancel
156 String version = Main.pref.get("osm-server.version", "0.6");
157 URL url = new URL(
158 Main.pref.get("osm-server.url") +
159 "/" + version +
160 "/" + "changeset" +
161 "/" + "create");
162 System.out.print("upload to: "+url+ "..." );
163 activeConnection = (HttpURLConnection)url.openConnection();
164 activeConnection.setConnectTimeout(15000);
165 activeConnection.setRequestMethod("PUT");
166 addAuth(activeConnection);
167
168 activeConnection.setDoOutput(true);
169 OutputStream out = activeConnection.getOutputStream();
170 OsmWriter.output(out, changeset);
171 out.close();
172
173 activeConnection.connect();
174 System.out.println("connected");
175
176 int retCode = activeConnection.getResponseCode();
177 if (retCode == 200)
178 changeset.id = readId(activeConnection.getInputStream());
179 System.out.println("got return: "+retCode+" with id "+changeset.id);
180 String retMsg = activeConnection.getResponseMessage();
181 activeConnection.disconnect();
182 if (retCode == 404)
183 {
184 System.out.println("Server does not support changesets, continuing");
185 return true;
186 }
187 if (retCode != 200 && retCode != 412) {
188 if (retries >= 0) {
189 retries--;
190 System.out.print("backing off for 10 seconds...");
191 Thread.sleep(10000);
192 System.out.println("retrying ("+retries+" left)");
193 return startChangeset(retries, comment);
194 } else {
195 // Look for a detailed error message from the server
196 if (activeConnection.getHeaderField("Error") != null)
197 retMsg += "\n" + activeConnection.getHeaderField("Error");
198
199 // Report our error
200 ByteArrayOutputStream o = new ByteArrayOutputStream();
201 OsmWriter.output(o, changeset);
202 System.out.println(new String(o.toByteArray(), "UTF-8").toString());
203 throw new RuntimeException(retCode+" "+retMsg);
204 }
205 }
206 } catch (UnknownHostException e) {
207 throw new RuntimeException(tr("Unknown host")+": "+e.getMessage(), e);
208 } catch(SocketTimeoutException e) {
209 System.out.println(" timed out, retries left: " + retries);
210 if (cancel)
211 return false; // assume cancel
212 if (retries-- > 0)
213 startChangeset(retries, comment);
214 else
215 throw new RuntimeException(e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
216 } catch (Exception e) {
217 if (cancel)
218 return false; // assume cancel
219 if (e instanceof RuntimeException)
220 throw (RuntimeException)e;
221 throw new RuntimeException(e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
222 }
223 return true;
224 }
225
226 private void stopChangeset(int retries) {
227 Main.pleaseWaitDlg.currentAction.setText(tr("Closing changeset..."));
228 try {
229 if (cancel)
230 return; // assume cancel
231 String version = Main.pref.get("osm-server.version", "0.6");
232 URL url = new URL(
233 Main.pref.get("osm-server.url") +
234 "/" + version +
235 "/" + "changeset" +
236 "/" + changeset.id +
237 "/close" );
238 System.out.print("upload to: "+url+ "..." );
239 activeConnection = (HttpURLConnection)url.openConnection();
240 activeConnection.setConnectTimeout(15000);
241 activeConnection.setRequestMethod("PUT");
242 addAuth(activeConnection);
243
244 activeConnection.setDoOutput(true);
245 OutputStream out = activeConnection.getOutputStream();
246 OsmWriter.output(out, changeset);
247 out.close();
248
249 activeConnection.connect();
250 System.out.println("connected");
251
252 int retCode = activeConnection.getResponseCode();
253 if (retCode == 200)
254 changeset.id = readId(activeConnection.getInputStream());
255 System.out.println("got return: "+retCode+" with id "+changeset.id);
256 String retMsg = activeConnection.getResponseMessage();
257 activeConnection.disconnect();
258 if (retCode == 404)
259 {
260 System.out.println("Server does not support changesets, continuing");
261 return;
262 }
263 if (retCode != 200 && retCode != 412) {
264 if (retries >= 0) {
265 retries--;
266 System.out.print("backing off for 10 seconds...");
267 Thread.sleep(10000);
268 System.out.println("retrying ("+retries+" left)");
269 stopChangeset(retries);
270 } else {
271 // Look for a detailed error message from the server
272 if (activeConnection.getHeaderField("Error") != null)
273 retMsg += "\n" + activeConnection.getHeaderField("Error");
274
275 // Report our error
276 ByteArrayOutputStream o = new ByteArrayOutputStream();
277 OsmWriter.output(o, changeset);
278 System.out.println(new String(o.toByteArray(), "UTF-8").toString());
279 throw new RuntimeException(retCode+" "+retMsg);
280 }
281 }
282 } catch (UnknownHostException e) {
283 throw new RuntimeException(tr("Unknown host")+": "+e.getMessage(), e);
284 } catch(SocketTimeoutException e) {
285 System.out.println(" timed out, retries left: " + retries);
286 if (cancel)
287 return; // assume cancel
288 if (retries-- > 0)
289 stopChangeset(retries);
290 else
291 throw new RuntimeException(e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
292 } catch (Exception e) {
293 if (cancel)
294 return; // assume cancel
295 if (e instanceof RuntimeException)
296 throw (RuntimeException)e;
297 throw new RuntimeException(e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
298 }
299 }
300
301 /**
302 * Upload a single node.
303 */
304 public void visit(Node n) {
305 if (n.deleted) {
306 sendRequest("DELETE", "node", n, true);
307 } else {
308 sendRequest("PUT", "node", n, true);
309 }
310 processed.add(n);
311 }
312
313 /**
314 * Upload a whole way with the complete node id list.
315 */
316 public void visit(Way w) {
317 if (w.deleted) {
318 sendRequest("DELETE", "way", w, true);
319 } else {
320 sendRequest("PUT", "way", w, true);
321 }
322 processed.add(w);
323 }
324
325 /**
326 * Upload an relation with all members.
327 */
328 public void visit(Relation e) {
329 if (e.deleted) {
330 sendRequest("DELETE", "relation", e, true);
331 } else {
332 sendRequest("PUT", "relation", e, true);
333 }
334 processed.add(e);
335 }
336 /**
337 * Read a long from the input stream and return it.
338 */
339 private long readId(InputStream inputStream) throws IOException {
340 BufferedReader in = new BufferedReader(new InputStreamReader(
341 inputStream));
342 String s = in.readLine();
343 if (s == null)
344 return 0;
345 try {
346 return Long.parseLong(s);
347 } catch (NumberFormatException e) {
348 return 0;
349 }
350 }
351
352 /**
353 * Send the request. The objects id will be replaced if it was 0 before
354 * (on add requests).
355 *
356 * @param requestMethod The http method used when talking with the server.
357 * @param urlSuffix The suffix to add at the server url.
358 * @param osm The primitive to encode to the server.
359 * @param body the body to be sent
360 */
361 private void sendRequestRetry(String requestMethod, String urlSuffix,
362 OsmPrimitive osm, OsmWriterInterface body, int retries) {
363 try {
364 if (cancel)
365 return; // assume cancel
366 String version = Main.pref.get("osm-server.version", "0.5");
367 URL url = new URL(
368 new URL(Main.pref.get("osm-server.url") +
369 "/" + version + "/"),
370 urlSuffix +
371 "/" + (osm.id==0 ? "create" : osm.id),
372 (java.net.URLStreamHandler)new MyHttpHandler());
373 System.out.print("upload to: "+url+ "..." );
374 activeConnection = (HttpURLConnection)url.openConnection();
375 activeConnection.setConnectTimeout(15000);
376 activeConnection.setRequestMethod(requestMethod);
377 addAuth(activeConnection);
378 if (body != null) {
379 activeConnection.setDoOutput(true);
380 OutputStream out = activeConnection.getOutputStream();
381 OsmWriter.output(out, body);
382 out.close();
383 }
384 activeConnection.connect();
385 System.out.println("connected");
386
387 int retCode = activeConnection.getResponseCode();
388 /* When creating new, the returned value is the new id, otherwise it is the new version */
389 if (retCode == 200)
390 if(osm.id == 0)
391 {
392 osm.id = readId(activeConnection.getInputStream());
393 osm.version = 1;
394 }
395 else
396 {
397 int read_version = (int)readId(activeConnection.getInputStream());
398 if( read_version > 0 )
399 osm.version = read_version;
400 }
401 System.out.println("got return: "+retCode+" with id "+osm.id);
402 String retMsg = activeConnection.getResponseMessage();
403 activeConnection.disconnect();
404 if (retCode == 410 && requestMethod.equals("DELETE"))
405 return; // everything fine.. was already deleted.
406 if (retCode != 200 && retCode != 412) {
407 if (retries >= 0) {
408 retries--;
409 System.out.print("backing off for 10 seconds...");
410 Thread.sleep(10000);
411 System.out.println("retrying ("+retries+" left)");
412 sendRequestRetry(requestMethod, urlSuffix, osm, body, retries);
413 } else {
414 // Look for a detailed error message from the server
415 if (activeConnection.getHeaderField("Error") != null)
416 retMsg += "\n" + activeConnection.getHeaderField("Error");
417
418 // Report our error
419 ByteArrayOutputStream o = new ByteArrayOutputStream();
420 OsmWriter.output(o, body);
421 System.out.println(new String(o.toByteArray(), "UTF-8").toString());
422 throw new RuntimeException(retCode+" "+retMsg);
423 }
424 }
425 } catch (UnknownHostException e) {
426 throw new RuntimeException(tr("Unknown host")+": "+e.getMessage(), e);
427 } catch(SocketTimeoutException e) {
428 System.out.println(" timed out, retries left: " + retries);
429 if (cancel)
430 return; // assume cancel
431 if (retries-- > 0)
432 sendRequestRetry(requestMethod, urlSuffix, osm, body, retries);
433 else
434 throw new RuntimeException(e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
435 } catch (Exception e) {
436 if (cancel)
437 return; // assume cancel
438 if (e instanceof RuntimeException)
439 throw (RuntimeException)e;
440 throw new RuntimeException(e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
441 }
442 }
443 private void sendRequest(String requestMethod, String urlSuffix,
444 OsmPrimitive osm, boolean addBody) {
445 XmlWriter.OsmWriterInterface body = null;
446 if (addBody) {
447 body = new OsmWriter.Single(osm, true, changeset);
448 }
449 sendRequestRetry(requestMethod, urlSuffix, osm, body, 10);
450 }
451}
Note: See TracBrowser for help on using the repository browser.