hotswap: scan using java.io.File instead of VirtualFile

This commit is contained in:
Eugene Zhuravlev
2012-01-17 14:12:19 +01:00
parent abe649a06c
commit da6630875f
3 changed files with 53 additions and 94 deletions
@@ -15,7 +15,7 @@
*/
package com.intellij.debugger.impl;
import com.intellij.openapi.vfs.VirtualFile;
import java.io.File;
/**
* User: lex
@@ -23,9 +23,9 @@ import com.intellij.openapi.vfs.VirtualFile;
* Time: 2:23:38 PM
*/
public class HotSwapFile {
VirtualFile file;
final File file;
public HotSwapFile(VirtualFile file) {
public HotSwapFile(File file) {
this.file = file;
}
}
@@ -20,24 +20,21 @@ import com.intellij.debugger.DebuggerManagerEx;
import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
import com.intellij.debugger.engine.events.DebuggerCommandImpl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.compiler.ex.CompilerPathsEx;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.fileTypes.FileTypes;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.OrderEnumerator;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.newvfs.RefreshQueue;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class HotSwapManager extends AbstractProjectComponent {
private final Map<DebuggerSession, Long> myTimeStamps = new HashMap<DebuggerSession, Long>();
@@ -70,56 +67,54 @@ public class HotSwapManager extends AbstractProjectComponent {
myTimeStamps.put(session, Long.valueOf(tStamp));
}
public HashMap<String, HotSwapFile> getModifiedClasses(final DebuggerSession session, final HotSwapProgress progress) {
public Map<String, HotSwapFile> getModifiedClasses(final DebuggerSession session, final HotSwapProgress progress) {
DebuggerManagerThreadImpl.assertIsManagerThread();
final long timeStamp = getTimeStamp(session);
final HashMap<String, HotSwapFile> modifiedClasses = new HashMap<String, HotSwapFile>();
final List<File> outputRoots = new ArrayList<File>();
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
final List<VirtualFile> allClasses = OrderEnumerator.orderEntries(myProject).withoutSdk().getPathsList().getRootDirs();
final VirtualFile[] allDirs = VfsUtil.toVirtualFileArray(allClasses);
final FileTypeManager fileTypeManager = FileTypeManager.getInstance();
CompilerPathsEx.visitFiles(allDirs, new CompilerPathsEx.FileVisitor() {
protected void acceptDirectory(final VirtualFile file, final String fileRoot, final String filePath) {
if (progress.isCancelled()) {
return;
}
progress.setText(DebuggerBundle.message("progress.hotswap.scanning.path", filePath));
if(file.getFileSystem() instanceof JarFileSystem && FileTypes.ARCHIVE.equals(file.getFileType())) {
if(file.getTimeStamp() > timeStamp) {
super.acceptDirectory(file, fileRoot, filePath);
}
}
else {
super.acceptDirectory(file, fileRoot, filePath);
}
}
protected void acceptFile(VirtualFile file, String fileRoot, String filePath) {
if (progress.isCancelled()) {
return;
}
if (file.getTimeStamp() > timeStamp && StdFileTypes.CLASS.equals(file.getFileType())) {
//noinspection HardCodedStringLiteral
if (SystemInfo.isFileSystemCaseSensitive? filePath.endsWith(CLASS_EXTENSION) : StringUtil.endsWithIgnoreCase(filePath, CLASS_EXTENSION)) {
progress.setText(DebuggerBundle.message("progress.hotswap.scanning.path", filePath));
//noinspection HardCodedStringLiteral
final String qualifiedName = filePath.substring(fileRoot.length() + 1, filePath.length() - CLASS_EXTENSION.length()).replace('/', '.');
modifiedClasses.put(qualifiedName, new HotSwapFile(file));
}
}
}
});
final List<VirtualFile> allClasses = OrderEnumerator.orderEntries(myProject).withoutSdk().withoutLibraries().getPathsList().getRootDirs();
for (VirtualFile dir : VfsUtil.toVirtualFileArray(allClasses)) {
outputRoots.add(new File(dir.getPath()));
}
}
});
final long timeStamp = getTimeStamp(session);
final Map<String, HotSwapFile> modifiedClasses = new HashMap<String, HotSwapFile>();
for (File root : outputRoots) {
final String rootPath = FileUtil.toCanonicalPath(root.getPath());
collectModifiedClasses(root, rootPath, rootPath + "/", modifiedClasses, progress, timeStamp);
}
return modifiedClasses;
}
private static boolean collectModifiedClasses(File file, String filePath, String rootPath, Map<String, HotSwapFile> container, HotSwapProgress progress, long timeStamp) {
if (progress.isCancelled()) {
return false;
}
final File[] files = file.listFiles();
if (files != null) {
for (File child : files) {
if (!collectModifiedClasses(child, filePath + "/" + child.getName(), rootPath, container, progress, timeStamp)) {
return false;
}
}
}
else { // not a dir
if (SystemInfo.isFileSystemCaseSensitive? StringUtil.endsWith(filePath, CLASS_EXTENSION) : StringUtil.endsWithIgnoreCase(filePath, CLASS_EXTENSION)) {
if (file.lastModified() > timeStamp) {
progress.setText(DebuggerBundle.message("progress.hotswap.scanning.path", filePath));
//noinspection HardCodedStringLiteral
final String qualifiedName = filePath.substring(rootPath.length(), filePath.length() - CLASS_EXTENSION.length()).replace('/', '.');
container.put(qualifiedName, new HotSwapFile(file));
}
}
}
return true;
}
public static HotSwapManager getInstance(Project project) {
return project.getComponent(HotSwapManager.class);
}
@@ -141,15 +136,12 @@ public class HotSwapManager extends AbstractProjectComponent {
}
});
final Set<Project> projectsToRefresh = new HashSet<Project>();
for (final DebuggerSession debuggerSession : sessions) {
if (debuggerSession.isAttached()) {
projectsToRefresh.add(debuggerSession.getProject());
scanClassesCommand.addCommand(debuggerSession.getProcess(), new DebuggerCommandImpl() {
protected void action() throws Exception {
swapProgress.setDebuggerSession(debuggerSession);
HashMap<String, HotSwapFile> sessionClasses =
getInstance(swapProgress.getProject()).getModifiedClasses(debuggerSession, swapProgress);
final Map<String, HotSwapFile> sessionClasses = getInstance(swapProgress.getProject()).getModifiedClasses(debuggerSession, swapProgress);
if (!sessionClasses.isEmpty()) {
modifiedClasses.put(debuggerSession, sessionClasses);
}
@@ -159,27 +151,7 @@ public class HotSwapManager extends AbstractProjectComponent {
}
swapProgress.setTitle(DebuggerBundle.message("progress.hotswap.scanning.classes"));
if (!scanClassesCommand.isEmpty()) {
// have to refresh dirs first:
final Collection<VirtualFile> roots = ApplicationManager.getApplication().runReadAction(new Computable<Collection<VirtualFile>>() {
public Collection<VirtualFile> compute() {
final Set<VirtualFile> result = new HashSet<VirtualFile>();
for (Project project : projectsToRefresh) {
for (VirtualFile file : OrderEnumerator.orderEntries(project).withoutSdk().getPathsList().getRootDirs()) {
if (!file.getFileSystem().isReadOnly()) {
result.add(file);
}
}
}
return result;
}
});
if (!roots.isEmpty()) {
RefreshQueue.getInstance().refresh(false, true, null, roots.toArray(new VirtualFile[roots.size()]));
}
scanClassesCommand.run();
}
scanClassesCommand.run();
return swapProgress.isCancelled() ? new HashMap<DebuggerSession, Map<String, HotSwapFile>>() : modifiedClasses;
}
@@ -21,11 +21,10 @@ import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.engine.events.DebuggerCommandImpl;
import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
import com.intellij.debugger.ui.breakpoints.BreakpointManager;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.StringBuilderSpinAllocator;
import com.intellij.util.ui.MessageCategory;
import com.sun.jdi.ReferenceType;
@@ -116,34 +115,22 @@ class ReloadClassesWorker {
try {
final Map<ReferenceType, byte[]> redefineMap = new HashMap<ReferenceType,byte[]>();
int processedClassesCount = 0;
final IOException[] _ex = new IOException[] {null};
for (final String qualifiedName : modifiedClasses.keySet()) {
processedClassesCount++;
if (qualifiedName != null) {
myProgress.setText(qualifiedName);
myProgress.setFraction(processedClassesCount / (double)modifiedClasses.size());
}
_ex[0] = null;
final HotSwapFile fileDescr = modifiedClasses.get(qualifiedName);
final byte[] buffer = ApplicationManager.getApplication().runReadAction(new Computable<byte[]>() {
public byte[] compute() {
try {
return fileDescr.file.contentsToByteArray();
}
catch (IOException e) {
_ex[0] = e;
return null;
}
}
});
if (buffer != null) {
try {
final byte[] buffer = FileUtil.loadFileBytes(fileDescr.file);
final List<ReferenceType> classes = virtualMachineProxy.classesByName(qualifiedName);
for (final ReferenceType reference : classes) {
redefineMap.put(reference, buffer);
}
}
else {
reportProblem(qualifiedName, _ex[0]);
catch (IOException e) {
reportProblem(qualifiedName, e);
}
if (redefineMap.size() >= CLASSES_CHUNK_SIZE) {
// reload this portion of clasess and clear the map to free memory