blob: 43c0a2d298f105bf5d624d984767e5884ced3b15 [file] [log] [blame]
/*
* 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.ConfigImportHelper;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.application.impl.ApplicationInfoImpl;
import com.intellij.openapi.diagnostic.Logger;
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 org.jetbrains.annotations.NonNls;
import javax.swing.*;
import java.io.File;
import java.lang.management.ManagementFactory;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* @author yole
*/
public class StartupUtil {
@NonNls public static final String NO_SPLASH = "nosplash";
private static SocketLock ourLock;
private static String myDefaultLAF;
private static String myWizardLAF;
private static String myWizardMacKeymap;
private static Set<String> myFeaturedPluginsToInstall = new HashSet<String>();
private StartupUtil() { }
public static void setDefaultLAF(String laf) {
myDefaultLAF = laf;
}
public static String getDefaultLAF() {
return myDefaultLAF;
}
public static void setWizardLAF(String myWizardLAF) {
StartupUtil.myWizardLAF = myWizardLAF;
}
public static String getWizardLAF() {
return myWizardLAF;
}
public static void setMyWizardMacKeymap(String myWizardMacKeymap) {
StartupUtil.myWizardMacKeymap = myWizardMacKeymap;
}
public static String getMyWizardMacKeymap() {
return myWizardMacKeymap;
}
public static Set<String> getMyFeaturedPluginsToInstall() {
return Collections.unmodifiableSet(myFeaturedPluginsToInstall);
}
public static void setFeaturedPluginsToInstall(Set<String> pluginsToInstall) {
myFeaturedPluginsToInstall.clear();
myFeaturedPluginsToInstall.addAll(pluginsToInstall);
}
public static boolean shouldShowSplash(final String[] args) {
return !Arrays.asList(args).contains(NO_SPLASH);
}
/** @deprecated use {@link Main#isHeadless()} (to remove in IDEA 14) */
@SuppressWarnings("unused")
public static boolean isHeadless() {
return Main.isHeadless();
}
public synchronized static void addExternalInstanceListener(Consumer<List<String>> consumer) {
ourLock.setActivateListener(consumer);
}
interface AppStarter {
void start(boolean newConfigFolder);
}
public synchronized static int getAcquiredPort() {
return ourLock.getAcquiredPort();
}
static void prepareAndStart(String[] args, AppStarter appStarter) {
boolean newConfigFolder = false;
if (!Main.isHeadless()) {
AppUIUtil.updateFrameClass();
newConfigFolder = !new File(PathManager.getConfigPath()).exists();
}
boolean canStart = checkJdkVersion() && checkSystemFolders() && lockSystemFolders(args); // note: uses config folder!
if (!canStart) {
System.exit(Main.STARTUP_IMPOSSIBLE);
}
if (newConfigFolder) {
ConfigImportHelper.importConfigsTo(PathManager.getConfigPath());
}
Logger.setFactory(LoggerFactory.class);
Logger log = Logger.getInstance(Main.class);
startLogging(log);
loadSystemLibraries(log);
Main.dumpDelayedLogging(log);
fixProcessEnvironment(log);
if (!Main.isHeadless()) {
AppUIUtil.updateWindowIcon(JOptionPane.getRootFrame());
AppUIUtil.registerBundledFonts();
}
appStarter.start(newConfigFolder);
}
/**
* Checks if the program can run under the JDK it was started with.
*/
private static boolean checkJdkVersion() {
if (!"true".equals(System.getProperty("idea.no.jre.check"))) {
try {
// try to find a class from tools.jar
Class.forName("com.sun.jdi.Field");
}
catch (ClassNotFoundException e) {
String message = "'tools.jar' seems to be not in " + ApplicationNamesInfo.getInstance().getProductName() + " classpath.\n" +
"Please ensure JAVA_HOME points to JDK rather than JRE.";
Main.showMessage("JDK Required", message, true);
return false;
}
if (StringUtil.containsIgnoreCase(System.getProperty("java.vm.name", ""), "OpenJDK") && !SystemInfo.isJavaVersionAtLeast("1.7")) {
String message = "OpenJDK 6 is not supported. Please use Oracle Java or newer OpenJDK.";
Main.showMessage("Unsupported JVM", message, true);
return false;
}
}
return true;
}
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 synchronized static boolean lockSystemFolders(String[] args) {
if (ourLock == null) {
ourLock = new SocketLock();
}
SocketLock.ActivateStatus activateStatus = ourLock.lock(PathManager.getConfigPath(), true, args);
if (activateStatus == SocketLock.ActivateStatus.NO_INSTANCE) {
activateStatus = ourLock.lock(PathManager.getSystemPath(), false);
}
if (activateStatus != SocketLock.ActivateStatus.NO_INSTANCE) {
if (Main.isHeadless() || activateStatus == 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 false;
}
return true;
}
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().asStringWithAllDetails() + ", " + 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, " "));
}
}
}