event-based hotswap for compile server

This commit is contained in:
Eugene Zhuravlev
2012-01-17 20:26:24 +01:00
parent 5c3df137e8
commit 65376ac093
16 changed files with 605 additions and 43 deletions
@@ -23,6 +23,7 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.OrderEnumerator;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
@@ -67,7 +68,7 @@ public class HotSwapManager extends AbstractProjectComponent {
myTimeStamps.put(session, Long.valueOf(tStamp));
}
public Map<String, HotSwapFile> getModifiedClasses(final DebuggerSession session, final HotSwapProgress progress) {
public Map<String, HotSwapFile> scanForModifiedClasses(final DebuggerSession session, final HotSwapProgress progress) {
DebuggerManagerThreadImpl.assertIsManagerThread();
final List<File> outputRoots = new ArrayList<File>();
@@ -125,7 +126,39 @@ public class HotSwapManager extends AbstractProjectComponent {
setTimeStamp(session, newSwapTime);
}
public static Map<DebuggerSession, Map<String, HotSwapFile>> getModifiedClasses(final List<DebuggerSession> sessions, final HotSwapProgress swapProgress) {
public static Map<DebuggerSession, Map<String, HotSwapFile>> findModifiedClasses(List<DebuggerSession> sessions, Map<String, List<String>> generatedPaths) {
final Map<DebuggerSession, Map<String, HotSwapFile>> result = new java.util.HashMap<DebuggerSession, Map<String, HotSwapFile>>();
List<Pair<DebuggerSession, Long>> sessionWithStamps = new ArrayList<Pair<DebuggerSession, Long>>();
for (DebuggerSession session : sessions) {
sessionWithStamps.add(new Pair<DebuggerSession, Long>(session, getInstance(session.getProject()).getTimeStamp(session)));
}
for (Map.Entry<String, List<String>> entry : generatedPaths.entrySet()) {
final File root = new File(entry.getKey());
for (String relativePath : entry.getValue()) {
if (SystemInfo.isFileSystemCaseSensitive? StringUtil.endsWith(relativePath, CLASS_EXTENSION) : StringUtil.endsWithIgnoreCase(relativePath, CLASS_EXTENSION)) {
final String qualifiedName = relativePath.substring(0, relativePath.length() - CLASS_EXTENSION.length()).replace('/', '.');
final HotSwapFile hotswapFile = new HotSwapFile(new File(root, relativePath));
final long fileStamp = hotswapFile.file.lastModified();
for (Pair<DebuggerSession, Long> pair : sessionWithStamps) {
final DebuggerSession session = pair.first;
if (fileStamp > pair.second) {
Map<String, HotSwapFile> container = result.get(session);
if (container == null) {
container = new java.util.HashMap<String, HotSwapFile>();
result.put(session, container);
}
container.put(qualifiedName, hotswapFile);
}
}
}
}
}
return result;
}
public static Map<DebuggerSession, Map<String, HotSwapFile>> scanForModifiedClasses(final List<DebuggerSession> sessions, final HotSwapProgress swapProgress) {
final Map<DebuggerSession, Map<String, HotSwapFile>> modifiedClasses = new HashMap<DebuggerSession, Map<String, HotSwapFile>>();
final MultiProcessCommand scanClassesCommand = new MultiProcessCommand();
@@ -141,7 +174,7 @@ public class HotSwapManager extends AbstractProjectComponent {
scanClassesCommand.addCommand(debuggerSession.getProcess(), new DebuggerCommandImpl() {
protected void action() throws Exception {
swapProgress.setDebuggerSession(debuggerSession);
final Map<String, HotSwapFile> sessionClasses = getInstance(swapProgress.getProject()).getModifiedClasses(debuggerSession, swapProgress);
final Map<String, HotSwapFile> sessionClasses = getInstance(swapProgress.getProject()).scanForModifiedClasses(debuggerSession, swapProgress);
if (!sessionClasses.isEmpty()) {
modifiedClasses.put(debuggerSession, sessionClasses);
}
@@ -41,12 +41,11 @@ import com.intellij.util.PairFunction;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.messages.MessageBus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
/**
* User: lex
@@ -62,7 +61,21 @@ public class HotSwapUIImpl extends HotSwapUI implements ProjectComponent{
public HotSwapUIImpl(final Project project, MessageBus bus) {
myProject = project;
bus.connect().subscribe(CompilerTopics.COMPILATION_STATUS, new CompilationStatusListener() {
private final AtomicReference<Map<String, List<String>>> myGeneratedPaths = new AtomicReference<Map<String, List<String>>>(new HashMap<String, List<String>>());
public void fileGenerated(String outputRoot, String relativePath) {
final Map<String, List<String>> map = myGeneratedPaths.get();
List<String> paths = map.get(outputRoot);
if (paths == null) {
paths = new ArrayList<String>();
map.put(outputRoot, paths);
}
paths.add(relativePath);
}
public void compilationFinished(boolean aborted, int errors, int warnings, CompileContext compileContext) {
final Map<String, List<String>> generated = myGeneratedPaths.getAndSet(new HashMap<String, List<String>>());
if (myProject.isDisposed()) {
return;
}
@@ -82,7 +95,7 @@ public class HotSwapUIImpl extends HotSwapUI implements ProjectComponent{
}
}
if (!sessions.isEmpty()) {
hotSwapSessions(sessions);
hotSwapSessions(sessions, generated);
}
}
myPerformHotswapAfterThisCompilation = true;
@@ -129,7 +142,7 @@ public class HotSwapUIImpl extends HotSwapUI implements ProjectComponent{
return false;
}
private void hotSwapSessions(final List<DebuggerSession> sessions) {
private void hotSwapSessions(final List<DebuggerSession> sessions, @Nullable final Map<String, List<String>> generatedPaths) {
final boolean shouldAskBeforeHotswap = myAskBeforeHotswap;
myAskBeforeHotswap = true;
@@ -148,7 +161,7 @@ public class HotSwapUIImpl extends HotSwapUI implements ProjectComponent{
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
public void run() {
final Map<DebuggerSession, Map<String, HotSwapFile>> modifiedClasses = getModifiedClasses(findClassesProgress, sessions);
final Map<DebuggerSession, Map<String, HotSwapFile>> modifiedClasses = generatedPaths == null? scanForModifiedClassesWithProgress(sessions, findClassesProgress) : HotSwapManager.findModifiedClasses(sessions, generatedPaths);
final Application application = ApplicationManager.getApplication();
if (modifiedClasses.isEmpty()) {
@@ -179,7 +192,7 @@ public class HotSwapUIImpl extends HotSwapUI implements ProjectComponent{
@Override
public Integer fun(Integer exitCode, JCheckBox cb) {
settings.HOTSWAP_HANG_WARNING_ENABLED = !cb.isSelected();
return exitCode == DialogWrapper.OK_EXIT_CODE? exitCode : DialogWrapper.CANCEL_EXIT_CODE;
return exitCode == DialogWrapper.OK_EXIT_CODE ? exitCode : DialogWrapper.CANCEL_EXIT_CODE;
}
}
);
@@ -203,6 +216,21 @@ public class HotSwapUIImpl extends HotSwapUI implements ProjectComponent{
});
}
private static Map<DebuggerSession, Map<String, HotSwapFile>> scanForModifiedClassesWithProgress(final List<DebuggerSession> sessions, final HotSwapProgressImpl progress) {
final Ref<Map<DebuggerSession, Map<String, HotSwapFile>>> result = Ref.create(null);
ProgressManager.getInstance().runProcess(new Runnable() {
public void run() {
try {
result.set(HotSwapManager.scanForModifiedClasses(sessions, progress));
}
finally {
progress.finished();
}
}
}, progress.getProgressIndicator());
return result.get();
}
private static void reloadModifiedClasses(final Map<DebuggerSession, Map<String, HotSwapFile>> modifiedClasses, final HotSwapProgressImpl progress) {
ProgressManager.getInstance().runProcess(new Runnable() {
public void run() {
@@ -212,18 +240,6 @@ public class HotSwapUIImpl extends HotSwapUI implements ProjectComponent{
}, progress.getProgressIndicator());
}
private static Map<DebuggerSession, Map<String, HotSwapFile>> getModifiedClasses(final HotSwapProgressImpl swapProgress, final List<DebuggerSession> sessions) {
final Ref<Map<DebuggerSession, Map<String, HotSwapFile>>> modifiedClasses = Ref.create(null);
ProgressManager.getInstance().runProcess(new Runnable() {
public void run() {
modifiedClasses.set(HotSwapManager.getModifiedClasses(sessions, swapProgress));
swapProgress.finished();
}
}, swapProgress.getProgressIndicator());
return modifiedClasses.get();
}
public void reloadChangedClasses(final DebuggerSession session, boolean compileBeforeHotswap) {
dontAskHotswapAfterThisCompilation();
if (compileBeforeHotswap) {
@@ -233,7 +249,7 @@ public class HotSwapUIImpl extends HotSwapUI implements ProjectComponent{
if(session.isAttached()) {
final List<DebuggerSession> sessions = new ArrayList<DebuggerSession>(1);
sessions.add(session);
hotSwapSessions(sessions);
hotSwapSessions(sessions, null);
}
}
}