/* * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.idea; import com.intellij.openapi.application.ApplicationInfo; import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.application.ex.ApplicationInfoEx; import com.intellij.openapi.application.impl.ApplicationInfoImpl; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.ShutDownTracker; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.SystemInfoRt; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.util.io.win32.IdeaWin32; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.AppUIUtil; import com.intellij.util.Consumer; import com.intellij.util.EnvironmentUtil; import com.intellij.util.lang.UrlClassLoader; import com.sun.jna.Native; import consulo.start.CommandLineArgs; import org.apache.log4j.ConsoleAppender; import org.apache.log4j.Level; import org.apache.log4j.PatternLayout; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.io.BuiltInServer; import javax.swing.*; import java.io.File; import java.lang.management.ManagementFactory; import java.text.SimpleDateFormat; import java.util.List; import java.util.Locale; /** * @author yole */ public class StartupUtil { private static SocketLock ourSocketLock; private StartupUtil() { } public synchronized static void addExternalInstanceListener(@NotNull Consumer<CommandLineArgs> consumer) { ourSocketLock.setExternalInstanceListener(consumer); } @Nullable public synchronized static BuiltInServer getServer() { return ourSocketLock == null ? null : ourSocketLock.getServer(); } public static void prepareAndStart(String[] args, Consumer<CommandLineArgs> appStarter) { CommandLineArgs commandLineArgs = CommandLineArgs.parse(args); if (commandLineArgs.isShowHelp()) { CommandLineArgs.printUsage(); System.exit(Main.USAGE_INFO); } if (commandLineArgs.isShowVersion()) { ApplicationInfoEx infoEx = ApplicationInfoImpl.getShadowInstance(); System.out.println(infoEx.getFullApplicationName()); System.exit(Main.VERSION_INFO); } if (!Main.isHeadless()) { AppUIUtil.updateFrameClass(); } // avoiding "log4j:WARN No appenders could be found" System.setProperty("log4j.defaultInitOverride", "true"); try { org.apache.log4j.Logger root = org.apache.log4j.Logger.getRootLogger(); if (!root.getAllAppenders().hasMoreElements()) { root.setLevel(Level.WARN); root.addAppender(new ConsoleAppender(new PatternLayout(PatternLayout.DEFAULT_CONVERSION_PATTERN))); } } catch (Throwable e) { //noinspection CallToPrintStackTrace e.printStackTrace(); } ActivationResult result = lockSystemFolders(args); if (result == ActivationResult.ACTIVATED) { System.exit(0); } else if (result != ActivationResult.STARTED) { System.exit(Main.INSTANCE_CHECK_FAILED); } Logger.setFactory(LoggerFactory.class); Logger log = Logger.getInstance(Main.class); startLogging(log); loadSystemLibraries(log); fixProcessEnvironment(log); if (!Main.isHeadless()) { AppUIUtil.updateWindowIcon(JOptionPane.getRootFrame()); AppUIUtil.registerBundledFonts(); } appStarter.consume(commandLineArgs); } private synchronized static boolean checkSystemFolders() { String configPath = PathManager.getConfigPath(); PathManager.ensureConfigFolderExists(); if (!new File(configPath).isDirectory()) { String message = "Config path '" + configPath + "' is invalid.\n" + "If you have modified the '" + PathManager.PROPERTY_CONFIG_PATH + "' property please make sure it is correct,\n" + "otherwise please re-install the IDE."; Main.showMessage("Invalid Config Path", message, true); return false; } String systemPath = PathManager.getSystemPath(); if (!new File(systemPath).isDirectory()) { String message = "System path '" + systemPath + "' is invalid.\n" + "If you have modified the '" + PathManager.PROPERTY_SYSTEM_PATH + "' property please make sure it is correct,\n" + "otherwise please re-install the IDE."; Main.showMessage("Invalid System Path", message, true); return false; } File ideTempDir = new File(PathManager.getTempPath()); String tempInaccessible = null; if (!ideTempDir.isDirectory() && !ideTempDir.mkdirs()) { tempInaccessible = "unable to create the directory"; } else { try { File ideTempFile = new File(ideTempDir, "idea_tmp_check.sh"); FileUtil.writeToFile(ideTempFile, "#!/bin/sh\nexit 0"); if (SystemInfo.isWindows || SystemInfo.isMac) { tempInaccessible = null; } else if (!ideTempFile.setExecutable(true, true)) { tempInaccessible = "cannot set executable permission"; } else if (new ProcessBuilder(ideTempFile.getAbsolutePath()).start().waitFor() != 0) { tempInaccessible = "cannot execute test script"; } if (!FileUtilRt.delete(ideTempFile)) { ideTempFile.deleteOnExit(); } } catch (Exception e) { tempInaccessible = e.getClass().getSimpleName() + ": " + e.getMessage(); } } if (tempInaccessible != null) { String message = "Temp directory '" + ideTempDir + "' is inaccessible.\n" + "If you have modified the '" + PathManager.PROPERTY_SYSTEM_PATH + "' property please make sure it is correct,\n" + "otherwise please re-install the IDE.\n\nDetails: " + tempInaccessible; Main.showMessage("Invalid System Path", message, true); return false; } return true; } private enum ActivationResult { STARTED, ACTIVATED, FAILED } @NotNull private synchronized static ActivationResult lockSystemFolders(String[] args) { if (ourSocketLock != null) { throw new AssertionError(); } ourSocketLock = new SocketLock(PathManager.getConfigPath(), PathManager.getSystemPath()); SocketLock.ActivateStatus status; try { status = ourSocketLock.lock(args); } catch (Exception e) { Main.showMessage("Cannot Lock System Folders", e); return ActivationResult.FAILED; } if (status == SocketLock.ActivateStatus.NO_INSTANCE) { ShutDownTracker.getInstance().registerShutdownTask(() -> { //noinspection SynchronizeOnThis synchronized (StartupUtil.class) { ourSocketLock.dispose(); ourSocketLock = null; } }); return ActivationResult.STARTED; } else if (status == SocketLock.ActivateStatus.ACTIVATED) { //noinspection UseOfSystemOutOrSystemErr System.out.println("Already running"); return ActivationResult.ACTIVATED; } else if (Main.isHeadless() || status == SocketLock.ActivateStatus.CANNOT_ACTIVATE) { String message = "Only one instance of " + ApplicationNamesInfo.getInstance().getFullProductName() + " can be run at a time."; Main.showMessage("Too Many Instances", message, true); } return ActivationResult.FAILED; } private static void fixProcessEnvironment(Logger log) { if (!Main.isCommandLine()) { System.setProperty("__idea.mac.env.lock", "unlocked"); } boolean envReady = EnvironmentUtil.isEnvironmentReady(); // trigger environment loading if (!envReady) { log.info("initializing environment"); } } private static final String JAVA_IO_TEMP_DIR = "java.io.tmpdir"; private static void loadSystemLibraries(final Logger log) { // load JNA and Snappy in own temp directory - to avoid collisions and work around no-exec /tmp File ideTempDir = new File(PathManager.getTempPath()); if (!(ideTempDir.mkdirs() || ideTempDir.exists())) { throw new RuntimeException("Unable to create temp directory '" + ideTempDir + "'"); } String javaTempDir = System.getProperty(JAVA_IO_TEMP_DIR); try { System.setProperty(JAVA_IO_TEMP_DIR, ideTempDir.getPath()); if (System.getProperty("jna.nosys") == null && System.getProperty("jna.nounpack") == null) { // force using bundled JNA dispatcher (if not explicitly stated) System.setProperty("jna.nosys", "true"); System.setProperty("jna.nounpack", "false"); } try { final long t = System.currentTimeMillis(); log.info("JNA library loaded (" + (Native.POINTER_SIZE * 8) + "-bit) in " + (System.currentTimeMillis() - t) + " ms"); } catch (Throwable t) { logError(log, "Unable to load JNA library", t); } } finally { System.setProperty(JAVA_IO_TEMP_DIR, javaTempDir); } if (SystemInfo.isWin2kOrNewer) { IdeaWin32.isAvailable(); // logging is done there } if (SystemInfo.isWin2kOrNewer && !Main.isHeadless()) { try { UrlClassLoader.loadPlatformLibrary("focusKiller"); log.info("Using \"FocusKiller\" library to prevent focus stealing."); } catch (Throwable t) { log.info("\"FocusKiller\" library not found or there were problems loading it.", t); } } } private static void logError(Logger log, String message, Throwable t) { message = message + " (OS: " + SystemInfo.OS_NAME + " " + SystemInfo.OS_VERSION + ")"; log.error(message, t); } private static void startLogging(final Logger log) { Runtime.getRuntime().addShutdownHook(new Thread("Shutdown hook - logging") { @Override public void run() { log.info("------------------------------------------------------ IDE SHUTDOWN ------------------------------------------------------"); } }); log.info("------------------------------------------------------ IDE STARTED ------------------------------------------------------"); ApplicationInfo appInfo = ApplicationInfoImpl.getShadowInstance(); ApplicationNamesInfo namesInfo = ApplicationNamesInfo.getInstance(); String buildDate = new SimpleDateFormat("dd MMM yyyy HH:ss", Locale.US).format(appInfo.getBuildDate().getTime()); log.info("IDE: " + namesInfo.getFullProductName() + " (build #" + appInfo.getBuild() + ", " + buildDate + ")"); log.info("OS: " + SystemInfoRt.OS_NAME + " (" + SystemInfoRt.OS_VERSION + ", " + SystemInfo.OS_ARCH + ")"); log.info("JRE: " + System.getProperty("java.runtime.version", "-") + " (" + System.getProperty("java.vendor", "-") + ")"); log.info("JVM: " + System.getProperty("java.vm.version", "-") + " (" + System.getProperty("java.vm.name", "-") + ")"); List<String> arguments = ManagementFactory.getRuntimeMXBean().getInputArguments(); if (arguments != null) { log.info("JVM Args: " + StringUtil.join(arguments, " ")); } } }