910dfab558bdcb371e6e38118a53fcaa73b83743
[idea/community.git] / platform / platform-impl / src / com / intellij / openapi / application / impl / ApplicationImpl.java
1 /*
2  * Copyright 2000-2009 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.CommonBundle;
19 import com.intellij.Patches;
20 import com.intellij.diagnostic.PluginException;
21 import com.intellij.ide.ActivityTracker;
22 import com.intellij.ide.ApplicationLoadListener;
23 import com.intellij.ide.IdeEventQueue;
24 import com.intellij.ide.IdeRepaintManager;
25 import com.intellij.ide.impl.ProjectUtil;
26 import com.intellij.ide.plugins.IdeaPluginDescriptor;
27 import com.intellij.ide.plugins.PluginManager;
28 import com.intellij.openapi.Disposable;
29 import com.intellij.openapi.application.*;
30 import com.intellij.openapi.application.ex.ApplicationEx;
31 import com.intellij.openapi.application.ex.ApplicationManagerEx;
32 import com.intellij.openapi.command.CommandProcessor;
33 import com.intellij.openapi.components.RoamingType;
34 import com.intellij.openapi.components.StateStorage;
35 import com.intellij.openapi.components.impl.ApplicationPathMacroManager;
36 import com.intellij.openapi.components.impl.ComponentManagerImpl;
37 import com.intellij.openapi.components.impl.stores.*;
38 import com.intellij.openapi.diagnostic.Logger;
39 import com.intellij.openapi.extensions.ExtensionPoint;
40 import com.intellij.openapi.extensions.ExtensionPointName;
41 import com.intellij.openapi.extensions.Extensions;
42 import com.intellij.openapi.extensions.PluginId;
43 import com.intellij.openapi.fileEditor.FileDocumentManager;
44 import com.intellij.openapi.progress.EmptyProgressIndicator;
45 import com.intellij.openapi.progress.ProcessCanceledException;
46 import com.intellij.openapi.progress.ProgressIndicator;
47 import com.intellij.openapi.progress.ProgressManager;
48 import com.intellij.openapi.progress.util.ProgressWindow;
49 import com.intellij.openapi.project.Project;
50 import com.intellij.openapi.project.ProjectManager;
51 import com.intellij.openapi.project.ex.ProjectEx;
52 import com.intellij.openapi.project.ex.ProjectManagerEx;
53 import com.intellij.openapi.ui.Messages;
54 import com.intellij.openapi.util.*;
55 import com.intellij.openapi.wm.IdeFrame;
56 import com.intellij.openapi.wm.ex.ProgressIndicatorEx;
57 import com.intellij.psi.PsiLock;
58 import com.intellij.util.ConcurrencyUtil;
59 import com.intellij.util.EventDispatcher;
60 import com.intellij.util.ReflectionCache;
61 import com.intellij.util.concurrency.ReentrantWriterPreferenceReadWriteLock;
62 import com.intellij.util.containers.Stack;
63 import com.intellij.util.ui.UIUtil;
64 import org.jetbrains.annotations.NonNls;
65 import org.jetbrains.annotations.NotNull;
66 import org.jetbrains.annotations.Nullable;
67 import org.picocontainer.MutablePicoContainer;
68
69 import javax.swing.*;
70 import java.awt.*;
71 import java.io.IOException;
72 import java.lang.reflect.InvocationTargetException;
73 import java.lang.reflect.Method;
74 import java.util.ArrayList;
75 import java.util.Collection;
76 import java.util.List;
77 import java.util.concurrent.*;
78 import java.util.concurrent.atomic.AtomicBoolean;
79
80 @SuppressWarnings({"AssignmentToStaticFieldFromInstanceMethod"})
81 public class ApplicationImpl extends ComponentManagerImpl implements ApplicationEx {
82   private static final Logger LOG = Logger.getInstance("#com.intellij.application.impl.ApplicationImpl");
83   private final ModalityState MODALITY_STATE_NONE = ModalityState.NON_MODAL;
84   private final ModalityInvokator myInvokator = new ModalityInvokatorImpl();
85
86   private final EventDispatcher<ApplicationListener> myDispatcher = EventDispatcher.create(ApplicationListener.class);
87
88   private boolean myTestModeFlag = false;
89   private boolean myHeadlessMode = false;
90   private boolean myCommandLineMode = false;
91
92   private boolean myIsInternal = false;
93   private final String myName;
94
95   private final ReentrantWriterPreferenceReadWriteLock myActionsLock = new ReentrantWriterPreferenceReadWriteLock();
96   private final Stack<Runnable> myWriteActionsStack = new Stack<Runnable>();
97
98   private volatile Runnable myExceptionalThreadWithReadAccessRunnable;
99
100   private int myInEditorPaintCounter = 0;
101   private long myStartTime = 0;
102   private boolean myDoNotSave = false;
103   private volatile boolean myDisposeInProgress = false;
104
105   private final AtomicBoolean mySaveSettingsIsInProgress = new AtomicBoolean(false);
106
107   private final ExecutorService ourThreadExecutorsService = new ThreadPoolExecutor(
108     3,
109     Integer.MAX_VALUE,
110     30 * 60L,
111     TimeUnit.SECONDS,
112     new SynchronousQueue<Runnable>(),
113     new ThreadFactory() {
114       public Thread newThread(Runnable r) {
115         final Thread thread = new Thread(r, "ApplicationImpl pooled thread") {
116           public void interrupt() {
117             if (LOG.isDebugEnabled()) {
118               LOG.debug("Interrupted worker, will remove from pool");
119             }
120             super.interrupt();
121           }
122
123           public void run() {
124             try {
125               super.run();
126             }
127             catch (Throwable t) {
128               if (LOG.isDebugEnabled()) {
129                 LOG.debug("Worker exits due to exception", t);
130               }
131             }
132           }
133         };
134         thread.setPriority(Thread.NORM_PRIORITY - 1);
135         return thread;
136       }
137     }
138   );
139   private boolean myIsFiringLoadingEvent = false;
140   @NonNls private static final String WAS_EVER_SHOWN = "was.ever.shown";
141
142   private Boolean myActive;
143
144   protected void boostrapPicoContainer() {
145     super.boostrapPicoContainer();
146     getPicoContainer().registerComponentImplementation(IComponentStore.class, StoresFactory.getApplicationStoreClass());
147     getPicoContainer().registerComponentImplementation(ApplicationPathMacroManager.class);
148   }
149
150   @Override
151   @NotNull
152   public synchronized IApplicationStore getStateStore() {
153     return (IApplicationStore)super.getStateStore();
154   }
155
156   public ApplicationImpl(boolean isInternal, boolean isUnitTestMode, boolean isHeadless, boolean isCommandLine, String appName) {
157     super(null);
158
159     getPicoContainer().registerComponentInstance(Application.class, this);
160
161     CommonBundle.assertKeyIsFound = isUnitTestMode;
162
163     if ((isInternal || isUnitTestMode) && !Comparing.equal("off", System.getProperty("idea.disposer.debug"))) {
164       Disposer.setDebugMode(true);
165     }
166     myStartTime = System.currentTimeMillis();
167     myName = appName;
168     ApplicationManagerEx.setApplication(this);
169
170     PluginsFacade.INSTANCE = new PluginsFacade() {
171       public IdeaPluginDescriptor getPlugin(PluginId id) {
172         return PluginManager.getPlugin(id);
173       }
174
175       public IdeaPluginDescriptor[] getPlugins() {
176         return PluginManager.getPlugins();
177       }
178     };
179
180     if (!isUnitTestMode && !isHeadless) {
181       Toolkit.getDefaultToolkit().getSystemEventQueue().push(IdeEventQueue.getInstance());
182       if (Patches.SUN_BUG_ID_6209673) {
183         RepaintManager.setCurrentManager(new IdeRepaintManager());
184       }
185       IconLoader.activate();
186     }
187
188     myIsInternal = isInternal;
189     myTestModeFlag = isUnitTestMode;
190     myHeadlessMode = isHeadless;
191     myCommandLineMode = isCommandLine;
192
193     loadApplicationComponents();
194
195     if (myTestModeFlag) {
196       registerShutdownHook();
197     }
198
199     if (!isUnitTestMode && !isHeadless) {
200       Disposer.register(this, Disposer.newDisposable(), "ui");
201     }
202   }
203
204   private void registerShutdownHook() {
205     ShutDownTracker.getInstance(); // Necessary to avoid creating an instance while already shutting down.
206
207     ShutDownTracker.getInstance().registerShutdownTask(new Runnable() {
208       public void run() {
209         if (isDisposed() || isDisposeInProgress()) return;
210         try {
211           SwingUtilities.invokeAndWait(new Runnable() {
212             public void run() {
213               ApplicationManagerEx.setApplication(ApplicationImpl.this);
214               saveAll();
215               disposeSelf();
216             }
217           });
218         }
219         catch (InterruptedException e) {
220           LOG.error(e);
221         }
222         catch (InvocationTargetException e) {
223           LOG.error(e);
224         }
225       }
226     });
227   }
228
229   private boolean disposeSelf() {
230     Project[] openProjects = ProjectManagerEx.getInstanceEx().getOpenProjects();
231     final boolean[] canClose = {true};
232     for (final Project project : openProjects) {
233       CommandProcessor commandProcessor = CommandProcessor.getInstance();
234       commandProcessor.executeCommand(project, new Runnable() {
235         public void run() {
236           canClose[0] = ProjectUtil.closeProject(project);
237         }
238       }, ApplicationBundle.message("command.exit"), null);
239       if (!canClose[0]) return false;
240     }
241     myDisposeInProgress = true;
242     Disposer.dispose(this);
243
244     Disposer.assertIsEmpty();
245     return true;
246   }
247
248   public String getName() {
249     return myName;
250   }
251
252   public boolean holdsReadLock() {
253     return myActionsLock.isReadLockAcquired();
254   }
255
256   @Override
257   protected void handleInitComponentError(final Throwable ex, final boolean fatal, final String componentClassName) {
258     if (PluginManager.isPluginClass(componentClassName)) {
259       LOG.error(ex);
260       PluginId pluginId = PluginManager.getPluginByClassName(componentClassName);
261       @NonNls final String errorMessage = "Plugin " + pluginId.getIdString() + " failed to initialize and will be disabled:\n" + ex.getMessage() +
262                                           "\nPlease restart " + ApplicationNamesInfo.getInstance().getFullProductName() + ".";
263       PluginManager.disablePlugin(pluginId.getIdString());
264       if (!myHeadlessMode) {
265         JOptionPane.showMessageDialog(null, errorMessage);
266       }
267       else {
268         //noinspection UseOfSystemOutOrSystemErr
269         System.out.println(errorMessage);
270       }
271       System.exit(1);
272     }
273     else if (fatal) {
274       LOG.error(ex);
275       @NonNls final String errorMessage = "Fatal error initializing class " + componentClassName + ":\n" +
276                                           ex.toString() +
277                                           "\nComplete error stacktrace was written to idea.log";
278       if (!myHeadlessMode) {
279         JOptionPane.showMessageDialog(null, errorMessage);
280       }
281       else {
282         //noinspection UseOfSystemOutOrSystemErr
283         System.out.println(errorMessage);
284       }
285     }
286     super.handleInitComponentError(ex, fatal, componentClassName);
287   }
288
289   private void loadApplicationComponents() {
290     final IdeaPluginDescriptor[] plugins = PluginManager.getPlugins();
291     for (IdeaPluginDescriptor plugin : plugins) {
292       if (PluginManager.shouldSkipPlugin(plugin)) continue;
293       loadComponentsConfiguration(plugin.getAppComponents(), plugin, false);
294     }
295   }
296
297   protected MutablePicoContainer createPicoContainer() {
298     return Extensions.getRootArea().getPicoContainer();
299   }
300
301   public boolean isInternal() {
302     return myIsInternal;
303   }
304
305   public boolean isUnitTestMode() {
306     return myTestModeFlag;
307   }
308
309   public boolean isHeadlessEnvironment() {
310     return myHeadlessMode;
311   }
312
313   public boolean isCommandLine() {
314     return myCommandLineMode;
315   }
316
317   public IdeaPluginDescriptor getPlugin(PluginId id) {
318     return PluginsFacade.INSTANCE.getPlugin(id);
319   }
320
321   public IdeaPluginDescriptor[] getPlugins() {
322     return PluginsFacade.INSTANCE.getPlugins();
323   }
324
325   public Future<?> executeOnPooledThread(final Runnable action) {
326     return ourThreadExecutorsService.submit(new Runnable() {
327       public void run() {
328         try {
329           action.run();
330         }
331         catch (Throwable t) {
332           LOG.error(t);
333         }
334         finally {
335           Thread.interrupted(); // reset interrupted status
336         }
337       }
338     });
339   }
340
341   private static Thread ourDispatchThread = null;
342
343   public boolean isDispatchThread() {
344     return EventQueue.isDispatchThread();
345   }
346
347   @NotNull
348   public ModalityInvokator getInvokator() {
349     return myInvokator;
350   }
351
352
353   public void invokeLater(final Runnable runnable) {
354     myInvokator.invokeLater(runnable);
355   }
356
357   public void invokeLater(final Runnable runnable, @NotNull final Condition expired) {
358     myInvokator.invokeLater(runnable, expired);
359   }
360
361   public void invokeLater(final Runnable runnable, @NotNull final ModalityState state) {
362     myInvokator.invokeLater(runnable, state);
363   }
364
365   public void invokeLater(final Runnable runnable, @NotNull final ModalityState state, @NotNull final Condition expired) {
366     myInvokator.invokeLater(runnable, state, expired);
367   }
368
369   public void load(String path) throws IOException, InvalidDataException {
370     getStateStore().setOptionsPath(path);
371     getStateStore().setConfigPath(PathManager.getConfigPath());
372     myIsFiringLoadingEvent = true;
373     try {
374       fireBeforeApplicationLoaded();
375     }
376     finally {
377       myIsFiringLoadingEvent = false;
378     }
379
380     loadComponentRoamingTypes();
381
382     try {
383       getStateStore().load();
384     }
385     catch (StateStorage.StateStorageException e) {
386       throw new IOException(e.getMessage());
387     }
388
389   }
390
391   @Override
392   protected <T> T getComponentFromContainer(final Class<T> interfaceClass) {
393     if (myIsFiringLoadingEvent) {
394       return null;
395     }
396     return super.getComponentFromContainer(interfaceClass);
397   }
398
399   private static void loadComponentRoamingTypes() {
400     ExtensionPoint<RoamingTypeExtensionPointBean> point = Extensions.getRootArea().getExtensionPoint("com.intellij.ComponentRoamingType");
401     final RoamingTypeExtensionPointBean[] componentRoamingTypes = point.getExtensions();
402
403     for (RoamingTypeExtensionPointBean object : componentRoamingTypes) {
404
405       assert object.componentName != null;
406       assert object.roamingType != null;
407
408       final RoamingType type = RoamingType.valueOf(object.roamingType);
409
410       assert type != null;
411
412       ComponentRoamingManager.getInstance().setRoamingType(object.componentName, type);
413     }
414   }
415
416   private void fireBeforeApplicationLoaded() {
417     ExtensionPoint<ApplicationLoadListener> point = Extensions.getRootArea().getExtensionPoint("com.intellij.ApplicationLoadListener");
418     final ApplicationLoadListener[] objects = point.getExtensions();
419     for (ApplicationLoadListener object : objects) {
420       object.beforeApplicationLoaded(this);
421     }
422   }
423
424
425   public void dispose() {
426     fireApplicationExiting();
427     disposeComponents();
428
429     ourThreadExecutorsService.shutdownNow();
430     super.dispose();
431   }
432
433   private final Object lock = new Object();
434   private void makeChangesVisibleToEDT() {
435     synchronized (lock) {
436       lock.hashCode();
437     }
438   }
439
440   public boolean runProcessWithProgressSynchronously(final Runnable process, String progressTitle, boolean canBeCanceled, Project project) {
441     return runProcessWithProgressSynchronously(process, progressTitle, canBeCanceled, project, null);
442   }
443
444   public boolean runProcessWithProgressSynchronously(final Runnable process, final String progressTitle, final boolean canBeCanceled, @Nullable final Project project,
445                                                      final JComponent parentComponent) {
446     return runProcessWithProgressSynchronously(process, progressTitle, canBeCanceled, project, parentComponent, null);
447   }
448
449   public boolean runProcessWithProgressSynchronously(final Runnable process, final String progressTitle, final boolean canBeCanceled, @Nullable final Project project,
450                                                        final JComponent parentComponent, final String cancelText) {
451     assertIsDispatchThread();
452
453     if (myExceptionalThreadWithReadAccessRunnable != null ||
454         ApplicationManager.getApplication().isUnitTestMode() ||
455         ApplicationManager.getApplication().isHeadlessEnvironment()) {
456       try {
457         ProgressManager.getInstance().runProcess(process, new EmptyProgressIndicator());
458       }
459       catch (ProcessCanceledException e) {
460         // ok to ignore.
461         return false;
462       }
463       return true;
464     }
465
466     final ProgressWindow progress = new ProgressWindow(canBeCanceled, false, project, parentComponent, cancelText);
467     progress.setTitle(progressTitle);
468
469     try {
470       myExceptionalThreadWithReadAccessRunnable = process;
471       final boolean[] threadStarted = {false};
472       SwingUtilities.invokeLater(new Runnable() {
473         public void run() {
474           if (myExceptionalThreadWithReadAccessRunnable != process) {
475               LOG.error("myExceptionalThreadWithReadAccessRunnable != process, process = " + myExceptionalThreadWithReadAccessRunnable);
476           }
477
478           executeOnPooledThread(new Runnable() {
479             public void run() {
480               if (myExceptionalThreadWithReadAccessRunnable != process) {
481                 LOG.error("myExceptionalThreadWithReadAccessRunnable != process, process = " + myExceptionalThreadWithReadAccessRunnable);
482               }
483
484               final boolean old = setExceptionalThreadWithReadAccessFlag(true);
485               LOG.assertTrue(isReadAccessAllowed());
486               try {
487                 ProgressManager.getInstance().runProcess(process, progress);
488               }
489               catch (ProcessCanceledException e) {
490                 progress.cancel();
491                 // ok to ignore.
492               }
493               finally {
494                 setExceptionalThreadWithReadAccessFlag(old);
495                 makeChangesVisibleToEDT();
496               }
497             }
498           });
499           threadStarted[0] = true;
500         }
501       });
502
503       progress.startBlocking();
504
505       LOG.assertTrue(threadStarted[0]);
506       LOG.assertTrue(!progress.isRunning());
507     }
508     finally {
509       myExceptionalThreadWithReadAccessRunnable = null;
510       makeChangesVisibleToEDT();
511     }
512
513     return !progress.isCanceled();
514   }
515
516   public boolean isInModalProgressThread() {
517     if (myExceptionalThreadWithReadAccessRunnable == null || !isExceptionalThreadWithReadAccess()) {
518       return false;
519     }
520     ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
521     return progressIndicator.isModal() && ((ProgressIndicatorEx)progressIndicator).isModalityEntered();
522   }
523
524   public <T> List<Future<T>> invokeAllUnderReadAction(@NotNull Collection<Callable<T>> tasks, final ExecutorService executorService) throws Throwable {
525     final List<Callable<T>> newCallables = new ArrayList<Callable<T>>(tasks.size());
526     for (final Callable<T> task : tasks) {
527       Callable<T> newCallable = new Callable<T>() {
528         public T call() throws Exception {
529           boolean old = setExceptionalThreadWithReadAccessFlag(true);
530           try {
531             LOG.assertTrue(isReadAccessAllowed());
532             return task.call();
533           }
534           finally {
535             setExceptionalThreadWithReadAccessFlag(old);
536           }
537         }
538       };
539       newCallables.add(newCallable);
540     }
541     final Ref<Throwable> exception = new Ref<Throwable>();
542     List<Future<T>> result = runReadAction(new Computable<List<Future<T>>>() {
543       public List<Future<T>> compute() {
544         try {
545           return ConcurrencyUtil.invokeAll(newCallables, executorService);
546         }
547         catch (Throwable throwable) {
548           exception.set(throwable);
549           return null;
550         }
551       }
552     });
553     Throwable throwable = exception.get();
554     if (throwable != null) throw throwable;
555     return result;
556   }
557
558   public void invokeAndWait(Runnable runnable, @NotNull ModalityState modalityState) {
559     if (isDispatchThread()) {
560       LOG.error("invokeAndWait must not be called from event queue thread");
561       runnable.run();
562       return;
563     }
564
565     if (isExceptionalThreadWithReadAccess()) { //OK if we're in exceptional thread.
566       LaterInvocator.invokeAndWait(runnable, modalityState);
567       return;
568     }
569
570     if (myActionsLock.isReadLockAcquired()) {
571       LOG.error("Calling invokeAndWait from read-action leads to possible deadlock.");
572     }
573
574     LaterInvocator.invokeAndWait(runnable, modalityState);
575   }
576
577   public ModalityState getCurrentModalityState() {
578     Object[] entities = LaterInvocator.getCurrentModalEntities();
579     return entities.length > 0 ? new ModalityStateEx(entities) : getNoneModalityState();
580   }
581
582   public ModalityState getModalityStateForComponent(Component c) {
583     Window window = c instanceof Window ? (Window)c : SwingUtilities.windowForComponent(c);
584     if (window == null) return getNoneModalityState(); //?
585     return LaterInvocator.modalityStateForWindow(window);
586   }
587
588   public ModalityState getDefaultModalityState() {
589     if (EventQueue.isDispatchThread()) {
590       return getCurrentModalityState();
591     }
592     else {
593       ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
594       return progress == null ? getNoneModalityState() : progress.getModalityState();
595     }
596   }
597
598   public ModalityState getNoneModalityState() {
599     return MODALITY_STATE_NONE;
600   }
601
602   public long getStartTime() {
603     return myStartTime;
604   }
605
606   public long getIdleTime() {
607     return IdeEventQueue.getInstance().getIdleTime();
608   }
609
610   public void exit() {
611     exit(false);
612   }
613
614   public void exit(final boolean force) {
615     if (!force && getDefaultModalityState() != ModalityState.NON_MODAL) {
616       return;
617     }
618
619     Runnable runnable = new Runnable() {
620       public void run() {
621         if (!force) {
622           if (!showConfirmation()) {
623             saveAll();
624             return;
625           }
626         }
627
628         FileDocumentManager.getInstance().saveAllDocuments();
629
630         saveSettings();
631
632         if (!canExit()) return;
633
634         if (disposeSelf()) System.exit(0);
635       }
636     };
637     
638     if (!isDispatchThread()) {
639       invokeLater(runnable, ModalityState.NON_MODAL);
640     }
641     else {
642       runnable.run();
643     }
644   }
645
646   private static boolean showConfirmation() {
647     final boolean hasUnsafeBgTasks = ProgressManager.getInstance().hasUnsafeProgressIndicator();
648     final ConfirmExitDialog confirmExitDialog = new ConfirmExitDialog(hasUnsafeBgTasks);
649     if (confirmExitDialog.isToBeShown()) {
650       confirmExitDialog.show();
651       if (!confirmExitDialog.isOK()) {
652         return false;
653       }
654     }
655     return true;
656   }
657
658   private boolean canExit() {
659     for (ApplicationListener applicationListener : myDispatcher.getListeners()) {
660       if (!applicationListener.canExitApplication()) {
661         return false;
662       }
663     }
664
665     ProjectManagerEx projectManager = (ProjectManagerEx)ProjectManager.getInstance();
666     Project[] projects = projectManager.getOpenProjects();
667     for (Project project : projects) {
668       if (!projectManager.canClose(project)) {
669         return false;
670       }
671     }
672
673     return true;
674   }
675
676   public void runReadAction(final Runnable action) {
677     /** if we are inside read action, do not try to acquire read lock again since it will deadlock if there is a pending writeAction
678      * see {@link com.intellij.util.concurrency.ReentrantWriterPreferenceReadWriteLock#allowReader()} */
679     boolean mustAcquire = !isReadAccessAllowed();
680
681     if (mustAcquire) {
682       LOG.assertTrue(myTestModeFlag || !Thread.holdsLock(PsiLock.LOCK), "Thread must not hold PsiLock while performing readAction");
683       try {
684         myActionsLock.readLock().acquire();
685       }
686       catch (InterruptedException e) {
687         throw new RuntimeInterruptedException(e);
688       }
689     }
690
691     try {
692       action.run();
693     }
694     finally {
695       if (mustAcquire) {
696         myActionsLock.readLock().release();
697       }
698     }
699   }
700
701   private static final ThreadLocal<Boolean> exceptionalThreadWithReadAccessFlag = new ThreadLocal<Boolean>();
702   private static boolean isExceptionalThreadWithReadAccess() {
703     Boolean flag = exceptionalThreadWithReadAccessFlag.get();
704     return flag == Boolean.TRUE;
705   }
706
707   public static boolean setExceptionalThreadWithReadAccessFlag(boolean flag) {
708     boolean old = isExceptionalThreadWithReadAccess();
709     if (flag) {
710       exceptionalThreadWithReadAccessFlag.set(Boolean.TRUE);
711     }
712     else {
713       exceptionalThreadWithReadAccessFlag.remove();
714     }
715     return old;
716   }
717
718   public <T> T runReadAction(final Computable<T> computation) {
719     final Ref<T> ref = Ref.create(null);
720     runReadAction(new Runnable() {
721       public void run() {
722         ref.set(computation.compute());
723       }
724     });
725     return ref.get();
726   }
727
728   public void runWriteAction(final Runnable action) {
729     assertCanRunWriteAction();
730
731     ActivityTracker.getInstance().inc();
732     fireBeforeWriteActionStart(action);
733
734     LOG.assertTrue(myActionsLock.isWriteLockAcquired(Thread.currentThread()) || !Thread.holdsLock(PsiLock.LOCK), "Thread must not hold PsiLock while performing writeAction");
735     try {
736       myActionsLock.writeLock().acquire();
737     }
738     catch (InterruptedException e) {
739       throw new RuntimeInterruptedException(e);
740     }
741
742     try {
743       synchronized (myWriteActionsStack) {
744         myWriteActionsStack.push(action);
745       }
746
747       fireWriteActionStarted(action);
748
749       action.run();
750     }
751     finally {
752       try {
753         fireWriteActionFinished(action);
754         
755         synchronized (myWriteActionsStack) {
756           myWriteActionsStack.pop();
757         }
758       }
759       finally {
760         myActionsLock.writeLock().release();
761       }
762     }
763   }
764
765   public <T> T runWriteAction(final Computable<T> computation) {
766     final Ref<T> ref = Ref.create(null);
767     runWriteAction(new Runnable() {
768       public void run() {
769         ref.set(computation.compute());
770       }
771     });
772     return ref.get();
773   }
774
775   public Object getCurrentWriteAction(Class actionClass) {
776     synchronized (myWriteActionsStack) {
777       for (int i = myWriteActionsStack.size() - 1; i >= 0; i--) {
778         Runnable action = myWriteActionsStack.get(i);
779         if (actionClass == null || ReflectionCache.isAssignable(actionClass, action.getClass())) return action;
780       }
781     }
782     return null;
783   }
784
785   public void assertReadAccessAllowed() {
786     if (myTestModeFlag || myHeadlessMode) return;
787     if (!isReadAccessAllowed()) {
788       LOG.error(
789         "Read access is allowed from event dispatch thread or inside read-action only (see com.intellij.openapi.application.Application.runReadAction())",
790         "Current thread: " + describe(Thread.currentThread()), "Our dispatch thread:" + describe(ourDispatchThread),
791         "SystemEventQueueThread: " + describe(getEventQueueThread()));
792     }
793   }
794
795   @NonNls
796   private static String describe(Thread o) {
797     if (o == null) return "null";
798     return o.toString() + " " + System.identityHashCode(o);
799   }
800
801   @Nullable
802   private static Thread getEventQueueThread() {
803     EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue();
804     try {
805       Method method = EventQueue.class.getDeclaredMethod("getDispatchThread");
806       method.setAccessible(true);
807       return (Thread)method.invoke(eventQueue);
808     }
809     catch (Exception e1) {
810       // ok
811     }
812     return null;
813   }
814
815   public boolean isReadAccessAllowed() {
816     Thread currentThread = Thread.currentThread();
817     return ourDispatchThread == currentThread ||
818            isExceptionalThreadWithReadAccess() ||
819            myActionsLock.isReadLockAcquired() ||
820            myActionsLock.isWriteLockAcquired() ||
821            isDispatchThread();
822   }
823
824   public void assertReadAccessToDocumentsAllowed() {
825     /* TODO
826     Thread currentThread = Thread.currentThread();
827     if (ourDispatchThread != currentThread) {
828       if (myExceptionalThreadWithReadAccess == currentThread) return;
829       if (myActionsLock.isReadLockAcquired(currentThread)) return;
830       if (myActionsLock.isWriteLockAcquired(currentThread)) return;
831       if (isDispatchThread(currentThread)) return;
832       LOG.error(
833         "Read access is allowed from event dispatch thread or inside read-action only (see com.intellij.openapi.application.Application.runReadAction())");
834     }
835     */
836   }
837
838   private void assertCanRunWriteAction() {
839     assertIsDispatchThread("Write access is allowed from event dispatch thread only");
840
841   }
842
843   public void assertIsDispatchThread() {
844     assertIsDispatchThread("Access is allowed from event dispatch thread only.");
845   }
846
847   private void assertIsDispatchThread(String message) {
848     if (myTestModeFlag || myHeadlessMode || ShutDownTracker.isShutdownHookRunning()) return;
849     final Thread currentThread = Thread.currentThread();
850     if (ourDispatchThread == currentThread) return;
851
852     if (EventQueue.isDispatchThread()) {
853       ourDispatchThread = currentThread;
854     }
855     if (ourDispatchThread == currentThread) return;
856
857     LOG.error(message,
858               "Current thread: " + describe(Thread.currentThread()),
859               "Our dispatch thread:" + describe(ourDispatchThread),
860               "SystemEventQueueThread: " + describe(getEventQueueThread()));
861   }
862
863   public void assertIsDispatchThread(@Nullable final JComponent component) {
864     if (component == null) return;
865
866     Thread curThread = Thread.currentThread();
867     if (ourDispatchThread == curThread) {
868       return;
869     }
870
871     if (Boolean.TRUE.equals(component.getClientProperty(WAS_EVER_SHOWN))) {
872       assertIsDispatchThread();
873     }
874     else {
875       final JRootPane root = component.getRootPane();
876       if (root != null) {
877         component.putClientProperty(WAS_EVER_SHOWN, Boolean.TRUE);
878         assertIsDispatchThread();
879       } 
880     }
881   }
882
883   public boolean tryRunReadAction(@NotNull Runnable action) {
884     /** if we are inside read action, do not try to acquire read lock again since it will deadlock if there is a pending writeAction
885      * see {@link com.intellij.util.concurrency.ReentrantWriterPreferenceReadWriteLock#allowReader()} */
886     boolean mustAcquire = !isReadAccessAllowed();
887
888     if (mustAcquire) {
889       LOG.assertTrue(myTestModeFlag || !Thread.holdsLock(PsiLock.LOCK), "Thread must not hold PsiLock while performing readAction");
890       try {
891         if (!myActionsLock.readLock().attempt(0)) return false;
892       }
893       catch (InterruptedException e) {
894         throw new RuntimeInterruptedException(e);
895       }
896     }
897
898     try {
899       action.run();
900     }
901     finally {
902       if (mustAcquire) {
903         myActionsLock.readLock().release();
904       }
905     }
906     return true;
907   }
908
909   public boolean tryToApplyActivationState(boolean active, Window window) {
910     final Component frame = UIUtil.findUltimateParent(window);
911
912     if (frame instanceof IdeFrame) {
913       final IdeFrame ideFrame = (IdeFrame)frame;
914       if (isActive() != active) {
915         myActive = Boolean.valueOf(active);
916         System.setProperty("idea.active", Boolean.valueOf(myActive).toString());
917         if (active) {
918           myDispatcher.getMulticaster().applicationActivated(ideFrame);
919         }
920         else {
921           myDispatcher.getMulticaster().applicationDeactivated(ideFrame);
922         }
923         return true;
924       }
925     }
926
927     return false;
928   }
929
930   public boolean isActive() {
931     if (isUnitTestMode()) return true;
932
933     if (myActive == null) {
934       Window active = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
935       return active != null;
936     }
937
938     return myActive;
939   }
940
941   public void assertWriteAccessAllowed() {
942     LOG.assertTrue(isWriteAccessAllowed(),
943                    "Write access is allowed inside write-action only (see com.intellij.openapi.application.Application.runWriteAction())");
944   }
945
946   public boolean isWriteAccessAllowed() {
947     return myActionsLock.isWriteLockAcquired(Thread.currentThread());
948   }
949
950   public void editorPaintStart() {
951     myInEditorPaintCounter++;
952   }
953
954   public void editorPaintFinish() {
955     myInEditorPaintCounter--;
956     LOG.assertTrue(myInEditorPaintCounter >= 0);
957   }
958
959   public void addApplicationListener(ApplicationListener l) {
960     myDispatcher.addListener(l);
961   }
962
963   public void addApplicationListener(ApplicationListener l, Disposable parent) {
964     myDispatcher.addListener(l, parent);
965   }
966
967   public void removeApplicationListener(ApplicationListener l) {
968     myDispatcher.removeListener(l);
969   }
970
971   private void fireApplicationExiting() {
972     myDispatcher.getMulticaster().applicationExiting();
973   }
974
975   private void fireBeforeWriteActionStart(Runnable action) {
976     myDispatcher.getMulticaster().beforeWriteActionStart(action);
977   }
978
979   private void fireWriteActionStarted(Runnable action) {
980     myDispatcher.getMulticaster().writeActionStarted(action);
981   }
982
983   private void fireWriteActionFinished(Runnable action) {
984     myDispatcher.getMulticaster().writeActionFinished(action);
985   }
986
987   public void _saveSettings() { // for testing purposes
988     if (mySaveSettingsIsInProgress.compareAndSet(false, true)) {
989       try {
990         doSave();
991       }
992       catch (final Throwable ex) {
993         if (isUnitTestMode()) {
994           System.out.println("Saving application settings failed");
995           ex.printStackTrace();
996         }
997         else {
998           LOG.info("Saving application settings failed", ex);
999           invokeLater(new Runnable() {
1000             public void run() {
1001               if (ex instanceof PluginException) {
1002                 final PluginException pluginException = (PluginException)ex;
1003                 PluginManager.disablePlugin(pluginException.getPluginId().getIdString());
1004                 Messages.showMessageDialog("The plugin " +
1005                                            pluginException.getPluginId() +
1006                                            " failed to save settings and has been disabled. Please restart " +
1007                                            ApplicationNamesInfo.getInstance().getFullProductName(), CommonBundle.getErrorTitle(),
1008                                            Messages.getErrorIcon());
1009               }
1010               else {
1011                 Messages.showMessageDialog(ApplicationBundle.message("application.save.settings.error", ex.getLocalizedMessage()),
1012                                            CommonBundle.getErrorTitle(), Messages.getErrorIcon());
1013
1014               }
1015             }
1016           });
1017         }
1018       }
1019       finally {
1020         mySaveSettingsIsInProgress.set(false);
1021       }
1022     }
1023   }
1024
1025   public void saveSettings() {
1026     if (myDoNotSave || isUnitTestMode() || isHeadlessEnvironment()) return;
1027     _saveSettings();
1028   }
1029
1030   public void saveAll() {
1031     if (myDoNotSave || isUnitTestMode() || isHeadlessEnvironment()) return;
1032
1033     FileDocumentManager.getInstance().saveAllDocuments();
1034
1035     Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
1036     for (Project openProject : openProjects) {
1037       ProjectEx project = (ProjectEx)openProject;
1038       project.save();
1039     }
1040
1041     saveSettings();
1042   }
1043
1044   public void doNotSave() {
1045     myDoNotSave = true;
1046   }
1047
1048
1049   public boolean isDoNotSave() {
1050     return myDoNotSave;
1051   }
1052
1053   public <T> T[] getExtensions(final ExtensionPointName<T> extensionPointName) {
1054     return Extensions.getRootArea().getExtensionPoint(extensionPointName).getExtensions();
1055   }
1056
1057   public boolean isDisposeInProgress() {
1058     return myDisposeInProgress;
1059   }
1060
1061   public boolean isRestartCapable() {
1062     return SystemInfo.isWindows;
1063   }
1064
1065   public void restart() {
1066    if (SystemInfo.isWindows) {
1067      Win32Restarter.restart();
1068     }
1069     else {
1070      exit();
1071    }
1072   }
1073
1074   public boolean isSaving() {
1075     if (getStateStore().isSaving()) return true;
1076     Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
1077     for (Project openProject : openProjects) {
1078       ProjectEx project = (ProjectEx)openProject;
1079       if (project.getStateStore().isSaving()) return true;
1080     }
1081
1082     return false;
1083   }
1084 }