2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com
.intellij
.lifecycle
;
18 import com
.intellij
.openapi
.application
.Application
;
19 import com
.intellij
.openapi
.application
.ApplicationManager
;
20 import com
.intellij
.openapi
.application
.ModalityState
;
21 import com
.intellij
.openapi
.application
.impl
.LaterInvocator
;
22 import com
.intellij
.openapi
.components
.ServiceManager
;
23 import com
.intellij
.openapi
.diagnostic
.Logger
;
24 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
25 import com
.intellij
.openapi
.progress
.ProgressIndicator
;
26 import com
.intellij
.openapi
.progress
.ProgressManager
;
27 import com
.intellij
.openapi
.project
.Project
;
28 import com
.intellij
.openapi
.project
.ProjectManager
;
29 import com
.intellij
.openapi
.project
.ProjectManagerListener
;
30 import com
.intellij
.openapi
.util
.Condition
;
31 import com
.intellij
.openapi
.util
.Pair
;
32 import com
.intellij
.openapi
.util
.Ref
;
33 import com
.intellij
.util
.concurrency
.Semaphore
;
34 import org
.jetbrains
.annotations
.NonNls
;
35 import org
.jetbrains
.annotations
.NotNull
;
37 import java
.util
.ArrayList
;
38 import java
.util
.HashMap
;
39 import java
.util
.List
;
42 public class PeriodicalTasksCloser
implements ProjectManagerListener
{
43 private final static Logger LOG
= Logger
.getInstance("#com.intellij.lifecycle.PeriodicalTasksCloser");
44 private final static Object ourLock
= new Object();
45 private final List
<Pair
<String
, Runnable
>> myInterrupters
;
46 private final static Map
<Project
, Boolean
> myStates
= new HashMap
<Project
, Boolean
>();
47 private final Project myProject
;
49 private PeriodicalTasksCloser(final Project project
, final ProjectManager projectManager
) {
51 myInterrupters
= new ArrayList
<Pair
<String
, Runnable
>>();
52 projectManager
.addProjectManagerListener(project
, this);
55 public static PeriodicalTasksCloser
getInstance(final Project project
) {
56 return ServiceManager
.getService(project
, PeriodicalTasksCloser
.class);
59 public boolean register(final String name
, final Runnable runnable
) {
60 synchronized (ourLock
) {
61 if (Boolean
.FALSE
.equals(myStates
.get(myProject
))) {
64 myInterrupters
.add(new Pair
<String
, Runnable
>(name
, runnable
));
69 public void projectOpened(Project project
) {
70 synchronized (ourLock
) {
71 myStates
.put(project
, Boolean
.TRUE
);
75 public boolean canCloseProject(Project project
) {
79 public void projectClosed(Project project
) {
80 synchronized (ourLock
) {
81 myStates
.remove(project
);
85 public void projectClosing(Project project
) {
86 synchronized (ourLock
) {
87 myStates
.put(project
, Boolean
.FALSE
);
89 ProgressManager
.getInstance().runProcessWithProgressSynchronously(new Runnable() {
91 final ProgressIndicator indicator
= ProgressManager
.getInstance().getProgressIndicator();
92 final List
<Pair
<String
, Runnable
>> list
;
93 synchronized (ourLock
) {
94 list
= myInterrupters
;
96 for (Pair
<String
, Runnable
> pair
: list
) {
97 if (indicator
!= null) {
98 indicator
.setText(pair
.getFirst());
99 indicator
.checkCanceled();
101 pair
.getSecond().run();
104 }, "Please wait for safe shutdown of periodical tasks...", true, project
);
107 public static<T
> T
safeGetComponent(@NotNull final Project project
, final Class
<T
> componentClass
) throws ProcessCanceledException
{
110 component
= project
.getComponent(componentClass
);
112 catch (Throwable t
) {
113 if (t
instanceof NullPointerException
) {
114 } else if (t
instanceof AssertionError
) {
118 throw new ProcessCanceledException();
120 synchronized (ourLock
) {
121 final Boolean state
= myStates
.get(project
);
122 // if project is already closed and project key is already removed from map - then it should have thrown an exception in the block above
123 // so ok to just check for 'closing' stage here
124 if (state
!= null && ! Boolean
.TRUE
.equals(state
)) {
125 throw new ProcessCanceledException();
131 public static void invokeAndWaitInterruptedWhenClosing(final Project project
, final Runnable runnable
, final ModalityState modalityState
) {
132 final Ref
<Boolean
> start
= new Ref
<Boolean
>(Boolean
.TRUE
);
133 final Application application
= ApplicationManager
.getApplication();
134 LOG
.assertTrue(! application
.isDispatchThread());
136 final Semaphore semaphore
= new Semaphore();
138 Runnable runnable1
= new Runnable() {
149 public String
toString() {
150 return "PeriodicalTaskCloser's invoke and wait [" + runnable
.toString() + "]";
153 LaterInvocator
.invokeLater(runnable1
, modalityState
, new Condition
<Object
>() {
154 public boolean value(Object o
) {
155 synchronized (start
) {
156 return ! start
.get();
162 if (semaphore
.waitFor(1000)) {
165 final Ref
<Boolean
> fire
= new Ref
<Boolean
>();
166 if (project
!= null) {
167 synchronized (ourLock
) {
168 final Boolean state
= myStates
.get(project
);
169 if (! Boolean
.TRUE
.equals(state
)) {
170 fire
.set(Boolean
.TRUE
);
172 if (Boolean
.TRUE
.equals(fire
.get())) {
173 synchronized (start
) {
174 start
.set(Boolean
.FALSE
);