From cb69f4c7d3e50d1cb34ebcaa5f2f2928eb5cc7d1 Mon Sep 17 00:00:00 2001
From: Roman Shevchenko
Date: Wed, 4 Sep 2019 22:07:02 +0300
Subject: [PATCH] Cleanup (leaner code in start-up utils; typos; formatting)
GitOrigin-RevId: 7364ef3a09389e516f798862c3a8a60eded0d221
---
.../bootstrap/src/com/intellij/idea/Main.java | 4 +-
.../src/com/intellij/idea/SocketLock.java | 305 +++++++-----------
.../src/com/intellij/idea/StartupUtil.java | 203 +++++-------
.../src/com/intellij/idea/MainImpl.java | 4 +-
.../com/intellij/idea/LockSupportTest.java | 51 ++-
.../com/intellij/diagnostic/ActivityImpl.java | 6 +-
.../application/JetBrainsProtocolHandler.java | 14 +-
.../util/ui/EdtInvocationManager.java | 19 +-
8 files changed, 246 insertions(+), 360 deletions(-)
diff --git a/platform/bootstrap/src/com/intellij/idea/Main.java b/platform/bootstrap/src/com/intellij/idea/Main.java
index 54c8e79d7999..b94829dc48b0 100644
--- a/platform/bootstrap/src/com/intellij/idea/Main.java
+++ b/platform/bootstrap/src/com/intellij/idea/Main.java
@@ -26,11 +26,9 @@ public final class Main {
public static final int LICENSE_ERROR = 7;
public static final int PLUGIN_ERROR = 8;
public static final int OUT_OF_MEMORY = 9;
- @SuppressWarnings("unused") // left for compatibility and reserved for future use
- public static final int UNSUPPORTED_JAVA_VERSION = 10;
+ @SuppressWarnings("unused") public static final int UNSUPPORTED_JAVA_VERSION = 10; // left for compatibility/reserved for future use
public static final int PRIVACY_POLICY_REJECTION = 11;
public static final int INSTALLATION_CORRUPTED = 12;
- // External cmdline and IDE activation
public static final int ACTIVATE_WRONG_TOKEN_CODE = 13;
public static final int ACTIVATE_LISTENER_NOT_INITIALIZED = 14;
public static final int ACTIVATE_RESPONSE_TIMEOUT = 15;
diff --git a/platform/platform-impl/src/com/intellij/idea/SocketLock.java b/platform/platform-impl/src/com/intellij/idea/SocketLock.java
index db190e891d7e..812e969c9cc3 100644
--- a/platform/platform-impl/src/com/intellij/idea/SocketLock.java
+++ b/platform/platform-impl/src/com/intellij/idea/SocketLock.java
@@ -12,15 +12,15 @@ import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.JetBrainsProtocolHandler;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
-import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.util.ArrayUtilRt;
import com.intellij.util.concurrency.AppExecutorUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;
+import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -44,19 +44,18 @@ import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFilePermission;
import java.util.List;
import java.util.*;
-import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Future;
-import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
+import java.util.function.Supplier;
import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
+
+import static com.intellij.openapi.util.Pair.pair;
public final class SocketLock {
- public enum ActivateStatus {ACTIVATED, NO_INSTANCE, CANNOT_ACTIVATE}
+ public enum ActivationStatus {ACTIVATED, NO_INSTANCE, CANNOT_ACTIVATE}
- private static final String PATHS_EOT_RESPONSE = "---";
private static final String PORT_FILE = "port";
private static final String PORT_LOCK_FILE = "port.lock";
private static final String TOKEN_FILE = "token";
@@ -64,12 +63,13 @@ public final class SocketLock {
private static final String ACTIVATE_COMMAND = "activate ";
private static final String PID_COMMAND = "pid";
private static final String OK_RESPONSE = "ok";
+ private static final String PATHS_EOT_RESPONSE = "---";
- private final AtomicReference myActivateListener = new AtomicReference<>();
+ private final AtomicReference, Future>> myCommandProcessorRef = new AtomicReference<>();
private final String myConfigPath;
private final String mySystemPath;
- private final BlockingQueue myLockedFiles = new LinkedBlockingQueue<>();
- @Nullable private Future extends Disposable> myBuiltinServerFuture;
+ private final List myLockedFiles = new ArrayList<>(2);
+ private @Nullable Future extends Disposable> myBuiltinServerFuture;
private volatile String myToken;
private BuiltInServer myServer;
@@ -81,8 +81,17 @@ public final class SocketLock {
}
}
- public void setExternalInstanceListener(@Nullable CliRequestProcessor processor) {
- myActivateListener.set(processor);
+ private static String canonicalPath(String path) {
+ try {
+ return new File(path).getCanonicalPath();
+ }
+ catch (IOException ignore) {
+ return path;
+ }
+ }
+
+ public void setCommandProcessor(@Nullable Function, Future> processor) {
+ myCommandProcessorRef.set(processor);
}
public void dispose() {
@@ -110,48 +119,40 @@ public final class SocketLock {
}
}
- @Nullable
- BuiltInServer getServer() {
+ @Nullable BuiltInServer getServer() {
return myServer;
}
- @NotNull
- public ActivateStatusAndResponse lockAndTryActivate() throws Exception {
- return lockAndTryActivate(ArrayUtilRt.EMPTY_STRING_ARRAY);
- }
-
- @NotNull
- public ActivateStatusAndResponse lockAndTryActivate(@NotNull String[] args) throws Exception {
+ public @NotNull Pair lockAndTryActivate(@NotNull String[] args) throws IOException {
log("enter: lock(config=%s system=%s)", myConfigPath, mySystemPath);
lockPortFiles();
- File portMarkerC = new File(myConfigPath, PORT_FILE);
- File portMarkerS = new File(mySystemPath, PORT_FILE);
MultiMap portToPath = MultiMap.createSmart();
- addExistingPort(portMarkerC, myConfigPath, portToPath);
- addExistingPort(portMarkerS, mySystemPath, portToPath);
+ readPort(myConfigPath, portToPath);
+ readPort(mySystemPath, portToPath);
if (!portToPath.isEmpty()) {
+ args = JetBrainsProtocolHandler.checkForJetBrainsProtocolCommand(args);
for (Map.Entry> entry : portToPath.entrySet()) {
- ActivateStatusAndResponse status = tryActivate(entry.getKey(), entry.getValue(), args);
- if (status.getActivateStatus() != ActivateStatus.NO_INSTANCE) {
- log("exit: lock(): " + status.getActivateStatus());
+ Pair status = tryActivate(entry.getKey(), entry.getValue(), args);
+ if (status.first != ActivationStatus.NO_INSTANCE) {
+ log("exit: lock(): " + status.first);
unlockPortFiles();
return status;
}
}
}
- if (isShutdownCommand()) {
+ if (JetBrainsProtocolHandler.isShutdownCommand()) {
unlockPortFiles();
System.exit(0);
}
myBuiltinServerFuture = AppExecutorUtil.getAppExecutorService().submit(() -> {
- Activity builtinServerLaunch = ParallelActivity.PREPARE_APP_INIT.start("builtin server launch");
+ Activity activity = ParallelActivity.PREPARE_APP_INIT.start("builtin server launch");
+
myToken = UUID.randomUUID().toString();
Path tokenFile = Paths.get(mySystemPath, TOKEN_FILE);
- // parent directories are already created (see underLocks)
Files.write(tokenFile, myToken.getBytes(StandardCharsets.UTF_8));
PosixFileAttributeView view = Files.getFileAttributeView(tokenFile, PosixFileAttributeView.class);
if (view != null) {
@@ -164,41 +165,45 @@ public final class SocketLock {
}
try {
- return launchBuiltinServer(new String[]{myConfigPath, mySystemPath});
+ String[] lockedPaths = {myConfigPath, mySystemPath};
+ Supplier handlerSupplier = () -> new MyChannelInboundHandler(lockedPaths, myCommandProcessorRef, myToken);
+ myServer = BuiltInServer.startNioOrOio(BuiltInServer.getRecommendedWorkerCount(), 6942, 50, false, handlerSupplier);
+ writePort(myServer.getPort());
+ unlockPortFiles();
+ return myServer;
+ }
+ catch (Exception e) {
+ log(e);
+ return null;
}
finally {
- builtinServerLaunch.end();
+ activity.end();
}
});
log("exit: lock(): succeed");
- return ActivateStatusAndResponse.emptyResponse(ActivateStatus.NO_INSTANCE);
+ return pair(ActivationStatus.NO_INSTANCE, null);
}
- void writePortAndUnlockFiles(int port) {
- File portMarkerC = new File(myConfigPath, PORT_FILE);
- File portMarkerS = new File(mySystemPath, PORT_FILE);
- byte[] portBytes = Integer.toString(port).getBytes(StandardCharsets.UTF_8);
-
+ private void writePort(int port) {
+ byte[] data = Integer.toString(port).getBytes(StandardCharsets.UTF_8);
try {
- FileUtil.writeToFile(portMarkerC, portBytes);
- FileUtil.writeToFile(portMarkerS, portBytes);
+ FileUtil.writeToFile(new File(myConfigPath, PORT_FILE), data);
+ FileUtil.writeToFile(new File(mySystemPath, PORT_FILE), data);
}
catch (IOException e) {
log(e);
}
-
- unlockPortFiles();
}
/**
- According to https://stackoverflow.com/a/12652718/3463676, file locks should be removed on process segfault.
- According to {@link FileLock} javadoc, file locks are marked as invalid on JVM termination.
-
-
Unlocking of port files (via {@link #unlockPortFiles}) happens either after builtin server init, or on app termination.
-
-
Because of that, we do not care about non-starting netty leading to infinite lock handling, as we are not ready to
- accept connections anyway; on app termination the locks will be released anyway.
+ *
According to https://stackoverflow.com/a/12652718/3463676, file locks should be removed on process segfault.
+ * According to {@link FileLock} documentation, file locks are marked as invalid on JVM termination.
+ *
+ * Unlocking of port files (via {@link #unlockPortFiles}) happens either after builtin server init, or on app termination.
+ *
+ * Because of that, we do not care about non-starting Netty leading to infinite lock handling, as the IDE is not ready to
+ * accept connections anyway; on app termination the locks will be released.
*/
private synchronized void lockPortFiles() throws IOException {
if (!myLockedFiles.isEmpty()) {
@@ -206,12 +211,12 @@ public final class SocketLock {
}
OpenOption[] options = {StandardOpenOption.CREATE, StandardOpenOption.APPEND};
- FileUtilRt.createDirectory(new File(myConfigPath));
+ FileUtil.createDirectory(new File(myConfigPath));
FileChannel cc = FileChannel.open(Paths.get(myConfigPath, PORT_LOCK_FILE), options);
FileLock cl = cc.lock();
myLockedFiles.add(cl);
- FileUtilRt.createDirectory(new File(mySystemPath));
+ FileUtil.createDirectory(new File(mySystemPath));
FileChannel sc = FileChannel.open(Paths.get(mySystemPath, PORT_LOCK_FILE), options);
FileLock sl = sc.lock();
myLockedFiles.add(sl);
@@ -226,67 +231,63 @@ public final class SocketLock {
try {
lock.release();
}
- catch (IOException ignore) {
- }
+ catch (IOException ignore) { }
}
myLockedFiles.clear();
}
- private static void addExistingPort(@NotNull File portMarker, @NotNull String path, @NotNull MultiMap portToPath) {
- if (portMarker.exists()) {
+ private static void readPort(String path, MultiMap portToPath) {
+ File portFile = new File(path, PORT_FILE);
+ if (portFile.exists()) {
try {
- portToPath.putValue(Integer.parseInt(FileUtilRt.loadFile(portMarker)), path);
+ portToPath.putValue(Integer.parseInt(FileUtil.loadFile(portFile)), path);
}
catch (Exception e) {
- log(e);
- // don't delete - we overwrite it on write in any case
+ log(e); // no need to delete - it would be overwritten
}
}
}
- @NotNull
- private ActivateStatusAndResponse tryActivate(int portNumber, @NotNull Collection paths, @NotNull String[] args) {
+ private Pair tryActivate(int portNumber, Collection paths, String[] args) {
log("trying: port=%s", portNumber);
- args = checkForJetBrainsProtocolCommand(args);
- try {
- try (Socket socket = new Socket(InetAddress.getLoopbackAddress(), portNumber)) {
- socket.setSoTimeout(5000);
- DataInputStream in = new DataInputStream(socket.getInputStream());
- final List stringList = readStringSequence(in);
- // Backward compatibility: it required at least one path to match
- boolean result = ContainerUtil.intersects(paths, stringList);
+ try (Socket socket = new Socket(InetAddress.getLoopbackAddress(), portNumber)) {
+ socket.setSoTimeout(5000);
- if (result) {
- // Update property right now, without scheduling on AWT. This allows to avoid shown-and-immediately-hidden splash in some cases.
- System.setProperty(SplashManager.NO_SPLASH, "true");
- EventQueue.invokeLater(() -> {
- Runnable hideSplashTask = SplashManager.getHideTask();
- if (hideSplashTask != null) hideSplashTask.run();
- });
+ DataInputStream in = new DataInputStream(socket.getInputStream());
+ List stringList = readStringSequence(in);
+ // backward compatibility: requires at least one path to match
+ boolean result = ContainerUtil.intersects(paths, stringList);
- try {
- String token = FileUtil.loadFile(new File(mySystemPath, TOKEN_FILE));
- @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") DataOutputStream out = new DataOutputStream(socket.getOutputStream());
- out.writeUTF(ACTIVATE_COMMAND + token + "\0" + new File(".").getAbsolutePath() + "\0" + StringUtil.join(args, "\0"));
- out.flush();
+ if (result) {
+ // update property right now, without scheduling to EDT - in some cases, allows to avoid a splash flickering
+ System.setProperty(SplashManager.NO_SPLASH, "true");
+ EventQueue.invokeLater(() -> {
+ Runnable hideSplashTask = SplashManager.getHideTask();
+ if (hideSplashTask != null) hideSplashTask.run();
+ });
- socket.setSoTimeout(0);
- List response = readStringSequence(in);
- log("read: response=%s", StringUtil.join(response, ";"));
- if (OK_RESPONSE.equals(ContainerUtil.getFirstItem(response))) {
- if (isShutdownCommand()) {
- printPID(portNumber);
- }
- return new ActivateStatusAndResponse(ActivateStatus.ACTIVATED, mapResponseToCliResult(response));
+ try {
+ String token = FileUtil.loadFile(new File(mySystemPath, TOKEN_FILE));
+ @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") DataOutputStream out = new DataOutputStream(socket.getOutputStream());
+ out.writeUTF(ACTIVATE_COMMAND + token + '\0' + new File(".").getAbsolutePath() + '\0' + StringUtil.join(args, "\0"));
+ out.flush();
+
+ socket.setSoTimeout(0);
+ List response = readStringSequence(in);
+ log("read: response=%s", StringUtil.join(response, ";"));
+ if (OK_RESPONSE.equals(ContainerUtil.getFirstItem(response))) {
+ if (JetBrainsProtocolHandler.isShutdownCommand()) {
+ printPID(portNumber);
}
+ return pair(ActivationStatus.ACTIVATED, mapResponseToCliResult(response));
}
- catch (IOException | IllegalArgumentException e) {
- log(e);
- }
-
- return ActivateStatusAndResponse.emptyResponse(ActivateStatus.CANNOT_ACTIVATE);
}
+ catch (IOException | IllegalArgumentException e) {
+ log(e);
+ }
+
+ return pair(ActivationStatus.CANNOT_ACTIVATE, null);
}
}
catch (ConnectException e) {
@@ -296,24 +297,7 @@ public final class SocketLock {
log(e);
}
- return ActivateStatusAndResponse.emptyResponse(ActivateStatus.NO_INSTANCE);
- }
-
- @Nullable
- private Disposable launchBuiltinServer(String[] lockedPaths) {
- try {
- myServer = BuiltInServer.startNioOrOio(BuiltInServer.getRecommendedWorkerCount(), 6942, 50, false, () -> {
- //noinspection CodeBlock2Expr
- // should be not inlined because handler created for each connected channel
- return new MyChannelInboundHandler(lockedPaths, myActivateListener, myToken);
- });
- writePortAndUnlockFiles(myServer.getPort());
- return myServer;
- }
- catch (Exception e) {
- log(e);
- return null;
- }
+ return pair(ActivationStatus.NO_INSTANCE, null);
}
@SuppressWarnings("ALL")
@@ -332,44 +316,28 @@ public final class SocketLock {
pid = Integer.parseInt(s.substring(0, s.indexOf('@')));
System.err.println(pid);
}
- }catch (IOException e) {
+ }
+ catch (IOException e) {
break;
}
}
}
- catch (Exception ignore) {
- }
- }
-
- private static boolean isShutdownCommand() {
- return "shutdown".equals(JetBrainsProtocolHandler.getCommand());
- }
-
- private static String[] checkForJetBrainsProtocolCommand(String[] args) {
- final String jbUrl = System.getProperty(JetBrainsProtocolHandler.class.getName());
- if (jbUrl != null) {
- return new String[]{jbUrl};
- }
- return args;
- }
- @FunctionalInterface
- public interface CliRequestProcessor {
- Future process(@NotNull List args);
+ catch (Exception ignore) { }
}
private static final class MyChannelInboundHandler extends MessageDecoder {
-
private enum State {HEADER, CONTENT}
+
private final String[] myLockedPaths;
- private final AtomicReference extends CliRequestProcessor> myActivateListener;
+ private final AtomicReference, Future>> myCommandProcessorRef;
private final String myToken;
private State myState = State.HEADER;
MyChannelInboundHandler(@NotNull String[] lockedPaths,
- @NotNull AtomicReference extends CliRequestProcessor> activateListener,
+ @NotNull AtomicReference, Future>> commandProcessorRef,
@NotNull String token) {
myLockedPaths = lockedPaths;
- myActivateListener = activateListener;
+ myCommandProcessorRef = commandProcessorRef;
myToken = token;
}
@@ -377,24 +345,22 @@ public final class SocketLock {
public void channelActive(ChannelHandlerContext context) throws Exception {
sendStringSequence(context, Arrays.asList(myLockedPaths));
}
+
@Override
protected void messageReceived(@NotNull ChannelHandlerContext context, @NotNull ByteBuf input) throws Exception {
while (true) {
switch (myState) {
case HEADER: {
ByteBuf buffer = getBufferIfSufficient(input, 2, context);
- if (buffer == null) {
- return;
- }
-
+ if (buffer == null) return;
contentLength = buffer.readUnsignedShort();
if (contentLength > 8192) {
context.close();
return;
}
myState = State.CONTENT;
+ break;
}
- break;
case CONTENT: {
CharSequence command = readChars(input);
@@ -415,7 +381,7 @@ public final class SocketLock {
String data = command.subSequence(ACTIVATE_COMMAND.length(), command.length()).toString();
List args = StringUtil.split(data, data.contains("\0") ? "\0" : "\uFFFD");
- final CliResult result;
+ CliResult result;
boolean tokenOK = !args.isEmpty() && myToken.equals(args.get(0));
if (!tokenOK) {
log(new UnsupportedOperationException("unauthorized request: " + command));
@@ -427,23 +393,22 @@ public final class SocketLock {
result = new CliResult(Main.ACTIVATE_WRONG_TOKEN_CODE, IdeBundle.message("activation.auth.message"));
}
else {
- CliRequestProcessor listener = myActivateListener.get();
+ Function, Future> listener = myCommandProcessorRef.get();
if (listener != null) {
- result = CliResult.getOrWrapFailure(listener.process(args.subList(1, args.size())), Main.ACTIVATE_RESPONSE_TIMEOUT);
+ result = CliResult.getOrWrapFailure(listener.apply(args.subList(1, args.size())), Main.ACTIVATE_RESPONSE_TIMEOUT);
}
else {
result = new CliResult(Main.ACTIVATE_LISTENER_NOT_INITIALIZED, IdeBundle.message("activation.not.initialized"));
}
}
- sendStringSequence(context,
- Stream.of(OK_RESPONSE, String.valueOf(result.getReturnCode()), result.getMessage())
- .filter(Objects::nonNull)
- .collect(Collectors.toList()));
+ List response = new ArrayList<>();
+ ContainerUtil.addAllNotNull(response, OK_RESPONSE, String.valueOf(result.getReturnCode()), result.getMessage());
+ sendStringSequence(context, response);
}
context.close();
+ break;
}
- break;
}
}
}
@@ -465,7 +430,6 @@ public final class SocketLock {
context.writeAndFlush(buffer);
}
- @NotNull
private static List readStringSequence(DataInputStream in) {
List result = new ArrayList<>();
while (true) {
@@ -485,13 +449,12 @@ public final class SocketLock {
return result;
}
- @NotNull
- private static CliResult mapResponseToCliResult(@NotNull List responseParts) throws IllegalArgumentException {
+ private static CliResult mapResponseToCliResult(List responseParts) throws IllegalArgumentException {
if (responseParts.size() > 3 || responseParts.size() < 2) {
throw new IllegalArgumentException("bad response: " + StringUtil.join(responseParts, ";"));
}
- final int code;
+ int code;
try {
code = Integer.parseInt(responseParts.get(1));
}
@@ -499,50 +462,18 @@ public final class SocketLock {
throw new IllegalArgumentException("Second part is not a parsable return code", e);
}
- final String message = responseParts.size() == 3 ? responseParts.get(2) : null;
+ String message = responseParts.size() == 3 ? responseParts.get(2) : null;
return new CliResult(code, message);
}
- @NotNull
- private static String canonicalPath(@NotNull String configPath) {
- try {
- return new File(configPath).getCanonicalPath();
- }
- catch (IOException ignore) {
- return configPath;
- }
- }
private static void log(Exception e) {
Logger.getInstance(SocketLock.class).warn(e);
}
+
private static void log(String format, Object... args) {
Logger logger = Logger.getInstance(SocketLock.class);
if (logger.isDebugEnabled()) {
logger.debug(String.format(format, args));
}
}
- public static class ActivateStatusAndResponse {
- @NotNull
- private final ActivateStatus myActivateStatus;
- @Nullable
- private final CliResult myResponse;
-
- public ActivateStatusAndResponse(@NotNull ActivateStatus status, @Nullable CliResult response) {
- myActivateStatus = status;
- myResponse = response;
- }
- @NotNull
- public static ActivateStatusAndResponse emptyResponse(@NotNull ActivateStatus status) {
- return new ActivateStatusAndResponse(status, null);
- }
- @NotNull
- public ActivateStatus getActivateStatus() {
- return myActivateStatus;
- }
-
- @Nullable
- public CliResult getResponse() {
- return myResponse;
- }
- }
}
\ No newline at end of file
diff --git a/platform/platform-impl/src/com/intellij/idea/StartupUtil.java b/platform/platform-impl/src/com/intellij/idea/StartupUtil.java
index 309ee73b57e9..f5d485656b46 100644
--- a/platform/platform-impl/src/com/intellij/idea/StartupUtil.java
+++ b/platform/platform-impl/src/com/intellij/idea/StartupUtil.java
@@ -24,6 +24,7 @@ import com.intellij.openapi.application.impl.ApplicationImpl;
import com.intellij.openapi.application.impl.ApplicationInfoImpl;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.IconLoader;
+import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.ShutDownTracker;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
@@ -67,6 +68,7 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Function;
import static com.intellij.diagnostic.LoadingPhase.LAF_INITIALIZED;
import static java.nio.file.attribute.PosixFilePermission.*;
@@ -87,15 +89,14 @@ public final class StartupUtil {
private static final Thread.UncaughtExceptionHandler HANDLER = (t, e) -> MainRunner.processException(e);
- public static synchronized void addExternalInstanceListener(@Nullable SocketLock.CliRequestProcessor processor) {
- // method called by app after startup
+ /* called by the app after startup */
+ public static synchronized void addExternalInstanceListener(@Nullable Function, Future> processor) {
if (ourSocketLock != null) {
- ourSocketLock.setExternalInstanceListener(processor);
+ ourSocketLock.setCommandProcessor(processor);
}
}
- @Nullable
- public static synchronized BuiltInServer getServer() {
+ public static synchronized @Nullable BuiltInServer getServer() {
return ourSocketLock == null ? null : ourSocketLock.getServer();
}
@@ -103,24 +104,23 @@ public final class StartupUtil {
Thread.currentThread().setUncaughtExceptionHandler(HANDLER);
}
- @FunctionalInterface
public interface AppStarter {
- // called in Idea Main thread
+ /* called from IDE init thread */
void start(@NotNull Future> initUiTask);
- // not called in EDT
+ /* called from IDE init thread */
default void beforeImportConfigs() {}
- // called in EDT
+ /* called from EDT */
default void beforeStartupWizard() {}
- // called in EDT
+ /* called from EDT */
default void startupWizardFinished(@NotNull CustomizeIDEWizardStepsProvider provider) {}
- // not called in EDT
+ /* called from IDE init thread */
default void importFinished(@NotNull Path newConfigDir) {}
- // called in EDT
+ /* called from EDT */
default int customizeIdeWizardDialog(@NotNull List steps) {
return -1;
}
@@ -140,8 +140,7 @@ public final class StartupUtil {
}
}
- static void prepareAndStart(@NotNull String[] args, @NotNull AppStarter appStarter)
- throws InvocationTargetException, InterruptedException, ExecutionException {
+ static void prepareAndStart(@NotNull String[] args, @NotNull AppStarter appStarter) throws Exception {
Activity fjp = StartUpMeasurer.start("setupForkJoinCommonPool");
IdeaForkJoinWorkerThreadFactory.setupForkJoinCommonPool(Main.isHeadless(args));
fjp.end();
@@ -150,23 +149,30 @@ public final class StartupUtil {
Future> initUiTask = scheduleInitUi(args);
- configureLogging();
+ configureLog4j();
if (!checkJdkVersion()) {
System.exit(Main.JDK_CHECK_FAILED);
}
// this check must be performed before system directories are locked
- boolean newConfigFolder = !Main.isHeadless() && !Files.exists(Paths.get(PathManager.getConfigPath()));
+ boolean configImportNeeded = !Main.isHeadless() && !Files.exists(Paths.get(PathManager.getConfigPath()));
- Logger log = lockDirsAndConfigureLogger(args);
+ Activity dirsAndLogs = StartUpMeasurer.start(Phases.CHECK_SYSTEM_DIR);
+ if (!checkSystemDirs()) { // note: uses config directory!
+ System.exit(Main.DIR_CHECK_FAILED);
+ }
+ dirsAndLogs = dirsAndLogs.endAndStart(Phases.LOCK_SYSTEM_DIRS);
+ lockSystemDirs(args);
+ dirsAndLogs = dirsAndLogs.endAndStart("configure file logger");
+ Logger log = setupLogger(); // log initialization should happen only after locking the system directory
+ dirsAndLogs.end();
ExecutorService executorService = AppExecutorUtil.getAppExecutorService();
- // no need to wait
- executorService.submit(() -> {
+ executorService.execute(() -> {
ApplicationInfo appInfo = ApplicationInfoImpl.getShadowInstance();
- Activity activity = ParallelActivity.PREPARE_APP_INIT.start("log essential info about ide");
+ Activity activity = ParallelActivity.PREPARE_APP_INIT.start("log essential info about the IDE");
logEssentialInfoAboutIde(log, appInfo);
activity.end();
});
@@ -180,24 +186,22 @@ public final class StartupUtil {
activity.end();
}));
- if (!newConfigFolder) {
+ if (!configImportNeeded) {
installPluginUpdates();
runPreAppClass(log);
}
- // no need to wait
executorService.execute(() -> loadSystemLibraries(log));
Activity waitTaskActivity = StartUpMeasurer.start(Phases.WAIT_TASKS);
- for (Future> future : futures) {
- future.get();
- }
+ for (Future> future : futures) future.get();
waitTaskActivity.end();
futures.clear();
if (!Main.isHeadless()) {
Activity activity = StartUpMeasurer.start(Phases.IMPORT_CONFIGS);
- if (newConfigFolder) {
+
+ if (configImportNeeded) {
appStarter.beforeImportConfigs();
Path newConfigDir = Paths.get(PathManager.getConfigPath());
runInEdtAndWait(log, () -> ConfigImportHelper.importConfigsTo(newConfigDir, log), initUiTask);
@@ -206,11 +210,11 @@ public final class StartupUtil {
showUserAgreementAndConsentsIfNeeded(log, initUiTask);
- if (newConfigFolder && !ConfigImportHelper.isConfigImported()) {
- // exception handler is already set by ConfigImportHelper
- // event queue and icons already initialized as part of old config import
+ if (configImportNeeded && !ConfigImportHelper.isConfigImported()) {
+ // exception handler is already set by ConfigImportHelper; event queue and icons already initialized as part of old config import
EventQueue.invokeAndWait(() -> runStartupWizard(appStarter));
}
+
activity.end();
}
@@ -225,9 +229,10 @@ public final class StartupUtil {
@NotNull
private static Future> scheduleInitUi(@NotNull String[] args) {
Activity initLafAsync = StartUpMeasurer.start("schedule LaF init");
+
// mainly call sun.util.logging.PlatformLogger.getLogger - it takes enormous time (up to 500 ms)
// Before lockDirsAndConfigureLogger can be executed only tasks that do not require log,
- // because we don't want to complicate logging. It is ok, because lockDirsAndConfigureLogger is not so heavy-weight as UI tasks.
+ // because we don't want to complicate logging. It is OK, because lockDirsAndConfigureLogger is not so heavy-weight as UI tasks.
Future> future = AppExecutorUtil.getAppExecutorService().submit(() -> {
installExceptionHandler();
@@ -255,8 +260,8 @@ public final class StartupUtil {
LoadingPhase.setCurrentPhase(LoadingPhase.SPLASH);
- // can be expensive (~200 ms), so, configure only after showing splash (not required for splash)
- StartupUiUtil.configureHtmlKitStylesheet();
+ // may be expensive (~200 ms), so configure only after showing the splash
+ StartupUiUtil.configureHtmlKitStylesheet();
});
}
catch (InterruptedException | InvocationTargetException e) {
@@ -284,85 +289,31 @@ public final class StartupUtil {
}
LoadingPhase.setCurrentPhase(LAF_INITIALIZED);
-
});
+
initLafAsync.end();
return future;
}
- @NotNull
- private static Logger lockDirsAndConfigureLogger(@NotNull String[] args) {
- Activity activity = StartUpMeasurer.start(Phases.CHECK_SYSTEM_DIR);
- // note: uses config folder!
- if (!checkSystemFolders()) {
- System.exit(Main.DIR_CHECK_FAILED);
- }
-
- activity = activity.endAndStart(Phases.LOCK_SYSTEM_DIRS);
-
- SocketLock.ActivateStatusAndResponse result = lockSystemFolders(args);
- if (result.getActivateStatus() == SocketLock.ActivateStatus.ACTIVATED) {
- final CliResult cliOutput = Objects.requireNonNull(result.getResponse(), "guaranteed by SocketLock.mapResponseToCliResult");
- if (cliOutput.getMessage() != null) {
- //noinspection UseOfSystemOutOrSystemErr
- System.out.println(cliOutput.getMessage());
- }
- System.exit(cliOutput.getReturnCode());
- }
- if (result.getActivateStatus() == SocketLock.ActivateStatus.CANNOT_ACTIVATE) {
- System.exit(Main.INSTANCE_CHECK_FAILED);
- }
-
- activity = activity.endAndStart("configure file logger");
-
- // the log initialization should happen only after locking the system directory
- Logger.setFactory(new LoggerFactory());
- Logger log = Logger.getInstance(Main.class);
-
- log.info("------------------------------------------------------ IDE STARTED ------------------------------------------------------");
- ShutDownTracker.getInstance().registerShutdownTask(() -> {
- log.info("------------------------------------------------------ IDE SHUTDOWN ------------------------------------------------------");
- });
- activity.end();
- return log;
- }
-
- private static void configureLogging() {
+ private static void configureLog4j() {
Activity activity = StartUpMeasurer.start(Phases.CONFIGURE_LOGGING);
// 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();
+ 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)));
}
activity.end();
}
- /**
- * Checks if the program can run under the JDK it was started with.
- */
private static boolean checkJdkVersion() {
if ("true".equals(System.getProperty("idea.jre.check"))) {
try {
- // try to find a JDK class
- Class.forName("com.sun.jdi.Field", false, StartupUtil.class.getClassLoader());
+ Class.forName("com.sun.jdi.Field", false, StartupUtil.class.getClassLoader()); // trying to find a JDK class
}
- catch (ClassNotFoundException e) {
- String message = "JDK classes seem to be not on " + ApplicationNamesInfo.getInstance().getProductName() + " classpath.\n" +
- "Please ensure you run the IDE on JDK rather than JRE.";
- Main.showMessage("JDK Required", message, true);
- return false;
- }
- catch (LinkageError e) {
- String message = "Cannot load a JDK class: " + e.getMessage() + "\n" +
- "Please ensure you run the IDE on JDK rather than JRE.";
+ catch (ClassNotFoundException | LinkageError e) {
+ String message = "Cannot load a JDK class: " + e.getMessage() + "\nPlease ensure you run the IDE on JDK rather than JRE.";
Main.showMessage("JDK Required", message, true);
return false;
}
@@ -370,7 +321,7 @@ public final class StartupUtil {
if ("true".equals(System.getProperty("idea.64bit.check"))) {
if (PlatformUtils.isCidr() && !SystemInfo.is64Bit) {
- String message = "32-bit JVM is not supported. Please use 64-bit version.";
+ String message = "32-bit JVM is not supported. Please use a 64-bit version.";
Main.showMessage("Unsupported JVM", message, true);
return false;
}
@@ -391,7 +342,7 @@ public final class StartupUtil {
}
}
- private static synchronized boolean checkSystemFolders() {
+ private static synchronized boolean checkSystemDirs() {
String configPath = PathManager.getConfigPath();
PathManager.ensureConfigFolderExists();
if (!checkDirectory(configPath, "Config", PathManager.PROPERTY_CONFIG_PATH, true, true, false)) {
@@ -464,7 +415,7 @@ public final class StartupUtil {
? "The application seems to be trans-located by macOS and cannot be used in this state.\n" +
"Please use Finder to move it to another location."
: "If you have modified the '" + property + "' property, please make sure it is correct,\n" +
- "otherwise please re-install the IDE.";
+ "otherwise, please re-install the IDE.";
String message = "The IDE " + problem + ".\nPossible reason: " + reason + ".\n\n" + advice +
"\n\n-----\nLocation: " + path + "\n" + e.getClass().getName() + ": " + e.getMessage();
Main.showMessage(title, message, true);
@@ -478,25 +429,13 @@ public final class StartupUtil {
}
}
- @NotNull
- private static synchronized SocketLock.ActivateStatusAndResponse lockSystemFolders(@NotNull String[] args) {
- if (ourSocketLock != null) {
- throw new AssertionError();
- }
-
+ private static synchronized void lockSystemDirs(String[] args) throws IOException {
+ if (ourSocketLock != null) throw new AssertionError();
ourSocketLock = new SocketLock(PathManager.getConfigPath(), PathManager.getSystemPath());
- SocketLock.ActivateStatusAndResponse status;
- try {
- status = ourSocketLock.lockAndTryActivate(args);
- }
- catch (Exception e) {
- Main.showMessage("Cannot Lock System Folders", e);
- return SocketLock.ActivateStatusAndResponse.emptyResponse(SocketLock.ActivateStatus.CANNOT_ACTIVATE);
- }
-
- switch (status.getActivateStatus()) {
- case NO_INSTANCE:
+ Pair status = ourSocketLock.lockAndTryActivate(args);
+ switch (status.first) {
+ case NO_INSTANCE: {
ShutDownTracker.getInstance().registerShutdownTask(() -> {
//noinspection SynchronizeOnThis
synchronized (StartupUtil.class) {
@@ -505,15 +444,33 @@ public final class StartupUtil {
}
});
break;
- case ACTIVATED:
+ }
+
+ case ACTIVATED: {
+ CliResult result = status.second;
+ String message = result.getMessage();
+ if (message == null) message = "Already running";
//noinspection UseOfSystemOutOrSystemErr
- System.out.println("Already running");
- break;
- case CANNOT_ACTIVATE:
+ System.out.println(message);
+ System.exit(result.getReturnCode());
+ }
+
+ case CANNOT_ACTIVATE: {
String message = "Only one instance of " + ApplicationNamesInfo.getInstance().getProductName() + " can be run at a time.";
Main.showMessage("Too Many Instances", message, true);
+ System.exit(Main.INSTANCE_CHECK_FAILED);
+ }
}
- return status;
+ }
+
+ private static Logger setupLogger() {
+ Logger.setFactory(new LoggerFactory());
+ Logger log = Logger.getInstance(Main.class);
+ log.info("------------------------------------------------------ IDE STARTED ------------------------------------------------------");
+ ShutDownTracker.getInstance().registerShutdownTask(() -> {
+ log.info("------------------------------------------------------ IDE SHUTDOWN ------------------------------------------------------");
+ });
+ return log;
}
private static void fixProcessEnvironment(Logger log) {
@@ -526,7 +483,6 @@ public final class StartupUtil {
}
}
- @SuppressWarnings("SpellCheckingInspection")
private static void setupSystemLibraries() {
String ideTempPath = PathManager.getTempPath();
@@ -647,7 +603,7 @@ public final class StartupUtil {
//
// At the same time, http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6209673 seems to be now fixed.
//
- // This matters only if swing.bufferPerWindow = true and we don't invoke JComponent.getGraphics() directly.
+ // This matters only if {@code swing.bufferPerWindow = true} and we don't invoke JComponent.getGraphics() directly.
//
// True double buffering is needed to eliminate tearing on blit-accelerated scrolling and to restore
// frame buffer content without the usual repainting, even when the EDT is blocked.
@@ -699,8 +655,7 @@ public final class StartupUtil {
IconLoader.activate();
// we don't set AppUIUtil.updateForDarcula(false) because light is default
}
- catch (Exception ignore) {
- }
+ catch (Exception ignore) { }
});
}
diff --git a/platform/platform-main/src/com/intellij/idea/MainImpl.java b/platform/platform-main/src/com/intellij/idea/MainImpl.java
index 063dc1cac94d..77f4977227f2 100644
--- a/platform/platform-main/src/com/intellij/idea/MainImpl.java
+++ b/platform/platform-main/src/com/intellij/idea/MainImpl.java
@@ -11,9 +11,7 @@ import java.util.concurrent.Future;
public final class MainImpl {
private MainImpl() { }
- /**
- * Called from PluginManager via reflection.
- */
+ /** Called via reflection from {@code MainRunner#start}. */
public static void start(@NotNull String[] args) throws Exception {
System.setProperty(PlatformUtils.PLATFORM_PREFIX_KEY, PlatformUtils.getPlatformPrefix(PlatformUtils.IDEA_CE_PREFIX));
diff --git a/platform/platform-tests/testSrc/com/intellij/idea/LockSupportTest.java b/platform/platform-tests/testSrc/com/intellij/idea/LockSupportTest.java
index 2baa4d218f0a..a447ff3624b2 100644
--- a/platform/platform-tests/testSrc/com/intellij/idea/LockSupportTest.java
+++ b/platform/platform-tests/testSrc/com/intellij/idea/LockSupportTest.java
@@ -1,9 +1,10 @@
-// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.idea;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.testFramework.rules.TempDirectory;
-import org.jetbrains.annotations.NotNull;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.TimeoutUtil;
import org.junit.Rule;
import org.junit.Test;
@@ -29,8 +30,8 @@ public class LockSupportTest {
SocketLock lock1 = new SocketLock(path1 + "/c", path1 + "/s");
SocketLock lock2 = new SocketLock(path2 + "/c", path2 + "/s");
try {
- assertEquals(SocketLock.ActivateStatus.NO_INSTANCE, tryActivate(lock1));
- assertEquals(SocketLock.ActivateStatus.ACTIVATED, tryActivate(lock2));
+ assertEquals(SocketLock.ActivationStatus.NO_INSTANCE, tryActivate(lock1));
+ assertEquals(SocketLock.ActivationStatus.ACTIVATED, tryActivate(lock2));
}
finally {
lock1.dispose();
@@ -42,7 +43,7 @@ public class LockSupportTest {
public void testLock() throws Exception {
SocketLock lock = new SocketLock(tempDir.getRoot().getPath() + "/c", tempDir.getRoot().getPath() + "/s");
try {
- assertEquals(SocketLock.ActivateStatus.NO_INSTANCE, lock.lockAndTryActivate().getActivateStatus());
+ assertEquals(SocketLock.ActivationStatus.NO_INSTANCE, lock.lockAndTryActivate(ArrayUtil.EMPTY_STRING_ARRAY).first);
}
finally {
lock.dispose();
@@ -53,41 +54,35 @@ public class LockSupportTest {
public void testTwoLocks() throws Exception {
List toClose = new ArrayList<>();
try {
- assertEquals(SocketLock.ActivateStatus.NO_INSTANCE, createLockAndTryActivate(toClose, tempDir.getRoot(), "c1", "s1"));
- assertEquals(SocketLock.ActivateStatus.NO_INSTANCE, createLockAndTryActivate(toClose, tempDir.getRoot(), "c2", "s2"));
- assertEquals(SocketLock.ActivateStatus.NO_INSTANCE, createLockAndTryActivate(toClose, tempDir.getRoot(), "c3", "s3"));
+ assertEquals(SocketLock.ActivationStatus.NO_INSTANCE, createLockAndTryActivate(toClose, tempDir.getRoot(), "c1", "s1"));
+ assertEquals(SocketLock.ActivationStatus.NO_INSTANCE, createLockAndTryActivate(toClose, tempDir.getRoot(), "c2", "s2"));
+ assertEquals(SocketLock.ActivationStatus.NO_INSTANCE, createLockAndTryActivate(toClose, tempDir.getRoot(), "c3", "s3"));
- assertEquals(SocketLock.ActivateStatus.ACTIVATED, createLockAndTryActivate(toClose, tempDir.getRoot(), "c1", "s1"));
- assertEquals(SocketLock.ActivateStatus.ACTIVATED, createLockAndTryActivate(toClose, tempDir.getRoot(), "c2", "s2"));
- assertEquals(SocketLock.ActivateStatus.ACTIVATED, createLockAndTryActivate(toClose, tempDir.getRoot(), "c3", "s3"));
+ assertEquals(SocketLock.ActivationStatus.ACTIVATED, createLockAndTryActivate(toClose, tempDir.getRoot(), "c1", "s1"));
+ assertEquals(SocketLock.ActivationStatus.ACTIVATED, createLockAndTryActivate(toClose, tempDir.getRoot(), "c2", "s2"));
+ assertEquals(SocketLock.ActivationStatus.ACTIVATED, createLockAndTryActivate(toClose, tempDir.getRoot(), "c3", "s3"));
}
finally {
toClose.forEach(SocketLock::dispose);
}
}
- @NotNull
- private static SocketLock.ActivateStatus createLockAndTryActivate(@NotNull List toClose, @NotNull File dir, @NotNull String cfg, @NotNull String sys)
- throws Exception {
- SocketLock lock = new SocketLock(dir.getPath() + "/" + cfg, dir.getPath() + "/" + sys);
+ private static SocketLock.ActivationStatus createLockAndTryActivate(List toClose, File dir, String cfg, String sys) throws Exception {
+ SocketLock lock = new SocketLock(dir.getPath() + '/' + cfg, dir.getPath() + '/' + sys);
toClose.add(lock);
-
return tryActivate(lock);
}
- @NotNull
- private static SocketLock.ActivateStatus tryActivate(@NotNull SocketLock lock) throws Exception {
- for (int attempt = 0; attempt < 10; attempt++) {
+ private static SocketLock.ActivationStatus tryActivate(SocketLock lock) throws Exception {
+ // on a same JVM, locking an already locked file results in `OverlappingFileLockException` instead of blocking
+ while (true) {
try {
- return lock.lockAndTryActivate().getActivateStatus();
+ return lock.lockAndTryActivate(ArrayUtil.EMPTY_STRING_ARRAY).first;
}
- // The case of creating two parallel locks on one JVM
catch (OverlappingFileLockException e) {
- //noinspection BusyWait
- Thread.sleep(1000);
+ TimeoutUtil.sleep(10);
}
}
- throw new AssertionError("Count not finish waiting for the config/system files to unlock");
}
@Test(timeout = 30000)
@@ -95,17 +90,17 @@ public class LockSupportTest {
SocketLock lock1 = new SocketLock(tempDir.getRoot().getPath() + "/c", tempDir.getRoot().getPath() + "/s");
SocketLock lock2 = new SocketLock(tempDir.getRoot().getPath() + "/c", tempDir.getRoot().getPath() + "/s");
- assertEquals(SocketLock.ActivateStatus.NO_INSTANCE, tryActivate(lock1));
- assertEquals(SocketLock.ActivateStatus.ACTIVATED, tryActivate(lock2));
+ assertEquals(SocketLock.ActivationStatus.NO_INSTANCE, tryActivate(lock1));
+ assertEquals(SocketLock.ActivationStatus.ACTIVATED, tryActivate(lock2));
lock1.dispose();
- assertEquals(SocketLock.ActivateStatus.NO_INSTANCE, tryActivate(lock2));
+ assertEquals(SocketLock.ActivationStatus.NO_INSTANCE, tryActivate(lock2));
lock2.dispose();
}
@Test(timeout = 30000, expected = IllegalArgumentException.class)
public void testPathCollision() {
String path = tempDir.getRoot().getPath() + "/d";
- new SocketLock(path, path).dispose();
+ new SocketLock(path, path);
}
}
\ No newline at end of file
diff --git a/platform/util/src/com/intellij/diagnostic/ActivityImpl.java b/platform/util/src/com/intellij/diagnostic/ActivityImpl.java
index 3960b8529b33..42cc14abc1ed 100644
--- a/platform/util/src/com/intellij/diagnostic/ActivityImpl.java
+++ b/platform/util/src/com/intellij/diagnostic/ActivityImpl.java
@@ -79,7 +79,7 @@ public final class ActivityImpl implements Activity {
return parallelActivity;
}
- // and how do we can sort correctly, when parent item equals to child (start and end) and also there is another child with start equals to end?
+ // and how do we can sort correctly, when parent item equals to child (start and end), also there is another child with start equals to end?
// so, parent added to API but as it was not enough, decided to measure time in nanoseconds instead of ms to mitigate such situations
@Override
@NotNull
@@ -124,7 +124,7 @@ public final class ActivityImpl implements Activity {
if (description != null) {
this.description = description;
}
- assert end == 0;
+ assert end == 0 : "not started or already ended";
end = System.nanoTime();
StartUpMeasurer.add(this);
}
@@ -140,4 +140,4 @@ public final class ActivityImpl implements Activity {
public String toString() {
return "ActivityImpl(name=" + name + ", start=" + TimeUnit.NANOSECONDS.toMillis(start) + ", end=" + TimeUnit.NANOSECONDS.toMillis(end) + ")";
}
-}
+}
\ No newline at end of file
diff --git a/platform/util/src/com/intellij/openapi/application/JetBrainsProtocolHandler.java b/platform/util/src/com/intellij/openapi/application/JetBrainsProtocolHandler.java
index 37536506462b..7469cd149a26 100644
--- a/platform/util/src/com/intellij/openapi/application/JetBrainsProtocolHandler.java
+++ b/platform/util/src/com/intellij/openapi/application/JetBrainsProtocolHandler.java
@@ -20,10 +20,10 @@ import java.util.Map;
public final class JetBrainsProtocolHandler {
public static final String PROTOCOL = "jetbrains://";
public static final String FRAGMENT_PARAM_NAME = "__fragment";
+ public static final String REQUIRED_PLUGINS_KEY = "idea.required.plugins.id";
private static String ourMainParameter = null;
private static String ourCommand = null;
- public static final String REQUIRED_PLUGINS_KEY = "idea.required.plugins.id";
private static Map ourParameters = Collections.emptyMap();
private static boolean initialized = false;
@@ -111,4 +111,14 @@ public final class JetBrainsProtocolHandler {
init();
return ourParameters;
}
-}
+
+ public static boolean isShutdownCommand() {
+ return "shutdown".equals(getCommand());
+ }
+
+ @NotNull
+ public static String[] checkForJetBrainsProtocolCommand(@NotNull String[] args) {
+ String property = System.getProperty(JetBrainsProtocolHandler.class.getName());
+ return property != null ? new String[]{property} : args;
+ }
+}
\ No newline at end of file
diff --git a/platform/util/ui/src/com/intellij/util/ui/EdtInvocationManager.java b/platform/util/ui/src/com/intellij/util/ui/EdtInvocationManager.java
index a57c84d7d630..b05d68914e39 100644
--- a/platform/util/ui/src/com/intellij/util/ui/EdtInvocationManager.java
+++ b/platform/util/ui/src/com/intellij/util/ui/EdtInvocationManager.java
@@ -9,13 +9,13 @@ import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.atomic.AtomicReference;
/**
- * Encapsulates EDT-related checks and processing. The general idea is that intellij threading model is tightly bound with EDT
+ * Encapsulates EDT-related checks and processing. The general idea is that IntelliJ threading model is tightly bound with EDT
* (e.g. write access is allowed from EDT only and any task executed from EDT is implicitly granted read access). That makes
- * a huge bottleneck in non-intellij environments like upsource - every vcs revision there is represented as a separate ide project
- * object, hence, global shared write lock and single EDT become a problem.
- *
- * That's why it should be possible to change that model in non-intellij environment - that involves either custom read/write locks
- * processing or custom EDT processing as well. This interface covers EDT part.
+ * a huge bottleneck in non-IntelliJ environments like Upsource - every vcs revision there is represented as a separate ide project
+ * object, hence, global shared write lock and single EDT become a problem.
+ *
+ * That's why it should be possible to change that model in non-IntelliJ environment - that involves either custom read/write locks
+ * processing or custom EDT processing as well. This interface covers EDT part.
*/
public abstract class EdtInvocationManager {
private static final AtomicReference ourInstance = new AtomicReference<>();
@@ -38,7 +38,7 @@ public abstract class EdtInvocationManager {
return result;
}
- @SuppressWarnings("unused") // Used in upsource
+ @SuppressWarnings("unused") // Used in Upsource
public static void setEdtInvocationManager(@NotNull EdtInvocationManager edtInvocationManager) {
ourInstance.set(edtInvocationManager);
}
@@ -78,7 +78,7 @@ public abstract class EdtInvocationManager {
}
/**
- * The default {@link EdtInvocationManager} implementation which works with the EDT via SwingUtilities.
+ * The default {@link EdtInvocationManager} implementation that uses {@link EventQueue}.
*/
public static class SwingEdtInvocationManager extends EdtInvocationManager {
@Override
@@ -88,7 +88,6 @@ public abstract class EdtInvocationManager {
@Override
public void invokeLater(@NotNull Runnable task) {
- //noinspection SSBasedInspection
EventQueue.invokeLater(task);
}
@@ -97,4 +96,4 @@ public abstract class EdtInvocationManager {
EventQueue.invokeAndWait(task);
}
}
-}
+}
\ No newline at end of file