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

Last change on this file since 529 was 529, checked in by gebner, 16 years ago

Part one of patch by Dave Hansen <dave@…>

  • Remove unused imports
  • Main.debug
  • Make attribute merging aware of TIGER-import attributes
  • Better upload progress information
  • Retry uploads
File size: 8.7 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;
18
19import org.openstreetmap.josm.Main;
20import org.openstreetmap.josm.data.osm.Relation;
21import org.openstreetmap.josm.data.osm.Node;
22import org.openstreetmap.josm.data.osm.OsmPrimitive;
23import org.openstreetmap.josm.data.osm.Way;
24import org.openstreetmap.josm.data.osm.visitor.NameVisitor;
25import org.openstreetmap.josm.data.osm.visitor.Visitor;
26import org.xml.sax.SAXException;
27
28/**
29 * Class that uploades all changes to the osm server.
30 *
31 * This is done like this: - All objects with id = 0 are uploaded as new, except
32 * those in deleted, which are ignored - All objects in deleted list are
33 * deleted. - All remaining objects with modified flag set are updated.
34 *
35 * This class implements visitor and will perform the correct upload action on
36 * the visited element.
37 *
38 * @author imi
39 */
40public class OsmServerWriter extends OsmConnection implements Visitor {
41
42 /**
43 * This list contain all sucessfull processed objects. The caller of
44 * upload* has to check this after the call and update its dataset.
45 *
46 * If a server connection error occours, this may contain fewer entries
47 * than where passed in the list to upload*.
48 */
49 public Collection<OsmPrimitive> processed;
50
51 /**
52 * Whether the operation should be aborted as soon as possible.
53 */
54 private boolean cancel = false;
55
56 /**
57 * Send the dataset to the server. Ask the user first and does nothing if he
58 * does not want to send the data.
59 */
60 private static final int MSECS_PER_SECOND = 1000;
61 private static final int SECONDS_PER_MINUTE = 60;
62 private static final int MSECS_PER_MINUTE = MSECS_PER_SECOND * SECONDS_PER_MINUTE;
63
64 long uploadStartTime;
65 public String timeLeft(int progress, int list_size) {
66 long now = System.currentTimeMillis();
67 long elapsed = now - uploadStartTime;
68 if (elapsed == 0)
69 elapsed = 1;
70 float uploads_per_ms = (float)progress / elapsed;
71 float uploads_left = list_size - progress;
72 int ms_left = (int)(uploads_left / uploads_per_ms);
73 int minutes_left = ms_left / MSECS_PER_MINUTE;
74 int seconds_left = (ms_left / MSECS_PER_SECOND) % SECONDS_PER_MINUTE ;
75 String time_left_str = Integer.toString(minutes_left) + ":";
76 if (seconds_left < 10)
77 time_left_str += "0";
78 time_left_str += Integer.toString(seconds_left);
79 return time_left_str;
80 }
81 public void uploadOsm(Collection<OsmPrimitive> list) throws SAXException {
82 processed = new LinkedList<OsmPrimitive>();
83 initAuthentication();
84
85 Main.pleaseWaitDlg.progress.setMaximum(list.size());
86 Main.pleaseWaitDlg.progress.setValue(0);
87
88 NameVisitor v = new NameVisitor();
89 try {
90 uploadStartTime = System.currentTimeMillis();
91 for (OsmPrimitive osm : list) {
92 if (cancel)
93 return;
94 osm.visit(v);
95 int progress = Main.pleaseWaitDlg.progress.getValue();
96 String time_left_str = timeLeft(progress, list.size());
97 Main.pleaseWaitDlg.currentAction.setText(tr("Upload {0} {1} (id: {2}) {3}% {4}/{5} ({6} left)...",
98 tr(v.className), v.name, osm.id, 100.0*progress/list.size(), progress, list.size(), time_left_str));
99 osm.visit(this);
100 Main.pleaseWaitDlg.progress.setValue(Main.pleaseWaitDlg.progress.getValue()+1);
101 Main.pleaseWaitDlg.progress.setValue(progress+1);
102 }
103 } catch (RuntimeException e) {
104 e.printStackTrace();
105 throw new SAXException("An error occoured: "+e.getMessage());
106 }
107 }
108
109 /**
110 * Upload a single node.
111 */
112 public void visit(Node n) {
113 if (n.id == 0 && !n.deleted && n.get("created_by") == null) {
114 n.put("created_by", "JOSM");
115 sendRequest("PUT", "node", n, true);
116 } else if (n.deleted) {
117 sendRequest("DELETE", "node", n, false);
118 } else {
119 sendRequest("PUT", "node", n, true);
120 }
121 processed.add(n);
122 }
123
124 /**
125 * Upload a whole way with the complete node id list.
126 */
127 public void visit(Way w) {
128 if (w.id == 0 && !w.deleted && w.get("created_by") == null) {
129 w.put("created_by", "JOSM");
130 sendRequest("PUT", "way", w, true);
131 } else if (w.deleted) {
132 sendRequest("DELETE", "way", w, false);
133 } else {
134 sendRequest("PUT", "way", w, true);
135 }
136 processed.add(w);
137 }
138
139 /**
140 * Upload an relation with all members.
141 */
142 public void visit(Relation e) {
143 if (e.id == 0 && !e.deleted && e.get("created_by") == null) {
144 e.put("created_by", "JOSM");
145 sendRequest("PUT", "relation", e, true);
146 } else if (e.deleted) {
147 sendRequest("DELETE", "relation", e, false);
148 } else {
149 sendRequest("PUT", "relation", e, true);
150 }
151 processed.add(e);
152 }
153 /**
154 * Read a long from the input stream and return it.
155 */
156 private long readId(InputStream inputStream) throws IOException {
157 BufferedReader in = new BufferedReader(new InputStreamReader(
158 inputStream));
159 String s = in.readLine();
160 if (s == null)
161 return 0;
162 try {
163 return Long.parseLong(s);
164 } catch (NumberFormatException e) {
165 return 0;
166 }
167 }
168
169 /**
170 * Send the request. The objects id will be replaced if it was 0 before
171 * (on add requests).
172 *
173 * @param requestMethod The http method used when talking with the server.
174 * @param urlSuffix The suffix to add at the server url.
175 * @param osm The primitive to encode to the server.
176 * @param addBody <code>true</code>, if the whole primitive body should be added.
177 * <code>false</code>, if only the id is encoded.
178 */
179 private void sendRequestRetry(String requestMethod, String urlSuffix,
180 OsmPrimitive osm, boolean addBody, int retries) {
181 try {
182 if (cancel)
183 return; // assume cancel
184 String version = Main.pref.get("osm-server.version", "0.5");
185 URL url = new URL(
186 Main.pref.get("osm-server.url") +
187 "/" + version +
188 "/" + urlSuffix +
189 "/" + (osm.id==0 ? "create" : osm.id));
190 System.out.print("upload to: "+url+ "..." );
191 activeConnection = (HttpURLConnection)url.openConnection();
192 activeConnection.setConnectTimeout(15000);
193 activeConnection.setRequestMethod(requestMethod);
194 if (addBody)
195 activeConnection.setDoOutput(true);
196 activeConnection.connect();
197 System.out.println("connected");
198 if (addBody) {
199 OutputStream out = activeConnection.getOutputStream();
200 OsmWriter.output(out, new OsmWriter.Single(osm, true));
201 out.close();
202 }
203
204 int retCode = activeConnection.getResponseCode();
205 if (retCode == 200 && osm.id == 0)
206 osm.id = readId(activeConnection.getInputStream());
207 System.out.println("got return: "+retCode+" with id "+osm.id);
208 String retMsg = activeConnection.getResponseMessage();
209 activeConnection.disconnect();
210 if (retCode == 410 && requestMethod.equals("DELETE"))
211 return; // everything fine.. was already deleted.
212 if (retCode != 200 && retCode != 412) {
213 if (retries >= 0) {
214 retries--;
215 System.out.print("backing off for 10 seconds...");
216 Thread.sleep(10000);
217 System.out.println("retrying ("+retries+" left)");
218 sendRequestRetry(requestMethod, urlSuffix, osm, addBody, retries);
219 } else {
220 // Look for a detailed error message from the server
221 if (activeConnection.getHeaderField("Error") != null)
222 retMsg += "\n" + activeConnection.getHeaderField("Error");
223
224 // Report our error
225 ByteArrayOutputStream o = new ByteArrayOutputStream();
226 OsmWriter.output(o, new OsmWriter.Single(osm, true));
227 System.out.println(new String(o.toByteArray(), "UTF-8").toString());
228 throw new RuntimeException(retCode+" "+retMsg);
229 }
230 }
231 } catch (UnknownHostException e) {
232 throw new RuntimeException(tr("Unknown host")+": "+e.getMessage(), e);
233 } catch(SocketTimeoutException e) {
234 System.out.println(" timed out, retries left: " + retries);
235 if (cancel)
236 return; // assume cancel
237 if (retries-- > 0)
238 sendRequestRetry(requestMethod, urlSuffix, osm, addBody, retries);
239 else
240 throw new RuntimeException(e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
241 } catch (Exception e) {
242 if (cancel)
243 return; // assume cancel
244 if (e instanceof RuntimeException)
245 throw (RuntimeException)e;
246 throw new RuntimeException(e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
247 }
248 }
249 private void sendRequest(String requestMethod, String urlSuffix,
250 OsmPrimitive osm, boolean addBody) {
251 sendRequestRetry(requestMethod, urlSuffix, osm, addBody, 10);
252 }
253}
Note: See TracBrowser for help on using the repository browser.