2 * Copyright 2000-2014 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.idea;
18 import com.intellij.openapi.application.ApplicationInfo;
19 import com.intellij.openapi.application.ApplicationNamesInfo;
20 import com.intellij.openapi.application.ConfigImportHelper;
21 import com.intellij.openapi.application.PathManager;
22 import com.intellij.openapi.application.impl.ApplicationInfoImpl;
23 import com.intellij.openapi.diagnostic.Logger;
24 import com.intellij.openapi.util.SystemInfo;
25 import com.intellij.openapi.util.SystemInfoRt;
26 import com.intellij.openapi.util.io.FileUtil;
27 import com.intellij.openapi.util.io.FileUtilRt;
28 import com.intellij.openapi.util.io.win32.IdeaWin32;
29 import com.intellij.openapi.util.text.StringUtil;
30 import com.intellij.ui.AppUIUtil;
31 import com.intellij.util.Consumer;
32 import com.intellij.util.EnvironmentUtil;
33 import com.intellij.util.lang.UrlClassLoader;
34 import com.sun.jna.Native;
35 import org.jetbrains.annotations.NonNls;
39 import java.lang.management.ManagementFactory;
40 import java.text.SimpleDateFormat;
46 public class StartupUtil {
47 @NonNls public static final String NO_SPLASH = "nosplash";
49 private static SocketLock ourLock;
50 private static String myDefaultLAF;
51 private static String myWizardLAF;
52 private static String myWizardMacKeymap;
53 private static Set<String> myFeaturedPluginsToInstall = new HashSet<String>();
55 private StartupUtil() { }
57 public static void setDefaultLAF(String laf) {
61 public static String getDefaultLAF() {
65 public static void setWizardLAF(String myWizardLAF) {
66 StartupUtil.myWizardLAF = myWizardLAF;
69 public static String getWizardLAF() {
73 public static void setMyWizardMacKeymap(String myWizardMacKeymap) {
74 StartupUtil.myWizardMacKeymap = myWizardMacKeymap;
77 public static String getMyWizardMacKeymap() {
78 return myWizardMacKeymap;
81 public static Set<String> getMyFeaturedPluginsToInstall() {
82 return Collections.unmodifiableSet(myFeaturedPluginsToInstall);
85 public static void setFeaturedPluginsToInstall(Set<String> pluginsToInstall) {
86 myFeaturedPluginsToInstall.clear();
87 myFeaturedPluginsToInstall.addAll(pluginsToInstall);
90 public static boolean shouldShowSplash(final String[] args) {
91 return !Arrays.asList(args).contains(NO_SPLASH);
94 /** @deprecated use {@link Main#isHeadless()} (to remove in IDEA 14) */
95 @SuppressWarnings("unused")
96 public static boolean isHeadless() {
97 return Main.isHeadless();
100 public synchronized static void addExternalInstanceListener(Consumer<List<String>> consumer) {
101 ourLock.setActivateListener(consumer);
104 interface AppStarter {
105 void start(boolean newConfigFolder);
108 public synchronized static int getAcquiredPort() {
109 return ourLock.getAcquiredPort();
112 static void prepareAndStart(String[] args, AppStarter appStarter) {
113 boolean newConfigFolder = false;
115 if (!Main.isHeadless()) {
116 AppUIUtil.updateFrameClass();
117 newConfigFolder = !new File(PathManager.getConfigPath()).exists();
120 boolean canStart = checkJdkVersion() && checkSystemFolders() && lockSystemFolders(args); // note: uses config folder!
122 System.exit(Main.STARTUP_IMPOSSIBLE);
125 if (newConfigFolder) {
126 ConfigImportHelper.importConfigsTo(PathManager.getConfigPath());
129 Logger.setFactory(LoggerFactory.class);
130 Logger log = Logger.getInstance(Main.class);
132 loadSystemLibraries(log);
133 fixProcessEnvironment(log);
135 if (!Main.isHeadless()) {
136 AppUIUtil.updateWindowIcon(JOptionPane.getRootFrame());
137 AppUIUtil.registerBundledFonts();
140 appStarter.start(newConfigFolder);
144 * Checks if the program can run under the JDK it was started with.
146 private static boolean checkJdkVersion() {
147 if (!"true".equals(System.getProperty("idea.no.jre.check"))) {
149 // try to find a class from tools.jar
150 Class.forName("com.sun.jdi.Field");
152 catch (ClassNotFoundException e) {
153 String message = "'tools.jar' seems to be not in " + ApplicationNamesInfo.getInstance().getProductName() + " classpath.\n" +
154 "Please ensure JAVA_HOME points to JDK rather than JRE.";
155 Main.showMessage("JDK Required", message, true);
159 if (StringUtil.containsIgnoreCase(System.getProperty("java.vm.name", ""), "OpenJDK") && !SystemInfo.isJavaVersionAtLeast("1.7")) {
160 String message = "OpenJDK 6 is not supported. Please use Oracle Java or newer OpenJDK.";
161 Main.showMessage("Unsupported JVM", message, true);
169 private synchronized static boolean checkSystemFolders() {
170 String configPath = PathManager.getConfigPath();
171 PathManager.ensureConfigFolderExists();
172 if (!new File(configPath).isDirectory()) {
173 String message = "Config path '" + configPath + "' is invalid.\n" +
174 "If you have modified the '" + PathManager.PROPERTY_CONFIG_PATH + "' property please make sure it is correct,\n" +
175 "otherwise please re-install the IDE.";
176 Main.showMessage("Invalid Config Path", message, true);
180 String systemPath = PathManager.getSystemPath();
181 if (!new File(systemPath).isDirectory()) {
182 String message = "System path '" + systemPath + "' is invalid.\n" +
183 "If you have modified the '" + PathManager.PROPERTY_SYSTEM_PATH + "' property please make sure it is correct,\n" +
184 "otherwise please re-install the IDE.";
185 Main.showMessage("Invalid System Path", message, true);
189 File ideTempDir = new File(PathManager.getTempPath());
190 String tempInaccessible = null;
192 if (!ideTempDir.isDirectory() && !ideTempDir.mkdirs()) {
193 tempInaccessible = "unable to create the directory";
197 File ideTempFile = new File(ideTempDir, "idea_tmp_check.sh");
198 FileUtil.writeToFile(ideTempFile, "#!/bin/sh\nexit 0");
200 if (SystemInfo.isWindows || SystemInfo.isMac) {
201 tempInaccessible = null;
203 else if (!ideTempFile.setExecutable(true, true)) {
204 tempInaccessible = "cannot set executable permission";
206 else if (new ProcessBuilder(ideTempFile.getAbsolutePath()).start().waitFor() != 0) {
207 tempInaccessible = "cannot execute test script";
210 if (!FileUtilRt.delete(ideTempFile)) {
211 ideTempFile.deleteOnExit();
214 catch (Exception e) {
215 tempInaccessible = e.getClass().getSimpleName() + ": " + e.getMessage();
219 if (tempInaccessible != null) {
220 String message = "Temp directory '" + ideTempDir + "' is inaccessible.\n" +
221 "If you have modified the '" + PathManager.PROPERTY_SYSTEM_PATH + "' property please make sure it is correct,\n" +
222 "otherwise please re-install the IDE.\n\nDetails: " + tempInaccessible;
223 Main.showMessage("Invalid System Path", message, true);
230 private synchronized static boolean lockSystemFolders(String[] args) {
231 if (ourLock == null) {
232 ourLock = new SocketLock();
235 SocketLock.ActivateStatus activateStatus = ourLock.lock(PathManager.getConfigPath(), true, args);
236 if (activateStatus == SocketLock.ActivateStatus.NO_INSTANCE) {
237 activateStatus = ourLock.lock(PathManager.getSystemPath(), false);
240 if (activateStatus != SocketLock.ActivateStatus.NO_INSTANCE) {
241 if (Main.isHeadless() || activateStatus == SocketLock.ActivateStatus.CANNOT_ACTIVATE) {
242 String message = "Only one instance of " + ApplicationNamesInfo.getInstance().getFullProductName() + " can be run at a time.";
243 Main.showMessage("Too Many Instances", message, true);
251 private static void fixProcessEnvironment(Logger log) {
252 if (!Main.isCommandLine()) {
253 System.setProperty("__idea.mac.env.lock", "unlocked");
255 boolean envReady = EnvironmentUtil.isEnvironmentReady(); // trigger environment loading
257 log.info("initializing environment");
261 private static final String JAVA_IO_TEMP_DIR = "java.io.tmpdir";
263 private static void loadSystemLibraries(final Logger log) {
264 // load JNA and Snappy in own temp directory - to avoid collisions and work around no-exec /tmp
265 File ideTempDir = new File(PathManager.getTempPath());
266 if (!(ideTempDir.mkdirs() || ideTempDir.exists())) {
267 throw new RuntimeException("Unable to create temp directory '" + ideTempDir + "'");
270 String javaTempDir = System.getProperty(JAVA_IO_TEMP_DIR);
272 System.setProperty(JAVA_IO_TEMP_DIR, ideTempDir.getPath());
273 if (System.getProperty("jna.nosys") == null && System.getProperty("jna.nounpack") == null) {
274 // force using bundled JNA dispatcher (if not explicitly stated)
275 System.setProperty("jna.nosys", "true");
276 System.setProperty("jna.nounpack", "false");
279 final long t = System.currentTimeMillis();
280 log.info("JNA library loaded (" + (Native.POINTER_SIZE * 8) + "-bit) in " + (System.currentTimeMillis() - t) + " ms");
282 catch (Throwable t) {
283 logError(log, "Unable to load JNA library", t);
287 System.setProperty(JAVA_IO_TEMP_DIR, javaTempDir);
290 if (SystemInfo.isWin2kOrNewer) {
291 IdeaWin32.isAvailable(); // logging is done there
294 if (SystemInfo.isWin2kOrNewer && !Main.isHeadless()) {
296 UrlClassLoader.loadPlatformLibrary("focusKiller");
297 log.info("Using \"FocusKiller\" library to prevent focus stealing.");
299 catch (Throwable t) {
300 log.info("\"FocusKiller\" library not found or there were problems loading it.", t);
305 private static void logError(Logger log, String message, Throwable t) {
306 message = message + " (OS: " + SystemInfo.OS_NAME + " " + SystemInfo.OS_VERSION + ")";
307 log.error(message, t);
310 private static void startLogging(final Logger log) {
311 Runtime.getRuntime().addShutdownHook(new Thread("Shutdown hook - logging") {
314 log.info("------------------------------------------------------ IDE SHUTDOWN ------------------------------------------------------");
317 log.info("------------------------------------------------------ IDE STARTED ------------------------------------------------------");
319 ApplicationInfo appInfo = ApplicationInfoImpl.getShadowInstance();
320 ApplicationNamesInfo namesInfo = ApplicationNamesInfo.getInstance();
321 String buildDate = new SimpleDateFormat("dd MMM yyyy HH:ss", Locale.US).format(appInfo.getBuildDate().getTime());
322 log.info("IDE: " + namesInfo.getFullProductName() + " (build #" + appInfo.getBuild() + ", " + buildDate + ")");
323 log.info("OS: " + SystemInfoRt.OS_NAME + " (" + SystemInfoRt.OS_VERSION + ", " + SystemInfo.OS_ARCH + ")");
324 log.info("JRE: " + System.getProperty("java.runtime.version", "-") + " (" + System.getProperty("java.vendor", "-") + ")");
325 log.info("JVM: " + System.getProperty("java.vm.version", "-") + " (" + System.getProperty("java.vm.name", "-") + ")");
327 List<String> arguments = ManagementFactory.getRuntimeMXBean().getInputArguments();
328 if (arguments != null) {
329 log.info("JVM Args: " + StringUtil.join(arguments, " "));