source: josm/trunk/src/org/openstreetmap/josm/actions/RestartAction.java@ 11124

Last change on this file since 11124 was 11093, checked in by simon04, 8 years ago

fix #13320 - Use restart text and icon in unsaved changes dialog

  • Property svn:eol-style set to native
File size: 9.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.actions;
3
4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.event.ActionEvent;
8import java.awt.event.KeyEvent;
9import java.io.File;
10import java.io.IOException;
11import java.lang.management.ManagementFactory;
12import java.util.ArrayList;
13import java.util.Arrays;
14import java.util.Collection;
15import java.util.List;
16
17import org.openstreetmap.josm.Main;
18import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
19import org.openstreetmap.josm.gui.io.SaveLayersDialog;
20import org.openstreetmap.josm.tools.ImageProvider;
21import org.openstreetmap.josm.tools.Shortcut;
22
23/**
24 * Restarts JOSM as it was launched. Comes from "restart" plugin, originally written by Upliner.
25 * <br><br>
26 * Mechanisms have been improved based on #8561 discussions and
27 * <a href="http://lewisleo.blogspot.jp/2012/08/programmatically-restart-java.html">this article</a>.
28 * @since 5857
29 */
30public class RestartAction extends JosmAction {
31
32 // AppleScript to restart OS X package
33 private static final String RESTART_APPLE_SCRIPT =
34 "tell application \"System Events\"\n"
35 + "repeat until not (exists process \"JOSM\")\n"
36 + "delay 0.2\n"
37 + "end repeat\n"
38 + "end tell\n"
39 + "tell application \"JOSM\" to activate";
40
41 /**
42 * Constructs a new {@code RestartAction}.
43 */
44 public RestartAction() {
45 super(tr("Restart"), "restart", tr("Restart the application."),
46 Shortcut.registerShortcut("file:restart", tr("File: {0}", tr("Restart")), KeyEvent.VK_J, Shortcut.ALT_CTRL_SHIFT), false);
47 putValue("help", ht("/Action/Restart"));
48 putValue("toolbar", "action/restart");
49 if (Main.toolbar != null) {
50 Main.toolbar.register(this);
51 }
52 setEnabled(isRestartSupported());
53 }
54
55 @Override
56 public void actionPerformed(ActionEvent e) {
57 // If JOSM has been started with property 'josm.restart=true' this means
58 // it is executed by a start script that can handle restart.
59 // Request for restart is indicated by exit code 9.
60 String scriptRestart = System.getProperty("josm.restart");
61 if ("true".equals(scriptRestart)) {
62 Main.exitJosm(true, 9, SaveLayersDialog.Reason.RESTART);
63 }
64
65 try {
66 restartJOSM();
67 } catch (IOException ex) {
68 Main.error(ex);
69 }
70 }
71
72 /**
73 * Determines if restarting the application should be possible on this platform.
74 * @return {@code true} if the mandatory system property {@code sun.java.command} is defined, {@code false} otherwise.
75 * @since 5951
76 */
77 public static boolean isRestartSupported() {
78 return System.getProperty("sun.java.command") != null;
79 }
80
81 /**
82 * Restarts the current Java application.
83 * @throws IOException in case of any I/O error
84 */
85 public static void restartJOSM() throws IOException {
86 if (isRestartSupported() && !Main.exitJosm(false, 0, SaveLayersDialog.Reason.RESTART)) return;
87 final List<String> cmd;
88 // special handling for OSX .app package
89 if (Main.isPlatformOsx() && System.getProperty("java.library.path").contains("/JOSM.app/Contents/MacOS")) {
90 cmd = getAppleCommands();
91 } else {
92 cmd = getCommands();
93 }
94 Main.info("Restart "+cmd);
95 if (Main.isDebugEnabled() && Main.pref.getBoolean("restart.debug.simulation")) {
96 Main.debug("Restart cancelled to get debug info");
97 return;
98 }
99 // execute the command in a shutdown hook, to be sure that all the
100 // resources have been disposed before restarting the application
101 Runtime.getRuntime().addShutdownHook(new Thread("josm-restarter") {
102 @Override
103 public void run() {
104 try {
105 Runtime.getRuntime().exec(cmd.toArray(new String[cmd.size()]));
106 } catch (IOException e) {
107 Main.error(e);
108 }
109 }
110 });
111 // exit
112 System.exit(0);
113 }
114
115 private static List<String> getAppleCommands() {
116 final List<String> cmd = new ArrayList<>();
117 cmd.add("/usr/bin/osascript");
118 for (String line : RESTART_APPLE_SCRIPT.split("\n")) {
119 cmd.add("-e");
120 cmd.add(line);
121 }
122 return cmd;
123 }
124
125 private static List<String> getCommands() throws IOException {
126 final List<String> cmd = new ArrayList<>();
127 // java binary
128 cmd.add(getJavaRuntime());
129 // vm arguments
130 addVMArguments(cmd);
131 // Determine webstart JNLP file. Use jnlpx.origFilenameArg instead of jnlp.application.href,
132 // because only this one is present when run from j2plauncher.exe (see #10795)
133 final String jnlp = System.getProperty("jnlpx.origFilenameArg");
134 // program main and program arguments (be careful a sun property. might not be supported by all JVM)
135 final String javaCommand = System.getProperty("sun.java.command");
136 String[] mainCommand = javaCommand.split(" ");
137 if (javaCommand.endsWith(".jnlp") && jnlp == null) {
138 // see #11751 - jnlp on Linux
139 if (Main.isDebugEnabled()) {
140 Main.debug("Detected jnlp without jnlpx.origFilenameArg property set");
141 }
142 cmd.addAll(Arrays.asList(mainCommand));
143 } else {
144 // look for a .jar in all chunks to support paths with spaces (fix #9077)
145 StringBuilder sb = new StringBuilder(mainCommand[0]);
146 for (int i = 1; i < mainCommand.length && !mainCommand[i-1].endsWith(".jar"); i++) {
147 sb.append(' ').append(mainCommand[i]);
148 }
149 String jarPath = sb.toString();
150 // program main is a jar
151 if (jarPath.endsWith(".jar")) {
152 // if it's a jar, add -jar mainJar
153 cmd.add("-jar");
154 cmd.add(new File(jarPath).getPath());
155 } else {
156 // else it's a .class, add the classpath and mainClass
157 cmd.add("-cp");
158 cmd.add('"' + System.getProperty("java.class.path") + '"');
159 cmd.add(mainCommand[0]);
160 }
161 // add JNLP file.
162 if (jnlp != null) {
163 cmd.add(jnlp);
164 }
165 }
166 // finally add program arguments
167 cmd.addAll(Main.getCommandLineArgs());
168 return cmd;
169 }
170
171 private static String getJavaRuntime() throws IOException {
172 final String java = System.getProperty("java.home") + File.separator + "bin" + File.separator +
173 (Main.isPlatformWindows() ? "java.exe" : "java");
174 if (!new File(java).isFile()) {
175 throw new IOException("Unable to find suitable java runtime at "+java);
176 }
177 return java;
178 }
179
180 private static void addVMArguments(Collection<String> cmd) {
181 List<String> arguments = ManagementFactory.getRuntimeMXBean().getInputArguments();
182 if (Main.isDebugEnabled()) {
183 Main.debug("VM arguments: "+arguments);
184 }
185 for (String arg : arguments) {
186 // When run from jp2launcher.exe, jnlpx.remove is true, while it is not when run from javaws
187 // Always set it to false to avoid error caused by a missing jnlp file on the second restart
188 arg = arg.replace("-Djnlpx.remove=true", "-Djnlpx.remove=false");
189 // if it's the agent argument : we ignore it otherwise the
190 // address of the old application and the new one will be in conflict
191 if (!arg.contains("-agentlib")) {
192 cmd.add(arg);
193 }
194 }
195 }
196
197 /**
198 * Returns a new {@code ButtonSpec} instance that performs this action.
199 * @return A new {@code ButtonSpec} instance that performs this action.
200 */
201 public static ButtonSpec getRestartButtonSpec() {
202 return new ButtonSpec(
203 tr("Restart"),
204 ImageProvider.get("restart"),
205 tr("Restart the application."),
206 ht("/Action/Restart"),
207 isRestartSupported()
208 );
209 }
210
211 /**
212 * Returns a new {@code ButtonSpec} instance that do not perform this action.
213 * @return A new {@code ButtonSpec} instance that do not perform this action.
214 */
215 public static ButtonSpec getCancelButtonSpec() {
216 return new ButtonSpec(
217 tr("Cancel"),
218 ImageProvider.get("cancel"),
219 tr("Click to restart later."),
220 null /* no specific help context */
221 );
222 }
223
224 /**
225 * Returns default {@code ButtonSpec} instances for this action (Restart/Cancel).
226 * @return Default {@code ButtonSpec} instances for this action.
227 * @see #getRestartButtonSpec
228 * @see #getCancelButtonSpec
229 */
230 public static ButtonSpec[] getButtonSpecs() {
231 return new ButtonSpec[] {
232 getRestartButtonSpec(),
233 getCancelButtonSpec()
234 };
235 }
236}
Note: See TracBrowser for help on using the repository browser.