Cleanup (daemon test to use existing API instead of impl. method; formatting; typos)
[idea/community.git] / platform / platform-impl / src / com / intellij / openapi / application / impl / ApplicationImpl.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.openapi.application.impl;
17
18 import com.intellij.BundleBase;
19 import com.intellij.CommonBundle;
20 import com.intellij.diagnostic.ThreadDumper;
21 import com.intellij.ide.*;
22 import com.intellij.ide.plugins.IdeaPluginDescriptor;
23 import com.intellij.ide.plugins.PluginManagerCore;
24 import com.intellij.idea.IdeaApplication;
25 import com.intellij.idea.Main;
26 import com.intellij.idea.StartupUtil;
27 import com.intellij.openapi.Disposable;
28 import com.intellij.openapi.application.*;
29 import com.intellij.openapi.application.ex.ApplicationEx;
30 import com.intellij.openapi.command.CommandProcessor;
31 import com.intellij.openapi.components.StateStorageException;
32 import com.intellij.openapi.components.impl.ApplicationPathMacroManager;
33 import com.intellij.openapi.components.impl.PlatformComponentManagerImpl;
34 import com.intellij.openapi.components.impl.stores.IApplicationStore;
35 import com.intellij.openapi.components.impl.stores.IComponentStore;
36 import com.intellij.openapi.components.impl.stores.StoreUtil;
37 import com.intellij.openapi.components.impl.stores.StoresFactory;
38 import com.intellij.openapi.diagnostic.Logger;
39 import com.intellij.openapi.extensions.ExtensionPointName;
40 import com.intellij.openapi.extensions.Extensions;
41 import com.intellij.openapi.fileEditor.FileDocumentManager;
42 import com.intellij.openapi.progress.EmptyProgressIndicator;
43 import com.intellij.openapi.progress.ProcessCanceledException;
44 import com.intellij.openapi.progress.ProgressIndicator;
45 import com.intellij.openapi.progress.ProgressManager;
46 import com.intellij.openapi.progress.util.ProgressWindow;
47 import com.intellij.openapi.project.Project;
48 import com.intellij.openapi.project.ProjectManager;
49 import com.intellij.openapi.project.ex.ProjectEx;
50 import com.intellij.openapi.project.ex.ProjectManagerEx;
51 import com.intellij.openapi.project.impl.ProjectManagerImpl;
52 import com.intellij.openapi.ui.DialogWrapper;
53 import com.intellij.openapi.ui.MessageDialogBuilder;
54 import com.intellij.openapi.ui.Messages;
55 import com.intellij.openapi.util.*;
56 import com.intellij.openapi.util.io.FileUtil;
57 import com.intellij.openapi.util.text.StringUtil;
58 import com.intellij.openapi.vfs.CharsetToolkit;
59 import com.intellij.openapi.wm.IdeFrame;
60 import com.intellij.openapi.wm.WindowManager;
61 import com.intellij.psi.PsiLock;
62 import com.intellij.ui.Splash;
63 import com.intellij.util.*;
64 import com.intellij.util.containers.Stack;
65 import com.intellij.util.io.storage.HeavyProcessLatch;
66 import com.intellij.util.ui.UIUtil;
67 import org.jetbrains.annotations.NonNls;
68 import org.jetbrains.annotations.NotNull;
69 import org.jetbrains.annotations.Nullable;
70 import org.jetbrains.annotations.TestOnly;
71 import org.jetbrains.ide.PooledThreadExecutor;
72 import org.picocontainer.MutablePicoContainer;
73
74 import javax.swing.*;
75 import java.awt.*;
76 import java.io.File;
77 import java.io.IOException;
78 import java.lang.reflect.Method;
79 import java.util.List;
80 import java.util.concurrent.Callable;
81 import java.util.concurrent.ExecutorService;
82 import java.util.concurrent.Future;
83 import java.util.concurrent.TimeUnit;
84 import java.util.concurrent.atomic.AtomicBoolean;
85 import java.util.concurrent.locks.ReentrantReadWriteLock;
86
87 public class ApplicationImpl extends PlatformComponentManagerImpl implements ApplicationEx {
88   private static final Logger LOG = Logger.getInstance("#com.intellij.application.impl.ApplicationImpl");
89   private final ModalityState MODALITY_STATE_NONE = ModalityState.NON_MODAL;
90
91   // about writer preference: the way the j.u.c.l.ReentrantReadWriteLock.NonfairSync is implemented, the
92   // writer thread will be always at the queue head and therefore, j.u.c.l.ReentrantReadWriteLock.NonfairSync.readerShouldBlock()
93   // will return true if the write action is pending, exactly as we need
94   private final ReentrantReadWriteLock myLock = new ReentrantReadWriteLock(false);
95
96   private final ModalityInvokator myInvokator = new ModalityInvokatorImpl();
97
98   private final EventDispatcher<ApplicationListener> myDispatcher = EventDispatcher.create(ApplicationListener.class);
99
100   private IApplicationStore myComponentStore;
101
102   private final boolean myTestModeFlag;
103   private final boolean myHeadlessMode;
104   private final boolean myCommandLineMode;
105
106   private final boolean myIsInternal;
107   private final String myName;
108
109   private final Stack<Class> myWriteActionsStack = new Stack<Class>(); // accessed from EDT only, no need to sync
110
111   private int myInEditorPaintCounter; // EDT only
112   private final long myStartTime;
113   @Nullable
114   private final Splash mySplash;
115   private boolean myDoNotSave;
116   private volatile boolean myDisposeInProgress;
117
118   private final Disposable myLastDisposable = Disposer.newDisposable(); // will be disposed last
119
120   private final AtomicBoolean mySaveSettingsIsInProgress = new AtomicBoolean(false);
121
122   private final ExecutorService ourThreadExecutorsService = PooledThreadExecutor.INSTANCE;
123   private boolean myIsFiringLoadingEvent = false;
124   private boolean myLoaded = false;
125   @NonNls private static final String WAS_EVER_SHOWN = "was.ever.shown";
126
127   private Boolean myActive;
128
129   private static final int IS_EDT_FLAG = 1<<30; // we don't mess with sign bit since we want to do arithmetic
130   private static final int IS_READ_LOCK_ACQUIRED_FLAG = 1<<29;
131   private static class Status {
132     // higher three bits are for IS_* flags
133     // lower bits are for edtSafe counter
134     private int flags;
135   }
136
137   private static final ThreadLocal<Status> status = new ThreadLocal<Status>(){
138     @Override
139     protected Status initialValue() {
140       Status status = new Status();
141       status.flags = BitUtil.set(status.flags, IS_EDT_FLAG, EventQueue.isDispatchThread());
142       return status;
143     }
144   };
145   private static Status getStatus() {
146     return status.get();
147   }
148   private static void setReadLockAcquired(Status status, boolean acquired) {
149     status.flags = BitUtil.set(status.flags, IS_READ_LOCK_ACQUIRED_FLAG, acquired);
150   }
151
152   private static final ModalityState ANY = new ModalityState() {
153     @Override
154     public boolean dominates(@NotNull ModalityState anotherState) {
155       return false;
156     }
157
158     @NonNls
159     @Override
160     public String toString() {
161       return "ANY";
162     }
163   };
164
165   @Override
166   protected void bootstrapPicoContainer(@NotNull String name) {
167     super.bootstrapPicoContainer(name);
168     getPicoContainer().registerComponentImplementation(IComponentStore.class, StoresFactory.getApplicationStoreClass());
169     getPicoContainer().registerComponentImplementation(ApplicationPathMacroManager.class);
170   }
171
172   @NotNull
173   public synchronized IApplicationStore getStateStore() {
174     if (myComponentStore == null) {
175       myComponentStore = (IApplicationStore)getPicoContainer().getComponentInstance(IComponentStore.class);
176     }
177     return myComponentStore;
178   }
179
180   @Override
181   public void initializeComponent(@NotNull Object component, boolean service) {
182     getStateStore().initComponent(component, service);
183   }
184
185   public ApplicationImpl(boolean isInternal,
186                          boolean isUnitTestMode,
187                          boolean isHeadless,
188                          boolean isCommandLine,
189                          @NotNull String appName,
190                          @Nullable Splash splash) {
191     super(null);
192
193     ApplicationManager.setApplication(this, myLastDisposable); // reset back to null only when all components already disposed
194
195     getPicoContainer().registerComponentInstance(Application.class, this);
196
197     BundleBase.assertKeyIsFound = IconLoader.STRICT = isUnitTestMode || isInternal;
198
199     AWTExceptionHandler.register(); // do not crash AWT on exceptions
200
201     String debugDisposer = System.getProperty("idea.disposer.debug");
202     Disposer.setDebugMode((isInternal || isUnitTestMode || "on".equals(debugDisposer)) && !"off".equals(debugDisposer));
203
204     myStartTime = System.currentTimeMillis();
205     mySplash = splash;
206     myName = appName;
207
208     myIsInternal = isInternal;
209     myTestModeFlag = isUnitTestMode;
210     myHeadlessMode = isHeadless;
211     myCommandLineMode = isCommandLine;
212
213     myDoNotSave = isUnitTestMode || isHeadless;
214
215     loadApplicationComponents();
216
217     if (myTestModeFlag) {
218       registerShutdownHook();
219     }
220
221     if (!isUnitTestMode && !isHeadless) {
222       Disposer.register(this, Disposer.newDisposable(), "ui");
223
224       StartupUtil.addExternalInstanceListener(new Consumer<List<String>>() {
225         @Override
226         public void consume(final List<String> args) {
227           invokeLater(new Runnable() {
228             @Override
229             public void run() {
230               LOG.info("ApplicationImpl.externalInstanceListener invocation");
231               String currentDirectory = args.isEmpty() ? null : args.get(0);
232               List<String> realArgs = args.isEmpty() ? args : args.subList(1, args.size());
233               final Project project = CommandLineProcessor.processExternalCommandLine(realArgs, currentDirectory);
234               final JFrame frame;
235               if (project != null) {
236                 frame = (JFrame)WindowManager.getInstance().getIdeFrame(project);
237               }
238               else {
239                 frame = WindowManager.getInstance().findVisibleFrame();
240               }
241               if (frame != null) frame.requestFocus();
242             }
243           });
244         }
245       });
246
247       WindowsCommandLineProcessor.LISTENER = new WindowsCommandLineListener() {
248         @Override
249         public void processWindowsLauncherCommandLine(final String currentDirectory, final String commandLine) {
250           LOG.info("Received external Windows command line: current directory " + currentDirectory + ", command line " + commandLine);
251           invokeLater(new Runnable() {
252             @Override
253             public void run() {
254               final List<String> args = StringUtil.splitHonorQuotes(commandLine, ' ');
255               args.remove(0);   // process name
256               CommandLineProcessor.processExternalCommandLine(args, currentDirectory);
257             }
258           });
259         }
260       };
261     }
262     if (isUnitTestMode && IdeaApplication.getInstance() == null) {
263       String[] args = {"inspect", "", "", ""};
264       Main.setFlags(args); // set both isHeadless and isCommandLine to true
265       System.setProperty(IdeaApplication.IDEA_IS_UNIT_TEST, Boolean.TRUE.toString());
266       assert Main.isHeadless();
267       assert Main.isCommandLine();
268       new IdeaApplication(args);
269     }
270   }
271
272   private void registerShutdownHook() {
273     ShutDownTracker.getInstance().registerShutdownTask(new Runnable() {
274       @Override
275       public void run() {
276         if (isDisposed() || myDisposeInProgress) {
277           return;
278         }
279         ShutDownTracker.invokeAndWait(isUnitTestMode(), true, new Runnable() {
280           @Override
281           public void run() {
282             if (ApplicationManager.getApplication() != ApplicationImpl.this) return;
283             try {
284               myDisposeInProgress = true;
285               saveAll();
286             }
287             finally {
288               if (!disposeSelf(true)) {
289                 myDisposeInProgress = false;
290               }
291             }
292           }
293         });
294       }
295     });
296   }
297
298   private boolean disposeSelf(final boolean checkCanCloseProject) {
299     final ProjectManagerImpl manager = (ProjectManagerImpl)ProjectManagerEx.getInstanceEx();
300     if (manager != null) {
301       final boolean[] canClose = {true};
302       for (final Project project : manager.getOpenProjects()) {
303         try {
304           CommandProcessor.getInstance().executeCommand(project, new Runnable() {
305             @Override
306             public void run() {
307               if (!manager.closeProject(project, true, true, checkCanCloseProject)) {
308                 canClose[0] = false;
309               }
310             }
311           }, ApplicationBundle.message("command.exit"), null);
312         }
313         catch (Throwable e) {
314           LOG.error(e);
315         }
316         if (!canClose[0]) {
317           return false;
318         }
319       }
320     }
321     runWriteAction(new Runnable() {
322       @Override
323       public void run() {
324         Disposer.dispose(ApplicationImpl.this);
325       }
326     });
327
328     Disposer.assertIsEmpty();
329     return true;
330   }
331
332   @Override
333   @NotNull
334   public String getName() {
335     return myName;
336   }
337
338   @Override
339   public boolean holdsReadLock() {
340     return holdsReadLock(getStatus());
341   }
342
343   private static boolean holdsReadLock(Status status) {
344     return BitUtil.isSet(status.flags, IS_READ_LOCK_ACQUIRED_FLAG);
345   }
346
347   private void loadApplicationComponents() {
348     PluginManagerCore.initPlugins(mySplash);
349     IdeaPluginDescriptor[] plugins = PluginManagerCore.getPlugins();
350     for (IdeaPluginDescriptor plugin : plugins) {
351       if (!PluginManagerCore.shouldSkipPlugin(plugin)) {
352         loadComponentsConfiguration(plugin.getAppComponents(), plugin, false);
353       }
354     }
355   }
356
357   @Override
358   protected synchronized Object createComponent(@NotNull Class componentInterface) {
359     Object component = super.createComponent(componentInterface);
360     if (mySplash != null) {
361       mySplash.showProgress("", 0.65f + getPercentageOfComponentsLoaded() * 0.35f);
362     }
363     return component;
364   }
365
366   @NotNull
367   @Override
368   protected MutablePicoContainer createPicoContainer() {
369     return Extensions.getRootArea().getPicoContainer();
370   }
371
372   @Override
373   public boolean isInternal() {
374     return myIsInternal;
375   }
376
377   @Override
378   public boolean isEAP() {
379     return ApplicationInfoImpl.getShadowInstance().isEAP();
380   }
381
382   @Override
383   public boolean isUnitTestMode() {
384     return myTestModeFlag;
385   }
386
387   @Override
388   public boolean isHeadlessEnvironment() {
389     return myHeadlessMode;
390   }
391
392   @Override
393   public boolean isCommandLine() {
394     return myCommandLineMode;
395   }
396
397   @NotNull
398   @Override
399   public Future<?> executeOnPooledThread(@NotNull final Runnable action) {
400     return ourThreadExecutorsService.submit(new Runnable() {
401       @Override
402       public void run() {
403         assert !isReadAccessAllowed(): describe(Thread.currentThread());
404         try {
405           action.run();
406         }
407         catch (ProcessCanceledException e) {
408           // ignore
409         }
410         catch (Throwable t) {
411           LOG.error(t);
412         }
413         finally {
414           //ReflectionUtil.resetThreadLocals();
415           Thread.interrupted(); // reset interrupted status
416           assert !isReadAccessAllowed(): describe(Thread.currentThread());
417         }
418       }
419     });
420   }
421
422   @NotNull
423   @Override
424   public <T> Future<T> executeOnPooledThread(@NotNull final Callable<T> action) {
425     return ourThreadExecutorsService.submit(new Callable<T>() {
426       @Override
427       public T call() {
428         assert !isReadAccessAllowed(): describe(Thread.currentThread());
429         try {
430           return action.call();
431         }
432         catch (ProcessCanceledException e) {
433           // ignore
434         }
435         catch (Throwable t) {
436           LOG.error(t);
437         }
438         finally {
439           //ReflectionUtil.resetThreadLocals();
440           Thread.interrupted(); // reset interrupted status
441           assert !isReadAccessAllowed(): describe(Thread.currentThread());
442         }
443         return null;
444       }
445     });
446   }
447
448   @Override
449   public boolean isDispatchThread() {
450     return isDispatchThread(getStatus());
451   }
452
453   private static boolean isDispatchThread(Status status) {
454     return BitUtil.isSet(status.flags, IS_EDT_FLAG);
455   }
456
457   @Override
458   @NotNull
459   public ModalityInvokator getInvokator() {
460     return myInvokator;
461   }
462
463
464   @Override
465   public void invokeLater(@NotNull final Runnable runnable) {
466     myInvokator.invokeLater(runnable);
467   }
468
469   @Override
470   public void invokeLater(@NotNull final Runnable runnable, @NotNull final Condition expired) {
471     myInvokator.invokeLater(runnable, expired);
472   }
473
474   @Override
475   public void invokeLater(@NotNull final Runnable runnable, @NotNull final ModalityState state) {
476     myInvokator.invokeLater(runnable, state);
477   }
478
479   @Override
480   public void invokeLater(@NotNull final Runnable runnable, @NotNull final ModalityState state, @NotNull final Condition expired) {
481     myInvokator.invokeLater(runnable, state, expired);
482   }
483
484   @Override
485   public void load(@Nullable String optionsPath) throws IOException {
486     load(PathManager.getConfigPath(), optionsPath == null ? PathManager.getOptionsPath() : optionsPath);
487   }
488
489   public void load(@NotNull String configPath, @NotNull String optionsPath) throws IOException {
490     IApplicationStore store = getStateStore();
491     store.setOptionsPath(optionsPath);
492     store.setConfigPath(configPath);
493
494     myIsFiringLoadingEvent = true;
495     try {
496       fireBeforeApplicationLoaded();
497     }
498     finally {
499       myIsFiringLoadingEvent = false;
500     }
501
502     AccessToken token = HeavyProcessLatch.INSTANCE.processStarted("Loading application components");
503     try {
504       store.load();
505     }
506     catch (StateStorageException e) {
507       throw new IOException(e.getMessage());
508     }
509     finally {
510       token.finish();
511     }
512     myLoaded = true;
513
514     createLocatorFile();
515   }
516
517   private static void createLocatorFile() {
518     File locatorFile = new File(PathManager.getSystemPath() + "/" + ApplicationEx.LOCATOR_FILE_NAME);
519     try {
520       byte[] data = PathManager.getHomePath().getBytes(CharsetToolkit.UTF8_CHARSET);
521       FileUtil.writeToFile(locatorFile, data);
522     }
523     catch (IOException e) {
524       LOG.warn("can't store a location in '" + locatorFile + "'", e);
525     }
526   }
527
528   @Override
529   public boolean isLoaded() {
530     return myLoaded;
531   }
532
533   @Override
534   protected <T> T getComponentFromContainer(@NotNull final Class<T> interfaceClass) {
535     if (myIsFiringLoadingEvent) {
536       return null;
537     }
538     return super.getComponentFromContainer(interfaceClass);
539   }
540
541   private void fireBeforeApplicationLoaded() {
542     for (ApplicationLoadListener listener : ApplicationLoadListener.EP_NAME.getExtensions()) {
543       try {
544         listener.beforeApplicationLoaded(this);
545       }
546       catch (Exception e) {
547         LOG.error(e);
548       }
549     }
550   }
551
552   @Override
553   public void dispose() {
554     fireApplicationExiting();
555
556     ShutDownTracker.getInstance().ensureStopperThreadsFinished();
557
558     disposeComponents();
559
560     ourThreadExecutorsService.shutdownNow();
561     myComponentStore = null;
562     super.dispose();
563     Disposer.dispose(myLastDisposable); // dispose it last
564   }
565
566   @Override
567   public boolean runProcessWithProgressSynchronously(@NotNull final Runnable process,
568                                                      @NotNull String progressTitle,
569                                                      boolean canBeCanceled,
570                                                      Project project) {
571     return runProcessWithProgressSynchronously(process, progressTitle, canBeCanceled, project, null);
572   }
573
574   @Override
575   public boolean runProcessWithProgressSynchronously(@NotNull final Runnable process,
576                                                      @NotNull final String progressTitle,
577                                                      final boolean canBeCanceled,
578                                                      @Nullable final Project project,
579                                                      final JComponent parentComponent) {
580     return runProcessWithProgressSynchronously(process, progressTitle, canBeCanceled, project, parentComponent, null);
581   }
582
583   @Override
584   public boolean runProcessWithProgressSynchronously(@NotNull final Runnable process,
585                                                      @NotNull final String progressTitle,
586                                                      final boolean canBeCanceled,
587                                                      @Nullable final Project project,
588                                                      final JComponent parentComponent,
589                                                      final String cancelText) {
590     assertIsDispatchThread();
591     boolean writeAccessAllowed = isInsideWriteActionEDTOnly();
592     if (writeAccessAllowed // Disallow running process in separate thread from under write action.
593                            // The thread will deadlock trying to get read action otherwise.
594         || isHeadlessEnvironment() && !isUnitTestMode()
595       ) {
596       LOG.debug("Starting process with progress from within write action makes no sense");
597       try {
598         ProgressManager.getInstance().runProcess(process, new EmptyProgressIndicator());
599       }
600       catch (ProcessCanceledException e) {
601         // ok to ignore.
602         return false;
603       }
604       return true;
605     }
606
607     final ProgressWindow progress = new ProgressWindow(canBeCanceled, false, project, parentComponent, cancelText);
608     // in case of abrupt application exit when 'ProgressManager.getInstance().runProcess(process, progress)' below
609     // does not have a chance to run, and as a result the progress won't be disposed
610     Disposer.register(this, progress);
611
612     progress.setTitle(progressTitle);
613
614     final AtomicBoolean threadStarted = new AtomicBoolean();
615     //noinspection SSBasedInspection
616     SwingUtilities.invokeLater(new Runnable() {
617       @Override
618       public void run() {
619         executeOnPooledThread(new Runnable() {
620           @Override
621           public void run() {
622             try {
623               ProgressManager.getInstance().runProcess(process, progress);
624             }
625             catch (ProcessCanceledException e) {
626               progress.cancel();
627               // ok to ignore.
628             }
629             catch (RuntimeException e) {
630               progress.cancel();
631               throw e;
632             }
633           }
634         });
635         threadStarted.set(true);
636       }
637     });
638
639     progress.startBlocking();
640
641     LOG.assertTrue(threadStarted.get());
642     LOG.assertTrue(!progress.isRunning());
643
644     return !progress.isCanceled();
645   }
646
647   @Override
648   public void invokeAndWait(@NotNull Runnable runnable, @NotNull ModalityState modalityState) {
649     Status status = getStatus();
650     if (isDispatchThread(status)) {
651       runnable.run();
652       return;
653     }
654
655     if (holdsReadLock()) {
656       LOG.error("Calling invokeAndWait from read-action leads to possible deadlock.");
657     }
658
659     LaterInvocator.invokeAndWait(runnable, modalityState);
660   }
661
662   @Override
663   @NotNull
664   public ModalityState getCurrentModalityState() {
665     Object[] entities = LaterInvocator.getCurrentModalEntities();
666     return entities.length > 0 ? new ModalityStateEx(entities) : getNoneModalityState();
667   }
668
669   @Override
670   @NotNull
671   public ModalityState getModalityStateForComponent(@NotNull Component c) {
672     Window window = UIUtil.getWindow(c);
673     if (window == null) return getNoneModalityState(); //?
674     return LaterInvocator.modalityStateForWindow(window);
675   }
676
677   @Override
678   @NotNull
679   public ModalityState getAnyModalityState() {
680     return ANY;
681   }
682
683   @Override
684   @NotNull
685   public ModalityState getDefaultModalityState() {
686     if (isDispatchThread()) {
687       return getCurrentModalityState();
688     }
689     ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
690     return progress == null ? getNoneModalityState() : progress.getModalityState();
691   }
692
693   @Override
694   @NotNull
695   public ModalityState getNoneModalityState() {
696     return MODALITY_STATE_NONE;
697   }
698
699   @Override
700   public long getStartTime() {
701     return myStartTime;
702   }
703
704   @Override
705   public long getIdleTime() {
706     assertIsDispatchThread();
707     return IdeEventQueue.getInstance().getIdleTime();
708   }
709
710   @Override
711   public void exit() {
712     exit(false, false);
713   }
714
715   @Override
716   public void exit(boolean force, final boolean exitConfirmed) {
717     exit(false, exitConfirmed, true, false);
718   }
719
720   @Override
721   public void restart() {
722     restart(false);
723   }
724
725   @Override
726   public void restart(final boolean exitConfirmed) {
727     exit(false, exitConfirmed, true, true);
728   }
729
730   /*
731    * There are two ways we can get an exit notification.
732    *  1. From user input i.e. ExitAction
733    *  2. From the native system.
734    *  We should not process any quit notifications if we are handling another one
735    *
736    *  Note: there are possible scenarios when we get a quit notification at a moment when another
737    *  quit message is shown. In that case, showing multiple messages sounds contra-intuitive as well
738    */
739   private static volatile boolean exiting = false;
740
741   public void exit(final boolean force, final boolean exitConfirmed, final boolean allowListenersToCancel, final boolean restart) {
742     if (!force && exiting) {
743       return;
744     }
745
746     exiting = true;
747     try {
748       if (!force && !exitConfirmed && getDefaultModalityState() != ModalityState.NON_MODAL) {
749         return;
750       }
751
752       Runnable runnable = new Runnable() {
753         @Override
754         public void run() {
755           if (!force && !confirmExitIfNeeded(exitConfirmed)) {
756             saveAll();
757             return;
758           }
759
760           getMessageBus().syncPublisher(AppLifecycleListener.TOPIC).appClosing();
761           myDisposeInProgress = true;
762           doExit(allowListenersToCancel, restart);
763           myDisposeInProgress = false;
764         }
765       };
766
767       if (isDispatchThread()) {
768         runnable.run();
769       }
770       else {
771         invokeLater(runnable, ModalityState.NON_MODAL);
772       }
773     }
774     finally {
775       exiting = false;
776     }
777   }
778
779   private boolean doExit(boolean allowListenersToCancel, boolean restart) {
780     saveSettings();
781
782     if (allowListenersToCancel && !canExit()) {
783       return false;
784     }
785
786     final boolean success = disposeSelf(allowListenersToCancel);
787     if (!success || isUnitTestMode()) {
788       return false;
789     }
790
791     int exitCode = 0;
792     if (restart && Restarter.isSupported()) {
793       try {
794         exitCode = Restarter.scheduleRestart();
795       }
796       catch (IOException e) {
797         LOG.warn("Cannot restart", e);
798       }
799     }
800     System.exit(exitCode);
801     return true;
802   }
803
804   private static boolean confirmExitIfNeeded(boolean exitConfirmed) {
805     final boolean hasUnsafeBgTasks = ProgressManager.getInstance().hasUnsafeProgressIndicator();
806     if (exitConfirmed && !hasUnsafeBgTasks) {
807       return true;
808     }
809
810     DialogWrapper.DoNotAskOption option = new DialogWrapper.DoNotAskOption() {
811       @Override
812       public boolean isToBeShown() {
813         return GeneralSettings.getInstance().isConfirmExit() && ProjectManager.getInstance().getOpenProjects().length > 0;
814       }
815
816       @Override
817       public void setToBeShown(boolean value, int exitCode) {
818         GeneralSettings.getInstance().setConfirmExit(value);
819       }
820
821       @Override
822       public boolean canBeHidden() {
823         return !hasUnsafeBgTasks;
824       }
825
826       @Override
827       public boolean shouldSaveOptionsOnCancel() {
828         return false;
829       }
830
831       @NotNull
832       @Override
833       public String getDoNotShowMessage() {
834         return "Do not ask me again";
835       }
836     };
837
838     if (hasUnsafeBgTasks || option.isToBeShown()) {
839       String message = ApplicationBundle
840         .message(hasUnsafeBgTasks ? "exit.confirm.prompt.tasks" : "exit.confirm.prompt",
841                  ApplicationNamesInfo.getInstance().getFullProductName());
842
843       if (MessageDialogBuilder.yesNo(ApplicationBundle.message("exit.confirm.title"), message).yesText(ApplicationBundle.message("command.exit")).noText(CommonBundle.message("button.cancel"))
844             .doNotAsk(option).show() != Messages.YES) {
845         return false;
846       }
847     }
848     return true;
849   }
850
851   private boolean canExit() {
852     for (ApplicationListener applicationListener : myDispatcher.getListeners()) {
853       if (!applicationListener.canExitApplication()) {
854         return false;
855       }
856     }
857
858     ProjectManagerEx projectManager = (ProjectManagerEx)ProjectManager.getInstance();
859     Project[] projects = projectManager.getOpenProjects();
860     for (Project project : projects) {
861       if (!projectManager.canClose(project)) {
862         return false;
863       }
864     }
865
866     return true;
867   }
868
869   @Override
870   public void runReadAction(@NotNull final Runnable action) {
871     Status status = getStatus();
872     if (isReadAccessAllowed(status)) {
873       action.run();
874     }
875     else {
876       startRead(status);
877       try {
878         action.run();
879       }
880       finally {
881         endRead(status);
882       }
883     }
884   }
885
886   @Override
887   public <T> T runReadAction(@NotNull final Computable<T> computation) {
888     Status status = getStatus();
889     if (isReadAccessAllowed(status)) {
890       return computation.compute();
891     }
892     startRead(status);
893     try {
894       return computation.compute();
895     }
896     finally {
897       endRead(status);
898     }
899   }
900
901   @Override
902   public <T, E extends Throwable> T runReadAction(@NotNull ThrowableComputable<T, E> computation) throws E {
903     Status status = getStatus();
904     if (isReadAccessAllowed(status)) {
905       return computation.compute();
906     }
907     startRead(status);
908     try {
909       return computation.compute();
910     }
911     finally {
912       endRead(status);
913     }
914   }
915
916   private void startRead(Status status) {
917     assertNoPsiLock();
918     try {
919       myLock.readLock().lockInterruptibly();
920       setReadLockAcquired(status, true);
921     }
922     catch (InterruptedException e) {
923       throw new RuntimeInterruptedException(e);
924     }
925   }
926
927   private void endRead(Status status) {
928     setReadLockAcquired(status, false);
929     myLock.readLock().unlock();
930   }
931
932   @Override
933   public void runWriteAction(@NotNull final Runnable action) {
934     Class<? extends Runnable> clazz = action.getClass();
935     startWrite(clazz);
936     try {
937       action.run();
938     }
939     finally {
940       endWrite(clazz);
941     }
942   }
943
944   @Override
945   public <T> T runWriteAction(@NotNull final Computable<T> computation) {
946     Class<? extends Computable> clazz = computation.getClass();
947     startWrite(clazz);
948     try {
949       return computation.compute();
950     }
951     finally {
952       endWrite(clazz);
953     }
954   }
955
956   @Override
957   public <T, E extends Throwable> T runWriteAction(@NotNull ThrowableComputable<T, E> computation) throws E {
958     Class<? extends ThrowableComputable> clazz = computation.getClass();
959     startWrite(clazz);
960     try {
961       return computation.compute();
962     }
963     finally {
964       endWrite(clazz);
965     }
966   }
967
968   @Override
969   public boolean hasWriteAction(@Nullable Class<?> actionClass) {
970     assertIsDispatchThread();
971
972     for (int i = myWriteActionsStack.size() - 1; i >= 0; i--) {
973       Class action = myWriteActionsStack.get(i);
974       if (actionClass == action || action != null && actionClass != null && ReflectionUtil.isAssignable(actionClass, action)) return true;
975     }
976     return false;
977   }
978
979   @Override
980   public void assertReadAccessAllowed() {
981     if (!isReadAccessAllowed()) {
982       LOG.error(
983         "Read access is allowed from event dispatch thread or inside read-action only" +
984         " (see com.intellij.openapi.application.Application.runReadAction())",
985         "Current thread: " + describe(Thread.currentThread()), "; dispatch thread: " + EventQueue.isDispatchThread() +"; isDispatchThread(): "+isDispatchThread(),
986         "SystemEventQueueThread: " + describe(getEventQueueThread()));
987     }
988   }
989
990   @NonNls
991   private static String describe(Thread o) {
992     if (o == null) return "null";
993     return o + " " + System.identityHashCode(o);
994   }
995
996   private static Thread getEventQueueThread() {
997     EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue();
998     try {
999       Method method = ReflectionUtil.getDeclaredMethod(EventQueue.class, "getDispatchThread");
1000       return (Thread)method.invoke(eventQueue);
1001     }
1002     catch (Exception e) {
1003       throw new RuntimeException(e);
1004     }
1005   }
1006
1007   @Override
1008   public boolean isReadAccessAllowed() {
1009     return isReadAccessAllowed(getStatus());
1010   }
1011
1012   private static boolean isReadAccessAllowed(Status status) {
1013     return (status.flags & (IS_EDT_FLAG | IS_READ_LOCK_ACQUIRED_FLAG)) != 0;
1014   }
1015
1016   @Override
1017   public void assertIsDispatchThread() {
1018     assertIsDispatchThread(getStatus());
1019   }
1020
1021   private static void assertIsDispatchThread(Status status) {
1022     if (isDispatchThread(status)) return;
1023     if (ShutDownTracker.isShutdownHookRunning()) return;
1024     int safeCounter = getSafeCounter(status);
1025     if (safeCounter == 0) {
1026       assertIsDispatchThread(status, "Access is allowed from event dispatch thread only.");
1027     }
1028   }
1029
1030   private static int getSafeCounter(Status status) {
1031     return status.flags & 0x1fffffff;
1032   }
1033
1034   private static void assertIsDispatchThread(Status status, @NotNull String message) {
1035     if (isDispatchThread(status)) return;
1036     LOG.error(message,
1037               "EventQueue.isDispatchThread()="+EventQueue.isDispatchThread(),
1038               "isDispatchThread()="+isDispatchThread(getStatus()),
1039               "Toolkit.getEventQueue()="+Toolkit.getDefaultToolkit().getSystemEventQueue(),
1040               "Current thread: " + describe(Thread.currentThread()),
1041               "SystemEventQueueThread: " + describe(getEventQueueThread()) +"\n"+ ThreadDumper.dumpThreadsToString()+"\n-----------");
1042   }
1043
1044   @Override
1045   public void runEdtSafeAction(@NotNull Runnable runnable) {
1046     Status status = getStatus();
1047     LOG.assertTrue(getSafeCounter(status) < 1<<26);
1048     status.flags++;
1049
1050     try {
1051       runnable.run();
1052     }
1053     finally {
1054       status.flags--;
1055     }
1056   }
1057
1058   @Override
1059   public void assertIsDispatchThread(@Nullable final JComponent component) {
1060     if (component == null) return;
1061
1062     Status status = getStatus();
1063     if (isDispatchThread(status)) {
1064       return;
1065     }
1066
1067     if (Boolean.TRUE.equals(component.getClientProperty(WAS_EVER_SHOWN))) {
1068       assertIsDispatchThread(status);
1069     }
1070     else {
1071       final JRootPane root = component.getRootPane();
1072       if (root != null) {
1073         component.putClientProperty(WAS_EVER_SHOWN, Boolean.TRUE);
1074         assertIsDispatchThread(status);
1075       }
1076     }
1077   }
1078
1079   @Override
1080   public void assertTimeConsuming() {
1081     if (myTestModeFlag || myHeadlessMode || ShutDownTracker.isShutdownHookRunning()) return;
1082     LOG.assertTrue(!isDispatchThread(), "This operation is time consuming and must not be called on EDT");
1083   }
1084
1085   @Override
1086   public boolean tryRunReadAction(@NotNull Runnable action) {
1087     Status status = getStatus();
1088     //if we are inside read action, do not try to acquire read lock again since it will deadlock if there is a pending writeAction
1089     boolean mustAcquire = !isReadAccessAllowed(status);
1090
1091     if (mustAcquire) {
1092       assertNoPsiLock();
1093       try {
1094         // timed version of tryLock() respects fairness unlike the no-args method
1095         if (!myLock.readLock().tryLock(0, TimeUnit.MILLISECONDS)) return false;
1096         setReadLockAcquired(status, true);
1097       }
1098       catch (InterruptedException e) {
1099         throw new RuntimeInterruptedException(e);
1100       }
1101     }
1102
1103     try {
1104       action.run();
1105     }
1106     finally {
1107       if (mustAcquire) {
1108         endRead(status);
1109       }
1110     }
1111     return true;
1112   }
1113
1114   public boolean tryToApplyActivationState(boolean active, Window window) {
1115     final Component frame = UIUtil.findUltimateParent(window);
1116
1117     if (frame instanceof IdeFrame) {
1118       final IdeFrame ideFrame = (IdeFrame)frame;
1119       if (isActive() != active) {
1120         myActive = active;
1121         System.setProperty("idea.active", myActive.toString());
1122         ApplicationActivationListener publisher = getMessageBus().syncPublisher(ApplicationActivationListener.TOPIC);
1123         if (active) {
1124           publisher.applicationActivated(ideFrame);
1125         }
1126         else {
1127           publisher.applicationDeactivated(ideFrame);
1128         }
1129         return true;
1130       }
1131     }
1132
1133     return false;
1134   }
1135
1136   @Override
1137   public boolean isActive() {
1138     if (isUnitTestMode()) return true;
1139
1140     if (myActive == null) {
1141       Window active = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
1142       return active != null;
1143     }
1144
1145     return myActive;
1146   }
1147
1148   @NotNull
1149   @Override
1150   public AccessToken acquireReadActionLock() {
1151     Status status = getStatus();
1152     // if we are inside read action, do not try to acquire read lock again since it will deadlock if there is a pending writeAction
1153     if (isReadAccessAllowed(status)) return AccessToken.EMPTY_ACCESS_TOKEN;
1154
1155     return new ReadAccessToken(status);
1156   }
1157
1158   private volatile boolean myWriteActionPending;
1159
1160   @Override
1161   public boolean isWriteActionPending() {
1162     return myWriteActionPending;
1163   }
1164
1165   private void startWrite(Class clazz) {
1166     boolean writeActionPending = myWriteActionPending;
1167     myWriteActionPending = true;
1168
1169     try {
1170       ActivityTracker.getInstance().inc();
1171       fireBeforeWriteActionStart(clazz);
1172
1173       try {
1174         if (!isWriteAccessAllowed()) {
1175           assertNoPsiLock();
1176         }
1177         if (!myLock.writeLock().tryLock()) {
1178           final AtomicBoolean lockAcquired = new AtomicBoolean(false);
1179           myLock.writeLock().lockInterruptibly();
1180           lockAcquired.set(true);
1181         }
1182       }
1183       catch (InterruptedException e) {
1184         throw new RuntimeInterruptedException(e);
1185       }
1186     }
1187     finally {
1188       myWriteActionPending = writeActionPending;
1189     }
1190
1191     myWriteActionsStack.push(clazz);
1192     fireWriteActionStarted(clazz);
1193   }
1194
1195   private void endWrite(Class clazz) {
1196     try {
1197       myWriteActionsStack.pop();
1198       fireWriteActionFinished(clazz);
1199     }
1200     finally {
1201       myLock.writeLock().unlock();
1202     }
1203   }
1204
1205   @NotNull
1206   @Override
1207   public AccessToken acquireWriteActionLock(Class clazz) {
1208     assertIsDispatchThread(getStatus(), "Write access is allowed from event dispatch thread only");
1209     return new WriteAccessToken(clazz);
1210   }
1211
1212   private class WriteAccessToken extends AccessToken {
1213     private final Class clazz;
1214
1215     public WriteAccessToken(Class clazz) {
1216       this.clazz = clazz;
1217       startWrite(clazz);
1218       markThreadNameInStackTrace();
1219       acquired();
1220     }
1221
1222     @Override
1223     public void finish() {
1224       try {
1225         endWrite(clazz);
1226       }
1227       finally {
1228         unmarkThreadNameInStackTrace();
1229         released();
1230       }
1231     }
1232
1233     private void markThreadNameInStackTrace() {
1234       String id = id();
1235
1236       if (id != null) {
1237         final Thread thread = Thread.currentThread();
1238         thread.setName(thread.getName() + id);
1239       }
1240     }
1241
1242     private void unmarkThreadNameInStackTrace() {
1243       String id = id();
1244
1245       if (id != null) {
1246         final Thread thread = Thread.currentThread();
1247         String name = thread.getName();
1248         name = StringUtil.replace(name, id, "");
1249         thread.setName(name);
1250       }
1251     }
1252
1253     private String id() {
1254       Class aClass = getClass();
1255       String name = aClass.getName();
1256       while (name == null) {
1257         aClass = aClass.getSuperclass();
1258         name = aClass.getName();
1259       }
1260
1261       name = name.substring(name.lastIndexOf('.') + 1);
1262       name = name.substring(name.lastIndexOf('$') + 1);
1263       if (!name.equals("AccessToken")) {
1264         return " [" + name+"]";
1265       }
1266       return null;
1267     }
1268   }
1269
1270   private class ReadAccessToken extends AccessToken {
1271     private final Status myStatus;
1272
1273     private ReadAccessToken(Status status) {
1274       myStatus = status;
1275       startRead(status);
1276       acquired();
1277     }
1278
1279     @Override
1280     public void finish() {
1281       endRead(myStatus);
1282       released();
1283     }
1284   }
1285
1286   private final boolean myExtraChecks = isUnitTestMode();
1287
1288   private void assertNoPsiLock() {
1289     if (myExtraChecks) {
1290       LOG.assertTrue(!Thread.holdsLock(PsiLock.LOCK), "Thread must not hold PsiLock while performing readAction");
1291     }
1292   }
1293
1294   @Override
1295   public void assertWriteAccessAllowed() {
1296     LOG.assertTrue(isWriteAccessAllowed(),
1297                    "Write access is allowed inside write-action only (see com.intellij.openapi.application.Application.runWriteAction())");
1298   }
1299
1300   @Override
1301   public boolean isWriteAccessAllowed() {
1302     return myLock.isWriteLockedByCurrentThread();
1303   }
1304
1305   // cheaper version of isWriteAccessAllowed(). must be called from EDT
1306   private boolean isInsideWriteActionEDTOnly() {
1307     return !myWriteActionsStack.isEmpty();
1308   }
1309
1310   @Override
1311   public boolean isWriteActionInProgress() {
1312     return myLock.isWriteLocked();
1313   }
1314
1315   public void editorPaintStart() {
1316     myInEditorPaintCounter++;
1317   }
1318
1319   public void editorPaintFinish() {
1320     myInEditorPaintCounter--;
1321     LOG.assertTrue(myInEditorPaintCounter >= 0);
1322   }
1323
1324   @Override
1325   public void addApplicationListener(@NotNull ApplicationListener l) {
1326     myDispatcher.addListener(l);
1327   }
1328
1329   @Override
1330   public void addApplicationListener(@NotNull ApplicationListener l, @NotNull Disposable parent) {
1331     myDispatcher.addListener(l, parent);
1332   }
1333
1334   @Override
1335   public void removeApplicationListener(@NotNull ApplicationListener l) {
1336     myDispatcher.removeListener(l);
1337   }
1338
1339   private void fireApplicationExiting() {
1340     myDispatcher.getMulticaster().applicationExiting();
1341   }
1342
1343   private void fireBeforeWriteActionStart(Class action) {
1344     myDispatcher.getMulticaster().beforeWriteActionStart(action);
1345   }
1346
1347   private void fireWriteActionStarted(Class action) {
1348     myDispatcher.getMulticaster().writeActionStarted(action);
1349   }
1350
1351   private void fireWriteActionFinished(Class action) {
1352     myDispatcher.getMulticaster().writeActionFinished(action);
1353   }
1354
1355   @Override
1356   public void saveSettings() {
1357     if (myDoNotSave) return;
1358
1359     if (mySaveSettingsIsInProgress.compareAndSet(false, true)) {
1360       try {
1361         StoreUtil.save(getStateStore(), null);
1362       }
1363       finally {
1364         mySaveSettingsIsInProgress.set(false);
1365       }
1366     }
1367   }
1368
1369   @Override
1370   public void saveAll() {
1371     if (myDoNotSave) return;
1372
1373     FileDocumentManager.getInstance().saveAllDocuments();
1374
1375     Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
1376     for (Project openProject : openProjects) {
1377       ProjectEx project = (ProjectEx)openProject;
1378       project.save();
1379     }
1380
1381     saveSettings();
1382   }
1383
1384   @Override
1385   public void doNotSave() {
1386     doNotSave(true);
1387   }
1388
1389   @Override
1390   public void doNotSave(boolean value) {
1391     myDoNotSave = value;
1392   }
1393
1394   @Override
1395   public boolean isDoNotSave() {
1396     return myDoNotSave;
1397   }
1398
1399   @NotNull
1400   @Override
1401   public <T> T[] getExtensions(@NotNull final ExtensionPointName<T> extensionPointName) {
1402     return Extensions.getRootArea().getExtensionPoint(extensionPointName).getExtensions();
1403   }
1404
1405   @Override
1406   public boolean isDisposeInProgress() {
1407     return myDisposeInProgress || ShutDownTracker.isShutdownHookRunning();
1408   }
1409
1410   @Override
1411   public boolean isRestartCapable() {
1412     return Restarter.isSupported();
1413   }
1414
1415   @Override
1416   protected boolean logSlowComponents() {
1417     return super.logSlowComponents() || ApplicationInfoImpl.getShadowInstance().isEAP();
1418   }
1419
1420   @TestOnly
1421   public void setDisposeInProgress(boolean disposeInProgress) {
1422     myDisposeInProgress = disposeInProgress;
1423   }
1424
1425   @NonNls
1426   @Override
1427   public String toString() {
1428     return "Application" +
1429            (isDisposed() ? " (Disposed)" : "") +
1430            (isUnitTestMode() ? " (Unit test)" : "") +
1431            (isInternal() ? " (Internal)" : "") +
1432            (isHeadlessEnvironment() ? " (Headless)" : "") +
1433            (isCommandLine() ? " (Command line)" : "");
1434   }
1435 }