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

Last change on this file since 11348 was 11288, checked in by simon04, 7 years ago

see #13376 - Use TimeUnit instead of combinations of 1000/60/60/24

  • Property svn:eol-style set to native
File size: 10.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io;
3
4import static org.openstreetmap.josm.tools.I18n.marktr;
5import static org.openstreetmap.josm.tools.I18n.tr;
6import static org.openstreetmap.josm.tools.I18n.trn;
7
8import java.util.ArrayList;
9import java.util.Collection;
10import java.util.Iterator;
11import java.util.LinkedList;
12import java.util.List;
13import java.util.concurrent.TimeUnit;
14
15import org.openstreetmap.josm.data.osm.Changeset;
16import org.openstreetmap.josm.data.osm.OsmPrimitive;
17import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
18import org.openstreetmap.josm.gui.JosmUserIdentityManager;
19import org.openstreetmap.josm.gui.io.UploadStrategySpecification;
20import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
21import org.openstreetmap.josm.gui.progress.ProgressMonitor;
22import org.openstreetmap.josm.tools.CheckParameterUtil;
23
24/**
25 * Class that uploads all changes to the osm server.
26 *
27 * This is done like this: - All objects with id = 0 are uploaded as new, except
28 * those in deleted, which are ignored - All objects in deleted list are
29 * deleted. - All remaining objects with modified flag set are updated.
30 */
31public class OsmServerWriter {
32 /**
33 * This list contains all successfully processed objects. The caller of
34 * upload* has to check this after the call and update its dataset.
35 *
36 * If a server connection error occurs, this may contain fewer entries
37 * than where passed in the list to upload*.
38 */
39 private Collection<OsmPrimitive> processed;
40
41 private static volatile List<OsmServerWritePostprocessor> postprocessors;
42
43 /**
44 * Registers a post-processor.
45 * @param pp post-processor to register
46 */
47 public static void registerPostprocessor(OsmServerWritePostprocessor pp) {
48 if (postprocessors == null) {
49 postprocessors = new ArrayList<>();
50 }
51 postprocessors.add(pp);
52 }
53
54 /**
55 * Unregisters a post-processor.
56 * @param pp post-processor to unregister
57 */
58 public static void unregisterPostprocessor(OsmServerWritePostprocessor pp) {
59 if (postprocessors != null) {
60 postprocessors.remove(pp);
61 }
62 }
63
64 private final OsmApi api = OsmApi.getOsmApi();
65 private boolean canceled;
66
67 private long uploadStartTime;
68
69 protected String timeLeft(int progress, int listSize) {
70 long now = System.currentTimeMillis();
71 long elapsed = now - uploadStartTime;
72 if (elapsed == 0) {
73 elapsed = 1;
74 }
75 double uploadsPerMs = (double) progress / elapsed;
76 double uploadsLeft = (double) listSize - progress;
77 long msLeft = (long) (uploadsLeft / uploadsPerMs);
78 long minutesLeft = msLeft / TimeUnit.MINUTES.toMillis(1);
79 long secondsLeft = (msLeft / TimeUnit.SECONDS.toMillis(1)) % TimeUnit.MINUTES.toSeconds(1);
80 StringBuilder timeLeftStr = new StringBuilder().append(minutesLeft).append(':');
81 if (secondsLeft < 10) {
82 timeLeftStr.append('0');
83 }
84 return timeLeftStr.append(secondsLeft).toString();
85 }
86
87 /**
88 * Uploads the changes individually. Invokes one API call per uploaded primitmive.
89 *
90 * @param primitives the collection of primitives to upload
91 * @param progressMonitor the progress monitor
92 * @throws OsmTransferException if an exception occurs
93 */
94 protected void uploadChangesIndividually(Collection<? extends OsmPrimitive> primitives, ProgressMonitor progressMonitor)
95 throws OsmTransferException {
96 try {
97 progressMonitor.beginTask(tr("Starting to upload with one request per primitive ..."));
98 progressMonitor.setTicksCount(primitives.size());
99 uploadStartTime = System.currentTimeMillis();
100 for (OsmPrimitive osm : primitives) {
101 String msg;
102 switch(OsmPrimitiveType.from(osm)) {
103 case NODE: msg = marktr("{0}% ({1}/{2}), {3} left. Uploading node ''{4}'' (id: {5})"); break;
104 case WAY: msg = marktr("{0}% ({1}/{2}), {3} left. Uploading way ''{4}'' (id: {5})"); break;
105 case RELATION: msg = marktr("{0}% ({1}/{2}), {3} left. Uploading relation ''{4}'' (id: {5})"); break;
106 default: throw new AssertionError();
107 }
108 int progress = progressMonitor.getTicks();
109 progressMonitor.subTask(
110 tr(msg,
111 Math.round(100.0*progress/primitives.size()),
112 progress,
113 primitives.size(),
114 timeLeft(progress, primitives.size()),
115 osm.getName() == null ? osm.getId() : osm.getName(), osm.getId()));
116 makeApiRequest(osm, progressMonitor);
117 processed.add(osm);
118 progressMonitor.worked(1);
119 }
120 } catch (OsmTransferException e) {
121 throw e;
122 } catch (Exception e) {
123 throw new OsmTransferException(e);
124 } finally {
125 progressMonitor.finishTask();
126 }
127 }
128
129 /**
130 * Upload all changes in one diff upload
131 *
132 * @param primitives the collection of primitives to upload
133 * @param progressMonitor the progress monitor
134 * @throws OsmTransferException if an exception occurs
135 */
136 protected void uploadChangesAsDiffUpload(Collection<? extends OsmPrimitive> primitives, ProgressMonitor progressMonitor)
137 throws OsmTransferException {
138 try {
139 progressMonitor.beginTask(tr("Starting to upload in one request ..."));
140 processed.addAll(api.uploadDiff(primitives, progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)));
141 } finally {
142 progressMonitor.finishTask();
143 }
144 }
145
146 /**
147 * Upload all changes in one diff upload
148 *
149 * @param primitives the collection of primitives to upload
150 * @param progressMonitor the progress monitor
151 * @param chunkSize the size of the individual upload chunks. &gt; 0 required.
152 * @throws IllegalArgumentException if chunkSize &lt;= 0
153 * @throws OsmTransferException if an exception occurs
154 */
155 protected void uploadChangesInChunks(Collection<? extends OsmPrimitive> primitives, ProgressMonitor progressMonitor, int chunkSize)
156 throws OsmTransferException {
157 if (chunkSize <= 0)
158 throw new IllegalArgumentException(tr("Value >0 expected for parameter ''{0}'', got {1}", "chunkSize", chunkSize));
159 try {
160 progressMonitor.beginTask(tr("Starting to upload in chunks..."));
161 List<OsmPrimitive> chunk = new ArrayList<>(chunkSize);
162 Iterator<? extends OsmPrimitive> it = primitives.iterator();
163 int numChunks = (int) Math.ceil((double) primitives.size() / (double) chunkSize);
164 int i = 0;
165 while (it.hasNext()) {
166 i++;
167 if (canceled) return;
168 int j = 0;
169 chunk.clear();
170 while (it.hasNext() && j < chunkSize) {
171 if (canceled) return;
172 j++;
173 chunk.add(it.next());
174 }
175 progressMonitor.setCustomText(
176 trn("({0}/{1}) Uploading {2} object...",
177 "({0}/{1}) Uploading {2} objects...",
178 chunk.size(), i, numChunks, chunk.size()));
179 processed.addAll(api.uploadDiff(chunk, progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)));
180 }
181 } finally {
182 progressMonitor.finishTask();
183 }
184 }
185
186 /**
187 * Send the dataset to the server.
188 *
189 * @param strategy the upload strategy. Must not be null.
190 * @param primitives list of objects to send
191 * @param changeset the changeset the data is uploaded to. Must not be null.
192 * @param monitor the progress monitor. If null, assumes {@link NullProgressMonitor#INSTANCE}
193 * @throws IllegalArgumentException if changeset is null
194 * @throws IllegalArgumentException if strategy is null
195 * @throws OsmTransferException if something goes wrong
196 */
197 public void uploadOsm(UploadStrategySpecification strategy, Collection<? extends OsmPrimitive> primitives,
198 Changeset changeset, ProgressMonitor monitor) throws OsmTransferException {
199 CheckParameterUtil.ensureParameterNotNull(changeset, "changeset");
200 processed = new LinkedList<>();
201 monitor = monitor == null ? NullProgressMonitor.INSTANCE : monitor;
202 monitor.beginTask(tr("Uploading data ..."));
203 try {
204 api.initialize(monitor);
205 // check whether we can use diff upload
206 if (changeset.getId() == 0) {
207 api.openChangeset(changeset, monitor.createSubTaskMonitor(0, false));
208 // update the user information
209 changeset.setUser(JosmUserIdentityManager.getInstance().asUser());
210 } else {
211 api.updateChangeset(changeset, monitor.createSubTaskMonitor(0, false));
212 }
213 api.setChangeset(changeset);
214 switch(strategy.getStrategy()) {
215 case SINGLE_REQUEST_STRATEGY:
216 uploadChangesAsDiffUpload(primitives, monitor.createSubTaskMonitor(0, false));
217 break;
218 case INDIVIDUAL_OBJECTS_STRATEGY:
219 uploadChangesIndividually(primitives, monitor.createSubTaskMonitor(0, false));
220 break;
221 case CHUNKED_DATASET_STRATEGY:
222 default:
223 uploadChangesInChunks(primitives, monitor.createSubTaskMonitor(0, false), strategy.getChunkSize());
224 break;
225 }
226 } finally {
227 executePostprocessors(monitor);
228 monitor.finishTask();
229 api.setChangeset(null);
230 }
231 }
232
233 void makeApiRequest(OsmPrimitive osm, ProgressMonitor progressMonitor) throws OsmTransferException {
234 if (osm.isDeleted()) {
235 api.deletePrimitive(osm, progressMonitor);
236 } else if (osm.isNew()) {
237 api.createPrimitive(osm, progressMonitor);
238 } else {
239 api.modifyPrimitive(osm, progressMonitor);
240 }
241 }
242
243 /**
244 * Cancel operation.
245 */
246 public void cancel() {
247 this.canceled = true;
248 if (api != null) {
249 api.cancel();
250 }
251 }
252
253 /**
254 * Replies the collection of successfully processed primitives
255 *
256 * @return the collection of successfully processed primitives
257 */
258 public Collection<OsmPrimitive> getProcessedPrimitives() {
259 return processed;
260 }
261
262 /**
263 * Calls all registered upload postprocessors.
264 * @param pm progress monitor
265 */
266 public void executePostprocessors(ProgressMonitor pm) {
267 if (postprocessors != null) {
268 for (OsmServerWritePostprocessor pp : postprocessors) {
269 pp.postprocessUploadedPrimitives(processed, pm);
270 }
271 }
272 }
273}
Note: See TracBrowser for help on using the repository browser.