Ticket #7539: JosmLauncher.java

File JosmLauncher.java, 20.1 KB (added by akks, 14 years ago)
Line 
1package josmlaunch;
2
3import java.awt.Dimension;
4import java.awt.GridLayout;
5import java.awt.Toolkit;
6import java.awt.event.ActionEvent;
7import java.awt.event.ActionListener;
8import java.awt.event.WindowAdapter;
9import java.awt.event.WindowEvent;
10import java.beans.PropertyChangeEvent;
11import java.beans.PropertyChangeListener;
12import java.io.*;
13
14import java.net.URL;
15import java.net.URLConnection;
16import java.util.Properties;
17import java.util.jar.JarInputStream;
18import java.util.zip.ZipEntry;
19import javax.swing.*;
20
21
22class JosmLauncher extends JFrame implements PropertyChangeListener {
23
24 JButton start = new JButton("---");
25 JButton update = new JButton("---");
26 JTextField opts = new JTextField(50);
27 JCheckBox portable = new JCheckBox("Portable installation");
28 JProgressBar progress = new JProgressBar(0, 100);
29 JComboBox cbInterval = new JComboBox(new String[]{"7","14","30","3","1"});
30 JComboBox cbTested = new JComboBox(
31 new String[]{"josm-tested","josm-latest"});
32
33 Properties properties = new Properties();
34
35 int availableVersion;
36 int currentVersion;
37 boolean versionChecked = false;
38
39 String getJosmAddress() {
40 return String.format("http://josm.openstreetmap.de/download/josm-%s.jar",
41 cbTested.getSelectedIndex()==0? "tested" : "latest");
42 }
43
44 String getVersionCheckAddress() {
45 return String.format("http://josm.openstreetmap.de/%s",
46 cbTested.getSelectedIndex()==0? "tested" : "latest");
47 }
48
49 String workDir="";
50
51 File jarToRun;
52 File fileWithNewVersion;
53 File backupFile;
54 File iniFile;
55
56 DownloadTask dt = null;
57
58 private String updateText;
59 private String startText;
60
61 private boolean launchOnDownload;
62
63 private long lastUpdate;
64 private long updateInterval;
65
66 @Override
67 public void propertyChange(PropertyChangeEvent evt) {
68 if ("progress".equals(evt.getPropertyName())) {
69 int val = ( (Integer) evt.getNewValue() ).intValue();
70 progress.setValue(val);
71 if (dt!=null) {
72 progress.setString(
73 String.format("Downloading josm-latest.jar v%d: %d of %d Kb", availableVersion, dt.downloadedBytes/1024, dt.downloadSize/1024));
74 }
75 progress.setStringPainted(true);
76 }
77 }
78
79 public JosmLauncher() {
80
81 setupDirectories();
82 loadProperties();
83
84 setSize(350,200);
85 center();
86
87 start.addActionListener(new ActionListener() {
88 public void actionPerformed(ActionEvent e) {
89 launch();
90 }
91 });
92
93 update.addActionListener(new ActionListener() {
94 public void actionPerformed(ActionEvent e) {
95 updateJar();
96 }
97 });
98
99 GridLayout gb =new GridLayout(5,2);
100 setLayout(gb);
101 addWindowListener(new WindowAdapter() {
102 public void windowClosing(WindowEvent e) {
103 saveProperties();
104 System.exit(0);
105 }
106 } );
107
108 startText = "Start JOSM";
109
110 JPanel p = new JPanel();
111 p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
112 p.add(portable);
113 p.add(cbTested);
114 add(p);
115
116 cbTested.addActionListener(new ActionListener() {
117 @Override
118 public void actionPerformed(ActionEvent e) {
119 if (cbTested.getSelectedIndex()==1) {
120 JOptionPane.showMessageDialog(JosmLauncher.this,
121 "Josm-latest version (released daily) may have more errors than \"tested\", so be careful!", "Warning", JOptionPane.WARNING_MESSAGE);
122 }
123 setIntervalList();
124 checkFreshVersion(true);
125 }
126
127 });
128
129 p = new JPanel();
130 p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
131 p.add(new JLabel("Java VM arguments: "));
132 p.add(opts);
133 add(p);
134
135 p = new JPanel();
136 p.add(new JLabel("Automatic update interval(days): "));
137 p.add(cbInterval);
138 add(p);
139 // add(opts);
140 add(start);
141 add(update);
142
143 }
144
145
146 void updateJar() {
147 if (dt!=null) { // cancel download
148 dt.cancel(true);
149 setUIState(CHOOSE );
150 dt = null;
151 return;
152 }
153
154 if (checkFreshVersion(false)) {
155 if (launchOnDownload) launch();
156 return;
157 }
158
159 setVisible(true);
160 dt = new DownloadTask(getJosmAddress());
161 dt.addPropertyChangeListener(this);
162 dt.execute();
163 setUIState(DOWNLOADING);
164 }
165
166 void setIntervalList() {
167 String[] its;
168 if (cbTested.getSelectedIndex()==1) {
169 its = new String[]{"10", "7", "5", "3", "2", "1"};
170 } else {
171 its = new String[]{"30", "40", "50", "60"};
172 }
173 cbInterval.removeAllItems();
174 for (String s: its) cbInterval.addItem(s);
175 }
176
177 private boolean checkFreshVersion(boolean checkNow) {
178 setupDirectories();
179 if (!versionChecked || checkNow) {
180 GetVersionTask vt = new GetVersionTask(getVersionCheckAddress());
181 vt.execute();
182 try {
183 availableVersion = vt.get();
184 System.err.printf("Version %d found on server\n",availableVersion);
185 } catch (Exception ex) {
186 JOptionPane.showMessageDialog(this, "Sorry, can not get JOSM version. There is some connection problem", "Warning", JOptionPane.WARNING_MESSAGE);
187 availableVersion = 0;
188 }
189 updateText = "Update to version "+availableVersion;
190
191 readVersionFromJar();
192 versionChecked = true;
193 }
194
195
196 if (currentVersion >= availableVersion) {
197 //if (showMessage) {
198 // JOptionPane.showMessageDialog(this, "Your JOSM version "+currentVersion
199 // +" does not need updating", "", JOptionPane.INFORMATION_MESSAGE);
200 //}
201 setUIState(STARTONLY);
202 return true;
203 }
204
205 if (currentVersion > 0) {
206 setUIState(CHOOSE);
207 } else {
208 setUIState(DOWNLOADONLY);
209 }
210 return false;
211 }
212
213
214
215 String getProperty(String key, String def) {
216 return properties.getProperty(key, def);
217 }
218
219 void loadProperties() {
220 FileReader fr = null;
221 try {
222 fr = new FileReader(iniFile);
223 properties.load(fr);
224 } catch (FileNotFoundException ex) {
225 System.err.println("Properties file is not found, will be created");
226 } catch (IOException ex) {
227 System.err.println("Can not read properties file");
228 } finally {
229 try {
230 if (fr!=null) fr.close();
231 } catch (IOException ex) { }
232 }
233 cbTested.setSelectedIndex(getProperty("josm-tested", "true").equals("true")?0:1);
234 opts.setText(getProperty("options", "-Xmx1024m"));
235 portable.setSelected(getProperty("portable", "false").equals("true"));
236
237
238 lastUpdate = Long.parseLong(getProperty("lastUpdate", "0"));
239 String ui = getProperty("updateInterval", "7");
240 updateInterval = Long.parseLong(ui);
241
242 setIntervalList();
243 cbInterval.setSelectedItem(String.valueOf(updateInterval));
244 if (!ui.equals( cbInterval.getSelectedItem()) ) {
245 cbInterval.addItem(ui);
246 cbInterval.setSelectedItem(ui);
247 }
248
249
250 }
251
252 void saveProperties() {
253 properties.setProperty("options", opts.getText());
254 properties.setProperty("portable", String.valueOf(portable.isSelected()));
255 properties.setProperty("lastUpdate", String.valueOf(lastUpdate));
256 properties.setProperty("updateInterval", cbInterval.getSelectedItem().toString());
257 properties.setProperty("josm-tested", cbTested.getSelectedIndex()==0?"true":"false");
258
259 FileWriter fr = null;
260 try {
261 fr = new FileWriter(iniFile);
262 properties.store(fr,"");
263 } catch (IOException ex) {
264 System.err.println("Can not write properties file");
265 } finally {
266 try {
267 if (fr!=null) fr.close();
268 } catch (IOException ex) { }
269 }
270 }
271
272 void readVersionFromJar() {
273 currentVersion = 0;
274 FileInputStream fis = null;
275 JarInputStream jis = null;
276
277 if (jarToRun == null) return; // no JAR file - no version
278 if (!jarToRun.exists()) return;
279
280 try {
281 fis = new FileInputStream(jarToRun);
282 jis = new JarInputStream(fis);
283
284 String ver = null;
285 ZipEntry ze;
286 while ((ze = jis.getNextEntry()) != null) {
287 if ("REVISION".equals(ze.getName())) {
288 ver = readStringFromStream(jis);
289 break;
290 }
291 }
292 if (ver==null) {
293 System.err.println("Can not determine JAR version");
294 return;
295 }
296
297 int idx = ver.lastIndexOf("Revision: ");
298 int idx2 = ver.indexOf("\n", idx);
299
300 if (idx<0 || idx2<0) {
301 System.err.println("Can not parse REVISION file in JAR");
302 return;
303 }
304
305 ver = ver.substring(idx+10, idx2).trim();
306
307 try {
308 if (ver!=null) currentVersion = Integer.parseInt(ver);
309 } catch (NumberFormatException ex) { }
310
311 startText = "Start JOSM v"+currentVersion;
312 System.err.println("Jar version: "+currentVersion);
313 } catch (IOException ex) {
314 System.err.println("Can not read JAR file");
315 } finally {
316 try {
317 if (jis!=null) jis.close();
318 } catch (IOException ex) { System.err.println("can not close"); }
319 try {
320 if (fis!=null) fis.close();
321 } catch (IOException ex) { System.err.println("can not close"); }
322 }
323 }
324
325
326 String readStringFromStream(InputStream is) {
327 try {
328 ByteArrayOutputStream out = new ByteArrayOutputStream(5000);
329 byte[] buffer = new byte[32768];
330 for (int read = is.read(buffer); read != -1; read = is.read(buffer)) {
331 out.write(buffer, 0, read);
332 }
333 out.close();
334 return out.toString();
335 } catch (IOException ex) {
336 System.err.println("Can not read string from stream");
337 return null;
338 }
339 }
340
341 private boolean checkAlreadyDownloaded() {
342 if (fileWithNewVersion.exists()) {
343 return renameDownloadedFiles();
344 }
345 return false;
346 }
347
348 private void setupDirectories() {
349 String workDir;
350 if (portable.isSelected()) {
351 workDir = "updates";
352 } else {
353 // Windows 7, non-admin installation
354 String path = System.getenv("APPDATA");
355 if (path != null) {
356 workDir = new File(path, "JOSM/updates").getAbsolutePath();
357 } else {
358 workDir = new File(System.getProperty("user.home"), ".josm/updates").getAbsolutePath();
359 }
360 }
361
362 String name = "josm-"+ (cbTested.getSelectedIndex()==0? "tested" : "latest");
363 fileWithNewVersion = new File(workDir, name+".new");
364 fileWithNewVersion.getParentFile().mkdirs();
365 jarToRun = new File(workDir, name+".jar");
366 backupFile = new File(workDir, name+".old");
367 iniFile = new File(workDir, "launcher.ini");
368 }
369
370 void setLaunchOnDownload(boolean b) {
371 launchOnDownload = b;
372 }
373
374 private void downloadIfNeededAndLaunch() {
375 if (System.currentTimeMillis() - lastUpdate > updateInterval*24*3600 ) {
376 launchOnDownload = true;
377 updateJar();
378 } else {
379 launch();
380 }
381 }
382
383
384 class GetVersionTask extends SwingWorker<Integer, Void> {
385 URLConnection downloadConnection;
386 String address;
387
388 int downloadSize;
389 int downloadedBytes;
390
391 public GetVersionTask(String address) {
392 this.address = address;
393 }
394
395 @Override
396 protected Integer doInBackground() throws Exception {
397 try {
398 InputStream in;
399 URL url = new URL(address);
400 downloadConnection = url.openConnection();
401 downloadConnection.setRequestProperty("Cache-Control", "no-cache");
402 downloadConnection.setRequestProperty("Host", url.getHost());
403 downloadConnection.connect();
404 downloadSize = downloadConnection.getContentLength();
405 in = downloadConnection.getInputStream();
406
407 String s = readStringFromStream(in);
408 in.close();
409 return Integer.parseInt(s);
410 } catch(IOException e) {
411 e.printStackTrace();
412 }
413 return null;
414 }
415
416 }
417
418 class DownloadTask extends SwingWorker<Void, Void> {
419
420 URLConnection downloadConnection;
421 String address;
422
423 int downloadSize;
424 int downloadedBytes;
425 private boolean succesful;
426
427
428 public DownloadTask(String address) {
429 this.address = address;
430 }
431
432 @Override
433 protected Void doInBackground() throws Exception {
434 OutputStream out = null;
435 InputStream in = null;
436 succesful = false;
437
438 try {
439
440 URL url = new URL(address);
441 downloadConnection = url.openConnection();
442 downloadConnection.setRequestProperty("Cache-Control", "no-cache");
443 downloadConnection.setRequestProperty("Host", url.getHost());
444 downloadConnection.connect();
445 downloadSize = downloadConnection.getContentLength();
446 if (downloadSize<3000000) {
447 System.err.println("Suspicious JOSM jar size, no download");
448 return null;
449 }
450
451 in = downloadConnection.getInputStream();
452 out = new FileOutputStream(fileWithNewVersion);
453 byte[] buffer = new byte[32768];
454 downloadedBytes=0;
455 int p1=0, p2=0;
456 for (int read = in.read(buffer); read != -1; read = in.read(buffer)) {
457 out.write(buffer, 0, read);
458 downloadedBytes+=read;
459 if (isCancelled()) {
460 System.err.println("Download cancelled");
461 return null;
462 }
463
464 p2 = 100 * downloadedBytes / downloadSize;
465 if (p2!=p1) {
466 setProgress(p2);
467 p1=p2;
468 }
469 }
470 } catch(Exception e) {
471 //if (canceled) return;
472 e.printStackTrace();
473 return null;
474 } finally {
475 if (out!=null) try {
476 out.close();
477 } catch (IOException ex) { }
478 }
479
480 succesful = renameDownloadedFiles();
481 return null;
482 }
483
484 @Override
485 protected void done() {
486 setUIState(dt.succesful ? STARTONLY : CHOOSE );
487 dt = null;
488 if (succesful) {
489 lastUpdate = System.currentTimeMillis();
490 }
491 if (launchOnDownload && succesful) {
492 launch();
493 }
494 }
495 }
496
497 boolean renameDownloadedFiles() {
498 backupFile.delete();
499 if (jarToRun.exists()) {
500 if (!jarToRun.renameTo(backupFile)) {
501 System.err.println("error renaming");
502 return false;
503 }
504 }
505 if (fileWithNewVersion.exists()) {
506 jarToRun.delete();
507 if (!fileWithNewVersion.renameTo(jarToRun)) {
508 System.err.println("error renaming .new -> .jar");
509 return false;
510 }
511 fileWithNewVersion.delete();
512 }
513 return true;
514 }
515
516 void launch() {
517 saveProperties();
518 try {
519 String addOpts = "";
520 if (portable.isSelected()) {
521 addOpts = String.format("-Djosm.home=\"%s\"", new File(workDir,"data").getAbsolutePath());
522 }
523 String cmd = String.format("java %s %s -jar \"%s\"", opts.getText(), addOpts, jarToRun.getAbsolutePath());
524 Process p = Runtime.getRuntime().exec(cmd);
525 Thread.sleep(2000);
526 if (p.exitValue()!=0) {
527 System.err.println("Error executing!!!");
528 jarToRun.delete();
529 }
530 } catch (Exception ex) {
531 System.exit(1);
532 }
533
534 System.exit(0);
535 }
536
537 void center() {
538 // Get the size of the screen
539 Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
540
541 // Determine the new location of the window
542 int w = getSize().width;
543 int h = getSize().height;
544 int x = (dim.width-w)/2;
545 int y = (dim.height-h)/2;
546
547 // Move the window
548 setLocation(x, y);
549 }
550
551
552 static final int CHOOSE=0;
553 static final int DOWNLOADING=1;
554 static final int DOWNLOADONLY=2;
555 static final int STARTONLY=3;
556 int oldState = 0;
557
558 void setUIState(int state) {
559 if (oldState!=state && oldState == DOWNLOADING) {
560 remove(progress);
561 add(start, 3);
562 update.setText(updateText);
563 validate(); repaint();
564 }
565 oldState = state;
566
567 switch (state) {
568 case CHOOSE:
569 updateText = "Update to version "+availableVersion;
570 start.setVisible(true);
571 update.setVisible(true);
572 break;
573 case STARTONLY:
574 startText = "Start JOSM";
575 start.setVisible(true);
576 update.setVisible(false);
577 break;
578 case DOWNLOADONLY:
579 updateText = "Download JOSM version "+availableVersion;
580 update.setVisible(true);
581 start.setVisible(false);
582 break;
583 case DOWNLOADING:
584 remove(start);
585 add(progress,3);
586 updateText = "Cancel download";
587 validate(); repaint();
588 }
589
590 start.setText(startText);
591 update.setText(updateText);
592 }
593
594 public static void main(String[] args) {
595 System.setProperty("java.net.useSystemProxies", "true");
596
597 JosmLauncher launcher = new JosmLauncher();
598 if (args.length>0) {
599 if ("-c".equals(args[0])) {
600 // Config option
601 launcher.checkFreshVersion(true);
602 launcher.setVisible(true);
603 return;
604 }
605
606 if ("-r".equals(args[0])) {
607 // launch Josm
608 launcher.launch();
609 return;
610 }
611
612 if ("-u".equals(args[0])) {
613 launcher.setLaunchOnDownload(true);
614 launcher.updateJar();
615 }
616 } else {
617 if (launcher.checkAlreadyDownloaded()) {
618 launcher.launch();
619 } else {
620 launcher.downloadIfNeededAndLaunch();
621 }
622 }
623 }
624}