package jetbrains.mps.ide.platform.watching;
/*Generated by MPS */
import com.intellij.openapi.components.ApplicationComponent;
import org.apache.log4j.Logger;
import org.apache.log4j.LogManager;
import com.intellij.openapi.project.ProjectManager;
import jetbrains.mps.make.IMakeNotificationListener;
import java.util.List;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import java.util.ArrayList;
import com.intellij.util.ui.update.MergingUpdateQueue;
import java.util.concurrent.atomic.AtomicInteger;
import jetbrains.mps.make.IMakeService;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.apache.log4j.Level;
import jetbrains.mps.util.Computable;
import jetbrains.mps.smodel.ModelAccess;
import com.intellij.openapi.application.ApplicationManager;
import jetbrains.mps.smodel.MPSModuleRepository;
import jetbrains.mps.progress.EmptyProgressMonitor;
import com.intellij.util.ui.update.Update;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.progress.ProgressIndicator;
import org.jetbrains.mps.openapi.util.ProgressMonitor;
import jetbrains.mps.progress.ProgressMonitorAdapter;
import jetbrains.mps.make.MakeNotification;
public class ReloadManagerComponent extends ReloadManager implements ApplicationComponent {
private static final Logger LOG = LogManager.getLogger(ReloadManagerComponent.class);
private final ProjectManager myProjectManager;
private final IMakeNotificationListener myMakeListener = new ReloadManagerComponent.NotReloadingOnMakeListener();
private final List<ReloadListener> myReloadListeners = ListSequence.fromList(new ArrayList<ReloadListener>());
private final ReloadManagerComponent.ReloadSessionBroker myReloadSessionBroker = new ReloadManagerComponent.ReloadSessionBroker();
private final MergingUpdateQueue myTaskQueue = new MergingUpdateQueue("Reload Manager Queue", 500, true, null, null, null, true);
private final Object myUpdateId = new Object();
private final AtomicInteger mySuspendCount = new AtomicInteger(0);
private final IMakeService myMakeService;
public ReloadManagerComponent(ProjectManager projectManager, IMakeService makeService) {
myProjectManager = projectManager;
myTaskQueue.setRestartTimerOnAdd(true);
myMakeService = makeService;
}
public void initComponent() {
myMakeService.addListener(myMakeListener);
}
public void disposeComponent() {
myMakeService.removeListener(myMakeListener);
}
@NonNls
@NotNull
public String getComponentName() {
return "Reload Manager";
}
public void suspendReloads() {
int count = mySuspendCount.incrementAndGet();
assert count >= 0;
myTaskQueue.suspend();
}
public void resumeReloads() {
int count = mySuspendCount.decrementAndGet();
assert count >= 0;
if (count == 0) {
myTaskQueue.resume();
}
}
@Override
public <T extends ReloadParticipant> void runReload(Class<T> participantClass, ReloadAction<T> reloadAction) {
ReloadSession rs;
rs = myReloadSessionBroker.employ();
try {
T participant = rs.getParticipant(participantClass);
reloadAction.runAction(participant);
rs.updateStatus();
} catch (RuntimeException e) {
if (LOG.isEnabledFor(Level.ERROR)) {
LOG.error("Exception during reload", e);
}
throw e;
} finally {
myReloadSessionBroker.dismiss(rs);
}
queueReloadSession();
}
@Override
public void addReloadListener(ReloadListener listener) {
ListSequence.fromList(myReloadListeners).addElement(listener);
}
@Override
public void removeReloadListener(ReloadListener listener) {
ListSequence.fromList(myReloadListeners).removeElement(listener);
}
@Override
public <T> T computeNoReload(Computable<T> computable) {
try {
suspendReloads();
return computable.compute();
} finally {
resumeReloads();
}
}
@Override
public void flush() {
// synchronously commit all pending reload requests
ReloadSession session = myReloadSessionBroker.waitForUnemployed();
if (session == null) {
return;
}
// see MPS-18743, 21760
ModelAccess.instance().runWriteAction(new Runnable() {
public void run() {
assert ApplicationManager.getApplication().isWriteAccessAllowed() : "Platform write access not allowed: execute from EDT or under progress";
MPSModuleRepository.getInstance().saveAll();
}
});
// Q: also do normal progressMonitor, as in real reload on timeout ?
session.doReload(new EmptyProgressMonitor());
}
private void queueReloadSession() {
if (!(myReloadSessionBroker.hasUnemployed())) {
return;
}
myTaskQueue.queue(new Update(myUpdateId) {
public void run() {
for (Project project : myProjectManager.getOpenProjects()) {
if (project.getComponent(ProjectLevelVcsManager.class).isBackgroundVcsOperationRunning()) {
queueReloadSession();
return;
}
}
final ReloadSession rs = myReloadSessionBroker.getUnemployed();
if (rs == null) {
queueReloadSession();
return;
}
if (rs.isEmpty()) {
return;
}
ApplicationManager.getApplication().invokeAndWait(new Runnable() {
public void run() {
// see MPS-18743, 21760
// fixme the problem itself must be fixed (reloading module while there are changed models in it
ModelAccess.instance().runWriteAction(new Runnable() {
public void run() {
MPSModuleRepository.getInstance().saveAll();
}
});
}
}, ModalityState.NON_MODAL);
ProgressManager.getInstance().run(new Task.Backgroundable(null, "Reloading Files", false) {
@Override
public void run(@NotNull final ProgressIndicator progressIndicator) {
ProgressMonitor monitor = new ProgressMonitorAdapter(progressIndicator);
monitor.start("Reloading Files", 1);
try {
monitor.step("Reloading File System");
rs.doReload(monitor.subTask(1));
} finally {
monitor.done();
}
}
});
}
});
}
private class ReloadSessionBroker {
private ReloadSession myReloadSession;
/*package*/ synchronized ReloadSession employ() {
if (myReloadSession == null) {
myReloadSession = new ReloadSession(myReloadListeners);
}
myReloadSession.incEmployCount();
return myReloadSession;
}
/*package*/ synchronized void dismiss(ReloadSession rs) {
assert myReloadSession == rs;
rs.decEmployCount();
notify();
}
/*package*/ boolean hasUnemployed() {
ReloadSession rs = myReloadSession;
return rs != null && !(rs.isBeingEmployed());
}
/*package*/ synchronized ReloadSession getUnemployed() {
if (myReloadSession == null || myReloadSession.isBeingEmployed()) {
return null;
}
ReloadSession rs = myReloadSession;
myReloadSession = null;
return rs;
}
/*package*/ synchronized ReloadSession waitForUnemployed() {
if (myReloadSession == null) {
return null;
}
while (myReloadSession != null && myReloadSession.isBeingEmployed()) {
try {
wait();
} catch (InterruptedException e) {
throw new RuntimeException("Waiting for reload session to be freed failed", e);
}
}
ReloadSession rs = myReloadSession;
myReloadSession = null;
return rs;
}
}
private class NotReloadingOnMakeListener extends IMakeNotificationListener.Stub {
@Override
public void sessionOpened(MakeNotification notification) {
suspendReloads();
}
@Override
public void sessionClosed(MakeNotification notification) {
resumeReloads();
}
}
}