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

Last change on this file since 12894 was 12846, checked in by bastiK, 7 years ago

see #15229 - use Config.getPref() wherever possible

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