001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.actions; 003 004import static org.openstreetmap.josm.gui.help.HelpUtil.ht; 005import static org.openstreetmap.josm.tools.I18n.tr; 006 007import java.awt.event.ActionEvent; 008import java.awt.event.KeyEvent; 009import java.io.File; 010import java.io.IOException; 011import java.lang.management.ManagementFactory; 012import java.util.ArrayList; 013import java.util.Arrays; 014import java.util.Collections; 015import java.util.List; 016 017import org.openstreetmap.josm.Main; 018import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec; 019import org.openstreetmap.josm.tools.ImageProvider; 020import org.openstreetmap.josm.tools.PlatformHookWindows; 021import org.openstreetmap.josm.tools.Shortcut; 022 023/** 024 * Restarts JOSM as it was launched. Comes from "restart" plugin, originally written by Upliner. 025 * <br/><br/> 026 * Mechanisms have been improved based on #8561 discussions and <a href="http://lewisleo.blogspot.jp/2012/08/programmatically-restart-java.html">this article</a>. 027 * @since 5857 028 */ 029public class RestartAction extends JosmAction { 030 031 /** 032 * Constructs a new {@code RestartAction}. 033 */ 034 public RestartAction() { 035 super(tr("Restart"), "restart", tr("Restart the application."), 036 Shortcut.registerShortcut("file:restart", tr("File: {0}", tr("Restart")), KeyEvent.VK_J, Shortcut.ALT_CTRL_SHIFT), false); 037 putValue("help", ht("/Action/Restart")); 038 putValue("toolbar", "action/restart"); 039 Main.toolbar.register(this); 040 setEnabled(isRestartSupported()); 041 } 042 043 @Override 044 public void actionPerformed(ActionEvent e) { 045 // If JOSM has been started with property 'josm.restart=true' this means 046 // it is executed by a start script that can handle restart. 047 // Request for restart is indicated by exit code 9. 048 String scriptRestart = System.getProperty("josm.restart"); 049 if ("true".equals(scriptRestart)) { 050 Main.exitJosm(true, 9); 051 } 052 053 try { 054 restartJOSM(); 055 } catch (IOException ex) { 056 ex.printStackTrace(); 057 } 058 } 059 060 /** 061 * Determines if restarting the application should be possible on this platform. 062 * @return {@code true} if the mandatory system property {@code sun.java.command} is defined, {@code false} otherwise. 063 * @since 5951 064 */ 065 public static boolean isRestartSupported() { 066 return System.getProperty("sun.java.command") != null; 067 } 068 069 /** 070 * Restarts the current Java application 071 * @throws IOException 072 */ 073 public static void restartJOSM() throws IOException { 074 if (isRestartSupported() && !Main.exitJosm(false, 0)) return; 075 try { 076 // java binary 077 final String java = System.getProperty("java.home") + File.separator + "bin" + File.separator + 078 (Main.platform instanceof PlatformHookWindows ? "java.exe" : "java"); 079 if (!new File(java).isFile()) { 080 throw new IOException("Unable to find suitable java runtime at "+java); 081 } 082 final List<String> cmd = new ArrayList<String>(Collections.singleton(java)); 083 // vm arguments 084 for (String arg : ManagementFactory.getRuntimeMXBean().getInputArguments()) { 085 // if it's the agent argument : we ignore it otherwise the 086 // address of the old application and the new one will be in conflict 087 if (!arg.contains("-agentlib")) { 088 cmd.add(arg); 089 } 090 } 091 // program main and program arguments (be careful a sun property. might not be supported by all JVM) 092 String[] mainCommand = System.getProperty("sun.java.command").split(" "); 093 // look for a .jar in all chunks to support paths with spaces (fix #9077) 094 String jarPath = mainCommand[0]; 095 for (int i = 1; i < mainCommand.length && !jarPath.endsWith(".jar"); i++) { 096 jarPath += " " + mainCommand[i]; 097 } 098 // program main is a jar 099 if (jarPath.endsWith(".jar")) { 100 // if it's a jar, add -jar mainJar 101 cmd.add("-jar"); 102 cmd.add(new File(jarPath).getPath()); 103 } else { 104 // else it's a .class, add the classpath and mainClass 105 cmd.add("-cp"); 106 cmd.add("\"" + System.getProperty("java.class.path") + "\""); 107 cmd.add(mainCommand[0]); 108 } 109 // if it's webstart add JNLP file 110 String jnlp = System.getProperty("jnlp.application.href"); 111 if (jnlp != null) { 112 cmd.add(jnlp); 113 } 114 // finally add program arguments 115 cmd.addAll(Arrays.asList(Main.commandLineArgs)); 116 Main.info("Restart "+cmd); 117 // execute the command in a shutdown hook, to be sure that all the 118 // resources have been disposed before restarting the application 119 Runtime.getRuntime().addShutdownHook(new Thread() { 120 @Override 121 public void run() { 122 try { 123 Runtime.getRuntime().exec(cmd.toArray(new String[cmd.size()])); 124 } catch (IOException e) { 125 e.printStackTrace(); 126 } 127 } 128 }); 129 // exit 130 System.exit(0); 131 } catch (Exception e) { 132 // something went wrong 133 throw new IOException("Error while trying to restart the application", e); 134 } 135 } 136 137 /** 138 * Returns a new {@code ButtonSpec} instance that performs this action. 139 * @return A new {@code ButtonSpec} instance that performs this action. 140 */ 141 public static ButtonSpec getRestartButtonSpec() { 142 return new ButtonSpec( 143 tr("Restart"), 144 ImageProvider.get("restart"), 145 tr("Restart the application."), 146 ht("/Action/Restart"), 147 isRestartSupported() 148 ); 149 } 150 151 /** 152 * Returns a new {@code ButtonSpec} instance that do not perform this action. 153 * @return A new {@code ButtonSpec} instance that do not perform this action. 154 */ 155 public static ButtonSpec getCancelButtonSpec() { 156 return new ButtonSpec( 157 tr("Cancel"), 158 ImageProvider.get("cancel"), 159 tr("Click to restart later."), 160 null /* no specific help context */ 161 ); 162 } 163 164 /** 165 * Returns default {@code ButtonSpec} instances for this action (Restart/Cancel). 166 * @return Default {@code ButtonSpec} instances for this action. 167 * @see #getRestartButtonSpec 168 * @see #getCancelButtonSpec 169 */ 170 public static ButtonSpec[] getButtonSpecs() { 171 return new ButtonSpec[] { 172 getRestartButtonSpec(), 173 getCancelButtonSpec() 174 }; 175 } 176}