From d84d20cc261e3af0a36b37143beadc65864255eb Mon Sep 17 00:00:00 2001 From: Zarina Kurbatova Date: Fri, 31 Jul 2020 21:25:56 +0300 Subject: [PATCH 1/7] Make the mining process cancellable --- ...Bundle.java => RefactorInsightBundle.java} | 8 +- .../actions/RefactoringAction.java | 6 +- .../processors/CommitMiner.java | 5 +- .../services/MiningService.java | 120 ++++++++++++------ .../services/SettingsConfigurable.java | 4 +- .../refactorinsight/ui/windows/GitWindow.java | 9 +- .../ui/windows/RefactoringHistoryToolbar.java | 14 +- .../ui/windows/SettingsComponent.java | 18 +-- .../research/refactorinsight/utils/Utils.java | 14 +- ...rties => RefactorInsightBundle.properties} | 0 10 files changed, 120 insertions(+), 78 deletions(-) rename src/main/java/org/jetbrains/research/refactorinsight/{services/RefactoringsBundle.java => RefactorInsightBundle.java} (78%) rename src/main/resources/{RefactoringsBundle.properties => RefactorInsightBundle.properties} (100%) diff --git a/src/main/java/org/jetbrains/research/refactorinsight/services/RefactoringsBundle.java b/src/main/java/org/jetbrains/research/refactorinsight/RefactorInsightBundle.java similarity index 78% rename from src/main/java/org/jetbrains/research/refactorinsight/services/RefactoringsBundle.java rename to src/main/java/org/jetbrains/research/refactorinsight/RefactorInsightBundle.java index 5ed76dbf..ce8038a3 100644 --- a/src/main/java/org/jetbrains/research/refactorinsight/services/RefactoringsBundle.java +++ b/src/main/java/org/jetbrains/research/refactorinsight/RefactorInsightBundle.java @@ -1,4 +1,4 @@ -package org.jetbrains.research.refactorinsight.services; +package org.jetbrains.research.refactorinsight; import com.intellij.AbstractBundle; import com.intellij.reference.SoftReference; @@ -6,11 +6,11 @@ import java.util.ResourceBundle; import org.jetbrains.annotations.PropertyKey; -public final class RefactoringsBundle { - private static final String BUNDLE = "RefactoringsBundle"; +public final class RefactorInsightBundle { + private static final String BUNDLE = "RefactorInsightBundle"; private static Reference INSTANCE; - private RefactoringsBundle() { + private RefactorInsightBundle() { } public static String message(@PropertyKey(resourceBundle = BUNDLE) String key, Object... params) { diff --git a/src/main/java/org/jetbrains/research/refactorinsight/actions/RefactoringAction.java b/src/main/java/org/jetbrains/research/refactorinsight/actions/RefactoringAction.java index 8c29cc94..3b70bfd0 100644 --- a/src/main/java/org/jetbrains/research/refactorinsight/actions/RefactoringAction.java +++ b/src/main/java/org/jetbrains/research/refactorinsight/actions/RefactoringAction.java @@ -8,7 +8,7 @@ import java.util.List; import org.jetbrains.annotations.NotNull; import org.jetbrains.research.refactorinsight.services.MiningService; -import org.jetbrains.research.refactorinsight.services.RefactoringsBundle; +import org.jetbrains.research.refactorinsight.RefactorInsightBundle; /** @@ -25,8 +25,8 @@ public void actionPerformed(@NotNull AnActionEvent e) { final List repositories = GitRepositoryManager .getInstance(e.getProject()).getRepositories(); if (repositories.isEmpty()) { - Messages.showErrorDialog(RefactoringsBundle.message("no.repo"), - RefactoringsBundle.message("name")); + Messages.showErrorDialog(RefactorInsightBundle.message("no.repo"), + RefactorInsightBundle.message("name")); return; } GitRepository repository = repositories.get(0); diff --git a/src/main/java/org/jetbrains/research/refactorinsight/processors/CommitMiner.java b/src/main/java/org/jetbrains/research/refactorinsight/processors/CommitMiner.java index af0e8a0d..933f1c1e 100644 --- a/src/main/java/org/jetbrains/research/refactorinsight/processors/CommitMiner.java +++ b/src/main/java/org/jetbrains/research/refactorinsight/processors/CommitMiner.java @@ -10,7 +10,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -19,7 +18,7 @@ import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.jgit.lib.Repository; import org.jetbrains.research.refactorinsight.data.RefactoringEntry; -import org.jetbrains.research.refactorinsight.services.RefactoringsBundle; +import org.jetbrains.research.refactorinsight.RefactorInsightBundle; import org.refactoringminer.api.GitHistoryRefactoringMiner; import org.refactoringminer.api.GitService; import org.refactoringminer.api.Refactoring; @@ -33,7 +32,7 @@ * Consumes a git commit, calls RefactoringMiner and detects the refactorings for a commit. */ public class CommitMiner implements Consumer { - private static final String progress = RefactoringsBundle.message("progress"); + private static final String progress = RefactorInsightBundle.message("progress"); private final ExecutorService pool; private final Map map; private final Project myProject; diff --git a/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java b/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java index bcc6cc3f..ff7202c4 100644 --- a/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java +++ b/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java @@ -6,25 +6,28 @@ import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; +import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Ref; +import com.intellij.util.ExceptionUtil; import com.intellij.util.xmlb.annotations.OptionTag; import com.intellij.vcs.log.VcsCommitMetadata; import git4idea.history.GitHistoryUtils; import git4idea.repo.GitRepository; + import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; + import org.jetbrains.annotations.NotNull; +import org.jetbrains.research.refactorinsight.RefactorInsightBundle; import org.jetbrains.research.refactorinsight.data.RefactoringEntry; import org.jetbrains.research.refactorinsight.data.RefactoringInfo; import org.jetbrains.research.refactorinsight.processors.CommitMiner; @@ -34,16 +37,16 @@ /** * This is the MiningService. * It computes, process and stores the data retrieved from RefactoringMiner. - * It can mine 1 specific commit, a fixed number of commits, or all commits in the repository. + * It can mine one specific commit, a fixed number of commits, or all commits in the repository. * it stores and persists the detected refactoring data in .idea/refactorings.xml file. */ @State(name = "MiningRefactoringsState", - storages = {@Storage("refactorings.xml")}) + storages = {@Storage("refactorings.xml")}) @Service public class MiningService implements PersistentStateComponent { public static ConcurrentHashMap> methodHistory - = new ConcurrentHashMap>(); + = new ConcurrentHashMap<>(); private boolean mining = false; private MyState innerState = new MyState(); @@ -113,7 +116,7 @@ public void mineRepo(GitRepository repository) { */ public void mineRepo(GitRepository repository, int limit) { ProgressManager.getInstance() - .run(new Task.Backgroundable(repository.getProject(), "Mining refactorings", true) { + .run(new Task.Backgroundable(repository.getProject(), RefactorInsightBundle.message("mining"), true) { @Override public void onCancel() { @@ -122,7 +125,7 @@ public void onCancel() { public void run(@NotNull ProgressIndicator progressIndicator) { mining = true; - progressIndicator.setText(RefactoringsBundle.message("mining")); + progressIndicator.setText(RefactorInsightBundle.message("mining")); progressIndicator.setIndeterminate(false); int cores = SettingsState .getInstance(repository.getProject()).threads; @@ -154,7 +157,7 @@ public void run(@NotNull ProgressIndicator progressIndicator) { if (repository.getCurrentRevision() != null) { computeRefactoringHistory(repository.getCurrentRevision(), repository.getProject()); } - progressIndicator.setText(RefactoringsBundle.message("finished")); + progressIndicator.setText(RefactorInsightBundle.message("finished")); } }); } @@ -177,38 +180,81 @@ public void mineAndWait(GitRepository repository) { } } - /** - * Method for mining a single commit. - * - * @param commit to be mined. - * @param project current project. - * @param info to be updated. - */ - public void mineAtCommit(VcsCommitMetadata commit, Project project, GitWindow info) { - ProgressManager.getInstance() - .run(new Task.Backgroundable(project, String.format( - RefactoringsBundle.message("mining.at"), commit.getId().asString())) { + /** + * Mine refactorings in the specific commit. + * + * @param commit to be mined. + * @param project current project. + * @param info to be updated. + */ + public void mineAtCommit(VcsCommitMetadata commit, Project project, GitWindow info) { + ProgressManager.getInstance() + .run(new Task.Backgroundable(project, String.format( + RefactorInsightBundle.message("mining.at"), commit.getId().asString())) { - @Override - public void onCancel() { - super.onCancel(); - } + @Override + public void onFinished() { + if (contains(commit.getId().asString())) { + ApplicationManager.getApplication() + .invokeLater(() -> info.refresh(commit.getId().asString())); + } + } - public void onFinished() { - super.onFinished(); - if (contains(commit.getId().asString())) { - System.out.println(RefactoringsBundle.message("finished")); - ApplicationManager.getApplication() - .invokeLater(() -> info.refresh(commit.getId().asString())); + @Override + public void run(@NotNull ProgressIndicator progressIndicator) { + try { + runWithCheckCanceled(() -> { + CommitMiner.mineAtCommitTimeout(commit, innerState.refactoringsMap.map, project); + return null; + }, + progressIndicator); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + + /** + * Allows to interrupt a process which does not performs checkCancelled() calls by itself. + */ + private void runWithCheckCanceled(@NotNull final Callable callable, @NotNull final ProgressIndicator indicator) throws Exception { + final Ref result = Ref.create(); + final Ref error = Ref.create(); + + Future future = ApplicationManager.getApplication().executeOnPooledThread(() -> ProgressManager.getInstance().executeProcessUnderProgress(() -> { + try { + result.set(callable.call()); + } catch (Throwable t) { + error.set(t); } - } + }, indicator)); - public void run(@NotNull ProgressIndicator progressIndicator) { - CommitMiner.mineAtCommitTimeout(commit, innerState.refactoringsMap.map, project); - } - }); - } + try { + runWithCheckCanceled(future, indicator); + ExceptionUtil.rethrowAll(error.get()); + } catch (ProcessCanceledException e) { + future.cancel(true); + throw e; + } + } + /** + * Waits for {@code future} to be complete, or the current thread's indicator to be canceled. + */ + private void runWithCheckCanceled(@NotNull Future future, + @NotNull final ProgressIndicator indicator) throws ExecutionException { + while (true) { + indicator.checkCanceled(); + try { + future.get(10, TimeUnit.MILLISECONDS); + return; + } catch (InterruptedException e) { + throw new ProcessCanceledException(e); + } catch (TimeoutException ignored) { + } + } + } public Map> getRefactoringHistory() { return methodHistory; diff --git a/src/main/java/org/jetbrains/research/refactorinsight/services/SettingsConfigurable.java b/src/main/java/org/jetbrains/research/refactorinsight/services/SettingsConfigurable.java index 6370b9dd..2f8bd00d 100644 --- a/src/main/java/org/jetbrains/research/refactorinsight/services/SettingsConfigurable.java +++ b/src/main/java/org/jetbrains/research/refactorinsight/services/SettingsConfigurable.java @@ -2,13 +2,13 @@ import com.intellij.openapi.options.Configurable; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.Messages; import git4idea.repo.GitRepository; import git4idea.repo.GitRepositoryManager; import java.util.List; import javax.swing.JComponent; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.Nullable; +import org.jetbrains.research.refactorinsight.RefactorInsightBundle; import org.jetbrains.research.refactorinsight.ui.windows.SettingsComponent; /** @@ -25,7 +25,7 @@ public SettingsConfigurable(Project project) { @Nls(capitalization = Nls.Capitalization.Title) @Override public String getDisplayName() { - return RefactoringsBundle.message("setting"); + return RefactorInsightBundle.message("setting"); } @Override diff --git a/src/main/java/org/jetbrains/research/refactorinsight/ui/windows/GitWindow.java b/src/main/java/org/jetbrains/research/refactorinsight/ui/windows/GitWindow.java index ad0f3ea3..a48422b9 100644 --- a/src/main/java/org/jetbrains/research/refactorinsight/ui/windows/GitWindow.java +++ b/src/main/java/org/jetbrains/research/refactorinsight/ui/windows/GitWindow.java @@ -3,21 +3,18 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.changes.ui.ChangesTree; import com.intellij.ui.Gray; -import com.intellij.ui.JBSplitter; import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBList; -import com.intellij.ui.components.JBPanel; import com.intellij.ui.components.JBViewport; import com.intellij.ui.treeStructure.Tree; import com.intellij.util.ui.UIUtil; import com.intellij.vcs.log.VcsCommitMetadata; import com.intellij.vcs.log.ui.MainVcsLogUi; import com.intellij.vcs.log.ui.table.VcsLogGraphTable; -import icons.RefactorInsightIcons; + import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Objects; -import javax.swing.JButton; import javax.swing.SwingConstants; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreePath; @@ -25,7 +22,7 @@ import org.jetbrains.research.refactorinsight.data.RefactoringEntry; import org.jetbrains.research.refactorinsight.data.RefactoringInfo; import org.jetbrains.research.refactorinsight.services.MiningService; -import org.jetbrains.research.refactorinsight.services.RefactoringsBundle; +import org.jetbrains.research.refactorinsight.RefactorInsightBundle; import org.jetbrains.research.refactorinsight.ui.tree.TreeUtils; import org.jetbrains.research.refactorinsight.ui.tree.renderers.MainCellRenderer; @@ -117,7 +114,7 @@ private void buildComponent() { if (entry.timeout || entry.getRefactorings().isEmpty()) { final JBLabel component = - new JBLabel(RefactoringsBundle.message("no.ref"), SwingConstants.CENTER); + new JBLabel(RefactorInsightBundle.message("no.ref"), SwingConstants.CENTER); component.setForeground(Gray._105); viewport.setView(component); return; diff --git a/src/main/java/org/jetbrains/research/refactorinsight/ui/windows/RefactoringHistoryToolbar.java b/src/main/java/org/jetbrains/research/refactorinsight/ui/windows/RefactoringHistoryToolbar.java index aba1204e..b895c330 100644 --- a/src/main/java/org/jetbrains/research/refactorinsight/ui/windows/RefactoringHistoryToolbar.java +++ b/src/main/java/org/jetbrains/research/refactorinsight/ui/windows/RefactoringHistoryToolbar.java @@ -43,7 +43,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.research.refactorinsight.data.RefactoringInfo; -import org.jetbrains.research.refactorinsight.services.RefactoringsBundle; +import org.jetbrains.research.refactorinsight.RefactorInsightBundle; import org.jetbrains.research.refactorinsight.ui.tree.TreeUtils; import org.jetbrains.research.refactorinsight.ui.tree.renderers.HistoryToolbarRenderer; import org.jetbrains.research.refactorinsight.utils.Utils; @@ -74,7 +74,7 @@ public RefactoringHistoryToolbar(Project project) { factory = VcsProjectLog.getInstance(project).getLogManager() .getMainLogUiFactory("method history", VcsLogFilterObject.collection()); toolWindow = - toolWindowManager.registerToolWindow(RefactoringsBundle.message("history"), + toolWindowManager.registerToolWindow(RefactorInsightBundle.message("history"), true, ToolWindowAnchor.BOTTOM); } @@ -112,7 +112,7 @@ public void showToolbar(Set refactorings, private void setSecondComponent(JBSplitter splitter) { final JBLabel component = - new JBLabel(RefactoringsBundle.message("click.to.jump"), SwingConstants.CENTER); + new JBLabel(RefactorInsightBundle.message("click.to.jump"), SwingConstants.CENTER); component.setForeground(Gray._105); splitter.setSecondComponent(component); } @@ -120,7 +120,7 @@ private void setSecondComponent(JBSplitter splitter) { private void setFirstComponent(int size, JBSplitter splitter, Tree tree) { JBScrollPane pane = new JBScrollPane(tree); JBLabel label = - new JBLabel(String.format(RefactoringsBundle.message("how.many.detected"), + new JBLabel(String.format(RefactorInsightBundle.message("how.many.detected"), size, size > 1 ? "s" : "", type.toString().toLowerCase())); label.setForeground(Gray._105); pane.setColumnHeaderView(label); @@ -192,7 +192,7 @@ private Tree createTree(List refactorings, if (methods != null && !methods.isEmpty()) { DefaultMutableTreeNode child = new DefaultMutableTreeNode( - RefactoringsBundle.message("check.methods")); + RefactorInsightBundle.message("check.methods")); addObjectsToTree(methods, child, true); if (child.getChildCount() > 0) { root.add(child); @@ -201,7 +201,7 @@ private Tree createTree(List refactorings, if (attributes != null && !attributes.isEmpty()) { DefaultMutableTreeNode child = new DefaultMutableTreeNode( - RefactoringsBundle.message("check.fields")); + RefactorInsightBundle.message("check.fields")); addObjectsToTree(attributes, child, false); if (child.getChildCount() > 0) { root.add(child); @@ -264,7 +264,7 @@ private void showContent(String methodName, JComponent tree) { private void showPopup(DataContext datacontext) { JBPanel panel = new JBPanel(new GridLayout(0, 1)); - panel.add(new JBLabel(RefactoringsBundle.message("no.ref.history"))); + panel.add(new JBLabel(RefactorInsightBundle.message("no.ref.history"))); JBPopup popup = JBPopupFactory.getInstance() .createComponentPopupBuilder(panel, null).createPopup(); popup.showInBestPositionFor(datacontext); diff --git a/src/main/java/org/jetbrains/research/refactorinsight/ui/windows/SettingsComponent.java b/src/main/java/org/jetbrains/research/refactorinsight/ui/windows/SettingsComponent.java index 936db74b..e61bf102 100644 --- a/src/main/java/org/jetbrains/research/refactorinsight/ui/windows/SettingsComponent.java +++ b/src/main/java/org/jetbrains/research/refactorinsight/ui/windows/SettingsComponent.java @@ -15,7 +15,7 @@ import javax.swing.JComponent; import javax.swing.JPanel; import org.jetbrains.research.refactorinsight.services.MiningService; -import org.jetbrains.research.refactorinsight.services.RefactoringsBundle; +import org.jetbrains.research.refactorinsight.RefactorInsightBundle; import org.jetbrains.research.refactorinsight.services.RefactoringsMapConverter; /** @@ -36,14 +36,14 @@ public class SettingsComponent { public SettingsComponent(Project project) { - JButton clear = new JButton(RefactoringsBundle.message("button.clear")); + JButton clear = new JButton(RefactorInsightBundle.message("button.clear")); clear.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { MiningService.getInstance(project).clear(); } }); - JButton all = new JButton(RefactoringsBundle.message("button.mine")); + JButton all = new JButton(RefactorInsightBundle.message("button.mine")); all.setPreferredSize(clear.getPreferredSize()); all.addMouseListener(new MouseAdapter() { @Override @@ -53,7 +53,7 @@ public void mouseClicked(MouseEvent e) { MiningService.getInstance(project).mineAll(repository); } }); - JButton choose = new JButton(RefactoringsBundle.message("button.import")); + JButton choose = new JButton(RefactorInsightBundle.message("button.import")); choose.setPreferredSize(clear.getPreferredSize()); choose.addMouseListener(new MouseAdapter() { @Override @@ -70,8 +70,8 @@ public void mouseClicked(MouseEvent e) { MiningService.getInstance(project).getState().refactoringsMap = new RefactoringsMapConverter().fromString(content); } catch (Exception ex) { - Messages.showErrorDialog(RefactoringsBundle.message("bad.file"), - RefactoringsBundle.message("name")); + Messages.showErrorDialog(RefactorInsightBundle.message("bad.file"), + RefactorInsightBundle.message("name")); } } ); @@ -79,10 +79,10 @@ public void mouseClicked(MouseEvent e) { }); myMainPanel = FormBuilder.createFormBuilder() - .addLabeledComponent(RefactoringsBundle.message("label.max.commits"), commitLimit, 1, false) - .addLabeledComponent(RefactoringsBundle.message("label.max.history"), historyLimit, 1, + .addLabeledComponent(RefactorInsightBundle.message("label.max.commits"), commitLimit, 1, false) + .addLabeledComponent(RefactorInsightBundle.message("label.max.history"), historyLimit, 1, false) - .addLabeledComponent(RefactoringsBundle.message("label.threads"), threads, 1, false) + .addLabeledComponent(RefactorInsightBundle.message("label.threads"), threads, 1, false) .addComponent(clear) .addComponent(all) .addComponent(choose) diff --git a/src/main/java/org/jetbrains/research/refactorinsight/utils/Utils.java b/src/main/java/org/jetbrains/research/refactorinsight/utils/Utils.java index 21d36a79..b39098ad 100644 --- a/src/main/java/org/jetbrains/research/refactorinsight/utils/Utils.java +++ b/src/main/java/org/jetbrains/research/refactorinsight/utils/Utils.java @@ -33,7 +33,7 @@ import org.jetbrains.research.refactorinsight.data.RefactoringEntry; import org.jetbrains.research.refactorinsight.data.RefactoringInfo; import org.jetbrains.research.refactorinsight.data.RefactoringLine; -import org.jetbrains.research.refactorinsight.services.RefactoringsBundle; +import org.jetbrains.research.refactorinsight.RefactorInsightBundle; import org.refactoringminer.api.RefactoringType; public class Utils { @@ -179,20 +179,20 @@ public static RefactoringInfo getMainRefactoringInfo(List infos if (infos.stream().anyMatch(ofType(RENAME_ATTRIBUTE)) && infos.stream().anyMatch(ofType(CHANGE_ATTRIBUTE_TYPE))) { info = infos.stream().filter(ofType(RENAME_ATTRIBUTE)).findFirst().get(); - info.setName(RefactoringsBundle.message("change.rename.attribute")); + info.setName(RefactorInsightBundle.message("change.rename.attribute")); } else if (infos.stream().anyMatch(ofType(RENAME_ATTRIBUTE))) { info = infos.stream().filter(ofType(RENAME_ATTRIBUTE)).findFirst().get(); - info.setName(RefactoringsBundle.message("rename.attribute")); + info.setName(RefactorInsightBundle.message("rename.attribute")); } else if (infos.stream().anyMatch(ofType(CHANGE_ATTRIBUTE_TYPE))) { info = infos.stream().filter(ofType(CHANGE_ATTRIBUTE_TYPE)).findFirst().get(); - info.setName(RefactoringsBundle.message("change.attribute")); + info.setName(RefactorInsightBundle.message("change.attribute")); } else if (infos.stream().anyMatch(ofType(CHANGE_VARIABLE_TYPE))) { info = infos.stream().filter(ofType(CHANGE_VARIABLE_TYPE)).findFirst().get(); - info.setName(RefactoringsBundle.message("change.rename.var")); + info.setName(RefactorInsightBundle.message("change.rename.var")); } else if (infos.stream().anyMatch(ofType(RENAME_PARAMETER)) && infos.stream().anyMatch(ofType(CHANGE_PARAMETER_TYPE))) { info = infos.stream().filter(ofType(RENAME_PARAMETER)).findFirst().get(); - info.setName(RefactoringsBundle.message("change.rename.param")); + info.setName(RefactorInsightBundle.message("change.rename.param")); } return info; } @@ -296,7 +296,7 @@ public static int getCommitCount(GitRepository repository) throws IOException { * @return the current version. */ public static String version() { - return RefactoringsBundle.message("version") + String.valueOf(Stream.of( + return RefactorInsightBundle.message("version") + String.valueOf(Stream.of( //all classes that can change RefactoringEntry.class, RefactoringInfo.class, diff --git a/src/main/resources/RefactoringsBundle.properties b/src/main/resources/RefactorInsightBundle.properties similarity index 100% rename from src/main/resources/RefactoringsBundle.properties rename to src/main/resources/RefactorInsightBundle.properties From 3b5c0205b03fecda30a3df14dd0fb17d2ae00f04 Mon Sep 17 00:00:00 2001 From: Zarina Kurbatova Date: Mon, 3 Aug 2020 15:53:25 +0300 Subject: [PATCH 2/7] Fix checkstyle issues --- .../services/MiningService.java | 149 ++++++++++-------- 1 file changed, 80 insertions(+), 69 deletions(-) diff --git a/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java b/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java index ff7202c4..2a610ebb 100644 --- a/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java +++ b/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java @@ -23,7 +23,14 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.*; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import org.jetbrains.annotations.NotNull; @@ -41,7 +48,7 @@ * it stores and persists the detected refactoring data in .idea/refactorings.xml file. */ @State(name = "MiningRefactoringsState", - storages = {@Storage("refactorings.xml")}) + storages = {@Storage("refactorings.xml")}) @Service public class MiningService implements PersistentStateComponent { @@ -180,81 +187,85 @@ public void mineAndWait(GitRepository repository) { } } - /** - * Mine refactorings in the specific commit. - * - * @param commit to be mined. - * @param project current project. - * @param info to be updated. - */ - public void mineAtCommit(VcsCommitMetadata commit, Project project, GitWindow info) { - ProgressManager.getInstance() - .run(new Task.Backgroundable(project, String.format( - RefactorInsightBundle.message("mining.at"), commit.getId().asString())) { - - @Override - public void onFinished() { - if (contains(commit.getId().asString())) { - ApplicationManager.getApplication() - .invokeLater(() -> info.refresh(commit.getId().asString())); - } - } - - @Override - public void run(@NotNull ProgressIndicator progressIndicator) { - try { - runWithCheckCanceled(() -> { - CommitMiner.mineAtCommitTimeout(commit, innerState.refactoringsMap.map, project); - return null; - }, - progressIndicator); - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - } + /** + * Mine refactorings in the specific commit. + * + * @param commit to be mined. + * @param project current project. + * @param info to be updated. + */ + public void mineAtCommit(VcsCommitMetadata commit, Project project, GitWindow info) { + ProgressManager.getInstance() + .run(new Task.Backgroundable(project, String.format( + RefactorInsightBundle.message("mining.at"), commit.getId().asString())) { - /** - * Allows to interrupt a process which does not performs checkCancelled() calls by itself. - */ - private void runWithCheckCanceled(@NotNull final Callable callable, @NotNull final ProgressIndicator indicator) throws Exception { - final Ref result = Ref.create(); - final Ref error = Ref.create(); + @Override + public void onFinished() { + if (contains(commit.getId().asString())) { + ApplicationManager.getApplication() + .invokeLater(() -> info.refresh(commit.getId().asString())); + } + } - Future future = ApplicationManager.getApplication().executeOnPooledThread(() -> ProgressManager.getInstance().executeProcessUnderProgress(() -> { + @Override + public void run(@NotNull ProgressIndicator progressIndicator) { try { - result.set(callable.call()); - } catch (Throwable t) { - error.set(t); + runWithCheckCanceled(() -> { + CommitMiner.mineAtCommitTimeout(commit, innerState.refactoringsMap.map, project); + return null; + }, + progressIndicator); + } catch (Exception e) { + e.printStackTrace(); } - }, indicator)); + } + }); + } - try { - runWithCheckCanceled(future, indicator); - ExceptionUtil.rethrowAll(error.get()); - } catch (ProcessCanceledException e) { - future.cancel(true); - throw e; - } + /** + * Allows to interrupt a process which does not performs checkCancelled() calls by itself. + */ + private void runWithCheckCanceled(@NotNull final Callable callable, + @NotNull final ProgressIndicator indicator) throws Exception { + final Ref result = Ref.create(); + final Ref error = Ref.create(); + + Future future = ApplicationManager.getApplication().executeOnPooledThread( + () -> ProgressManager.getInstance().executeProcessUnderProgress(() -> { + try { + result.set(callable.call()); + } catch (Throwable t) { + error.set(t); + } + }, indicator) + ); + + try { + runWithCheckCanceled(future, indicator); + ExceptionUtil.rethrowAll(error.get()); + } catch (ProcessCanceledException e) { + future.cancel(true); + throw e; } + } - /** - * Waits for {@code future} to be complete, or the current thread's indicator to be canceled. - */ - private void runWithCheckCanceled(@NotNull Future future, - @NotNull final ProgressIndicator indicator) throws ExecutionException { - while (true) { - indicator.checkCanceled(); - try { - future.get(10, TimeUnit.MILLISECONDS); - return; - } catch (InterruptedException e) { - throw new ProcessCanceledException(e); - } catch (TimeoutException ignored) { - } - } + /** + * Waits for {@code future} to be complete, or the current thread's indicator to be canceled. + */ + private void runWithCheckCanceled(@NotNull Future future, + @NotNull final ProgressIndicator indicator) throws ExecutionException { + while (true) { + indicator.checkCanceled(); + try { + future.get(10, TimeUnit.MILLISECONDS); + return; + } catch (InterruptedException e) { + throw new ProcessCanceledException(e); + } catch (TimeoutException ignored) { + ignored.printStackTrace(); + } } + } public Map> getRefactoringHistory() { return methodHistory; From c8de61c4bcbf45a100317987421753004c1e7d27 Mon Sep 17 00:00:00 2001 From: Zarina Kurbatova Date: Tue, 4 Aug 2020 13:26:07 +0300 Subject: [PATCH 3/7] Use Runnable instead of Callable, move methods to IdeUtils class --- .../services/MiningService.java | 55 +---------------- .../refactorinsight/utils/IdeUtils.java | 60 +++++++++++++++++++ 2 files changed, 62 insertions(+), 53 deletions(-) create mode 100644 src/main/java/org/jetbrains/research/refactorinsight/utils/IdeUtils.java diff --git a/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java b/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java index 2a610ebb..30a72bb2 100644 --- a/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java +++ b/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java @@ -6,13 +6,10 @@ import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; -import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Ref; -import com.intellij.util.ExceptionUtil; import com.intellij.util.xmlb.annotations.OptionTag; import com.intellij.vcs.log.VcsCommitMetadata; import git4idea.history.GitHistoryUtils; @@ -23,14 +20,10 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import org.jetbrains.annotations.NotNull; @@ -41,6 +34,8 @@ import org.jetbrains.research.refactorinsight.ui.windows.GitWindow; import org.jetbrains.research.refactorinsight.utils.Utils; +import static org.jetbrains.research.refactorinsight.utils.IdeUtils.runWithCheckCanceled; + /** * This is the MiningService. * It computes, process and stores the data retrieved from RefactoringMiner. @@ -212,7 +207,6 @@ public void run(@NotNull ProgressIndicator progressIndicator) { try { runWithCheckCanceled(() -> { CommitMiner.mineAtCommitTimeout(commit, innerState.refactoringsMap.map, project); - return null; }, progressIndicator); } catch (Exception e) { @@ -222,51 +216,6 @@ public void run(@NotNull ProgressIndicator progressIndicator) { }); } - /** - * Allows to interrupt a process which does not performs checkCancelled() calls by itself. - */ - private void runWithCheckCanceled(@NotNull final Callable callable, - @NotNull final ProgressIndicator indicator) throws Exception { - final Ref result = Ref.create(); - final Ref error = Ref.create(); - - Future future = ApplicationManager.getApplication().executeOnPooledThread( - () -> ProgressManager.getInstance().executeProcessUnderProgress(() -> { - try { - result.set(callable.call()); - } catch (Throwable t) { - error.set(t); - } - }, indicator) - ); - - try { - runWithCheckCanceled(future, indicator); - ExceptionUtil.rethrowAll(error.get()); - } catch (ProcessCanceledException e) { - future.cancel(true); - throw e; - } - } - - /** - * Waits for {@code future} to be complete, or the current thread's indicator to be canceled. - */ - private void runWithCheckCanceled(@NotNull Future future, - @NotNull final ProgressIndicator indicator) throws ExecutionException { - while (true) { - indicator.checkCanceled(); - try { - future.get(10, TimeUnit.MILLISECONDS); - return; - } catch (InterruptedException e) { - throw new ProcessCanceledException(e); - } catch (TimeoutException ignored) { - ignored.printStackTrace(); - } - } - } - public Map> getRefactoringHistory() { return methodHistory; } diff --git a/src/main/java/org/jetbrains/research/refactorinsight/utils/IdeUtils.java b/src/main/java/org/jetbrains/research/refactorinsight/utils/IdeUtils.java new file mode 100644 index 00000000..c7a880d6 --- /dev/null +++ b/src/main/java/org/jetbrains/research/refactorinsight/utils/IdeUtils.java @@ -0,0 +1,60 @@ +package org.jetbrains.research.refactorinsight.utils; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.util.Ref; +import com.intellij.util.ExceptionUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class IdeUtils { + /** + * Allows to interrupt a process which does not performs checkCancelled() calls by itself. + */ + public static void runWithCheckCanceled(@NotNull final Runnable runnable, + @NotNull final ProgressIndicator indicator) throws Exception { + final Ref error = Ref.create(); + + Future future = ApplicationManager.getApplication().executeOnPooledThread( + () -> ProgressManager.getInstance().executeProcessUnderProgress(() -> { + try { + runnable.run(); + } catch (Throwable t) { + error.set(t); + } + }, indicator) + ); + + try { + runWithCheckCanceled(future, indicator); + ExceptionUtil.rethrowAll(error.get()); + } catch (ProcessCanceledException e) { + future.cancel(true); + throw e; + } + } + + /** + * Waits for {@code future} to be complete, or the current thread's indicator to be canceled. + */ + private static void runWithCheckCanceled(@NotNull Future future, + @NotNull final ProgressIndicator indicator) throws ExecutionException { + while (true) { + indicator.checkCanceled(); + try { + future.get(10, TimeUnit.MILLISECONDS); + return; + } catch (InterruptedException e) { + throw new ProcessCanceledException(e); + } catch (TimeoutException ignored) { + ignored.printStackTrace(); + } + } + } +} From 315652ef9ba7ee08a98bf8955f99da01ba1f47e4 Mon Sep 17 00:00:00 2001 From: Zarina Kurbatova Date: Tue, 4 Aug 2020 16:48:46 +0300 Subject: [PATCH 4/7] Add logger --- .../refactorinsight/services/MiningService.java | 12 +++++++----- .../research/refactorinsight/utils/IdeUtils.java | 6 +++++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java b/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java index 30a72bb2..1164f17e 100644 --- a/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java +++ b/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java @@ -10,6 +10,7 @@ import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.util.xmlb.annotations.OptionTag; import com.intellij.vcs.log.VcsCommitMetadata; import git4idea.history.GitHistoryUtils; @@ -51,6 +52,7 @@ public class MiningService implements PersistentStateComponent(); private boolean mining = false; private MyState innerState = new MyState(); + private final Logger logger = Logger.getInstance(MiningService.class); public MiningService() { } @@ -205,12 +207,12 @@ public void onFinished() { @Override public void run(@NotNull ProgressIndicator progressIndicator) { try { - runWithCheckCanceled(() -> { - CommitMiner.mineAtCommitTimeout(commit, innerState.refactoringsMap.map, project); - }, - progressIndicator); + runWithCheckCanceled( + () -> CommitMiner.mineAtCommitTimeout(commit, innerState.refactoringsMap.map, project), + progressIndicator + ); } catch (Exception e) { - e.printStackTrace(); + logger.info(String.format("The mining of refactorings at the commit %s was canceled", commit.getId())); } } }); diff --git a/src/main/java/org/jetbrains/research/refactorinsight/utils/IdeUtils.java b/src/main/java/org/jetbrains/research/refactorinsight/utils/IdeUtils.java index c7a880d6..c7d57efc 100644 --- a/src/main/java/org/jetbrains/research/refactorinsight/utils/IdeUtils.java +++ b/src/main/java/org/jetbrains/research/refactorinsight/utils/IdeUtils.java @@ -1,6 +1,7 @@ package org.jetbrains.research.refactorinsight.utils; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; @@ -14,6 +15,9 @@ import java.util.concurrent.TimeoutException; public class IdeUtils { + + private static final Logger logger = Logger.getInstance(IdeUtils.class); + /** * Allows to interrupt a process which does not performs checkCancelled() calls by itself. */ @@ -53,7 +57,7 @@ private static void runWithCheckCanceled(@NotNull Future future, } catch (InterruptedException e) { throw new ProcessCanceledException(e); } catch (TimeoutException ignored) { - ignored.printStackTrace(); + logger.info("The timeout has been exceeded while checking task cancellation"); } } } From 3b1366a231984a1f50f6842d1dace358a5693cc6 Mon Sep 17 00:00:00 2001 From: ioana Date: Thu, 6 Aug 2020 14:20:34 +0300 Subject: [PATCH 5/7] Add timeout when checking for the task being cancelled --- .../services/MiningService.java | 4 +-- .../refactorinsight/utils/IdeUtils.java | 31 ++++++++++++++----- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java b/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java index 1164f17e..b1368185 100644 --- a/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java +++ b/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java @@ -208,8 +208,8 @@ public void onFinished() { public void run(@NotNull ProgressIndicator progressIndicator) { try { runWithCheckCanceled( - () -> CommitMiner.mineAtCommitTimeout(commit, innerState.refactoringsMap.map, project), - progressIndicator + () -> CommitMiner.mineAtCommit(commit, innerState.refactoringsMap.map, project), + progressIndicator, commit, project ); } catch (Exception e) { logger.info(String.format("The mining of refactorings at the commit %s was canceled", commit.getId())); diff --git a/src/main/java/org/jetbrains/research/refactorinsight/utils/IdeUtils.java b/src/main/java/org/jetbrains/research/refactorinsight/utils/IdeUtils.java index c7d57efc..91e8ecd7 100644 --- a/src/main/java/org/jetbrains/research/refactorinsight/utils/IdeUtils.java +++ b/src/main/java/org/jetbrains/research/refactorinsight/utils/IdeUtils.java @@ -5,14 +5,21 @@ import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Ref; import com.intellij.util.ExceptionUtil; +import com.intellij.vcs.log.VcsCommitMetadata; +import java.util.ArrayList; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import org.jetbrains.annotations.NotNull; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.jetbrains.research.refactorinsight.data.RefactoringEntry; +import org.jetbrains.research.refactorinsight.services.MiningService; public class IdeUtils { @@ -22,9 +29,9 @@ public class IdeUtils { * Allows to interrupt a process which does not performs checkCancelled() calls by itself. */ public static void runWithCheckCanceled(@NotNull final Runnable runnable, - @NotNull final ProgressIndicator indicator) throws Exception { + @NotNull final ProgressIndicator indicator, + VcsCommitMetadata commit, Project project) throws Exception { final Ref error = Ref.create(); - Future future = ApplicationManager.getApplication().executeOnPooledThread( () -> ProgressManager.getInstance().executeProcessUnderProgress(() -> { try { @@ -34,9 +41,8 @@ public static void runWithCheckCanceled(@NotNull final Runnable runnable, } }, indicator) ); - try { - runWithCheckCanceled(future, indicator); + runWithCheckCanceled(future, indicator, commit, project); ExceptionUtil.rethrowAll(error.get()); } catch (ProcessCanceledException e) { future.cancel(true); @@ -45,11 +51,14 @@ public static void runWithCheckCanceled(@NotNull final Runnable runnable, } /** - * Waits for {@code future} to be complete, or the current thread's indicator to be canceled. + * Waits for {@code future} to be complete or reach the maximum allowed mining time of 60 sec, + * or the current thread's indicator to be canceled. */ private static void runWithCheckCanceled(@NotNull Future future, - @NotNull final ProgressIndicator indicator) throws ExecutionException { - while (true) { + @NotNull final ProgressIndicator indicator, + VcsCommitMetadata commit, Project project) throws ExecutionException { + int timeout = 6000; + while (timeout > 0) { indicator.checkCanceled(); try { future.get(10, TimeUnit.MILLISECONDS); @@ -59,6 +68,14 @@ private static void runWithCheckCanceled(@NotNull Future future, } catch (TimeoutException ignored) { logger.info("The timeout has been exceeded while checking task cancellation"); } + timeout -= 1; + } + if (timeout == 0) { + RefactoringEntry refactoringEntry = RefactoringEntry + .convert(new ArrayList<>(), commit, project); + refactoringEntry.setTimeout(true); + MiningService.getInstance(project).getState().refactoringsMap.map.put(commit.getId().asString(), + refactoringEntry); } } } From 7810f11d609d90199ae974169cf9fcff150010dc Mon Sep 17 00:00:00 2001 From: Bob Brockbernd Date: Thu, 6 Aug 2020 16:50:05 +0200 Subject: [PATCH 6/7] WIP: added canceling when walking over vcs table --- .../SingleCommitRefactoringTask.java | 142 ++++++++++++++++++ .../services/MiningService.java | 38 ++--- .../refactorinsight/utils/IdeUtils.java | 81 ---------- 3 files changed, 151 insertions(+), 110 deletions(-) create mode 100644 src/main/java/org/jetbrains/research/refactorinsight/processors/SingleCommitRefactoringTask.java diff --git a/src/main/java/org/jetbrains/research/refactorinsight/processors/SingleCommitRefactoringTask.java b/src/main/java/org/jetbrains/research/refactorinsight/processors/SingleCommitRefactoringTask.java new file mode 100644 index 00000000..b1f4dcf1 --- /dev/null +++ b/src/main/java/org/jetbrains/research/refactorinsight/processors/SingleCommitRefactoringTask.java @@ -0,0 +1,142 @@ +package org.jetbrains.research.refactorinsight.processors; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Ref; +import com.intellij.util.ExceptionUtil; +import com.intellij.vcs.log.VcsCommitMetadata; +import java.util.ArrayList; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.research.refactorinsight.RefactorInsightBundle; +import org.jetbrains.research.refactorinsight.data.RefactoringEntry; +import org.jetbrains.research.refactorinsight.services.MiningService; +import org.jetbrains.research.refactorinsight.ui.windows.GitWindow; + +public class SingleCommitRefactoringTask extends Task.Backgroundable { + + private Project project; + private VcsCommitMetadata commit; + private GitWindow window; + private MiningService service; + private boolean canceled = false; + private final Logger logger = Logger.getInstance(SingleCommitRefactoringTask.class); + + /** + * Cancelable mining task for mining a single commit. + * + * @param project Current IDEA project + * @param commit Commit meta data + * @param window Git Window for callback + */ + public SingleCommitRefactoringTask( + @Nullable Project project, + VcsCommitMetadata commit, + GitWindow window) { + super(project, + String.format(RefactorInsightBundle.message("mining.at"), commit.getId().asString())); + this.project = project; + this.commit = commit; + this.window = window; + this.service = ServiceManager.getService(project, MiningService.class); + } + + @Override + public void onCancel() { + super.onCancel(); + } + + @Override + public void onFinished() { + super.onFinished(); + if (service.containsCommit(commit.getId().asString())) { + System.out.println(RefactorInsightBundle.message("finished")); + ApplicationManager.getApplication() + .invokeLater(() -> window.refresh(commit.getId().asString())); + } + } + + @Override + public void run(@NotNull ProgressIndicator progressIndicator) { + try { + runWithCheckCanceled( + () -> CommitMiner.mineAtCommit(commit, service.getState().refactoringsMap.map, project), + progressIndicator, commit, project + ); + } catch (Exception e) { + logger.info(String.format("The mining of refactorings at the commit %s was canceled", commit.getId())); + } + } + + public void cancel() { + canceled = true; + } + + /** + * Allows to interrupt a process which does not performs checkCancelled() calls by itself. + */ + public void runWithCheckCanceled(@NotNull final Runnable runnable, + @NotNull final ProgressIndicator indicator, + VcsCommitMetadata commit, Project project) throws Exception { + final Ref error = Ref.create(); + Future future = ApplicationManager.getApplication().executeOnPooledThread( + () -> ProgressManager.getInstance().executeProcessUnderProgress(() -> { + try { + runnable.run(); + } catch (Throwable t) { + error.set(t); + } + }, indicator) + ); + try { + runWithCheckCanceled(future, indicator, commit, project); + ExceptionUtil.rethrowAll(error.get()); + } catch (ProcessCanceledException e) { + future.cancel(true); + throw e; + } + } + + /** + * Waits for {@code future} to be complete or reach the maximum allowed mining time of 60 sec, + * or the current thread's indicator to be canceled. + */ + private void runWithCheckCanceled(@NotNull Future future, + @NotNull final ProgressIndicator indicator, + VcsCommitMetadata commit, Project project) throws + ExecutionException { + int timeout = 6000; + while (timeout > 0) { + if (canceled) { + indicator.cancel(); + } + indicator.checkCanceled(); + try { + future.get(10, TimeUnit.MILLISECONDS); + return; + } catch (InterruptedException e) { + throw new ProcessCanceledException(e); + } catch (TimeoutException ignored) { + logger.info("The timeout has been exceeded while checking task cancellation"); + } + timeout -= 1; + } + if (timeout == 0) { + RefactoringEntry refactoringEntry = RefactoringEntry + .convert(new ArrayList<>(), commit, project); + refactoringEntry.setTimeout(true); + MiningService.getInstance(project).getState().refactoringsMap.map.put(commit.getId().asString(), + refactoringEntry); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java b/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java index 3ee16aa3..3b8efd9b 100644 --- a/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java +++ b/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java @@ -1,6 +1,5 @@ package org.jetbrains.research.refactorinsight.services; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.components.Service; import com.intellij.openapi.components.ServiceManager; @@ -32,11 +31,10 @@ import org.jetbrains.research.refactorinsight.data.RefactoringEntry; import org.jetbrains.research.refactorinsight.data.RefactoringInfo; import org.jetbrains.research.refactorinsight.processors.CommitMiner; +import org.jetbrains.research.refactorinsight.processors.SingleCommitRefactoringTask; import org.jetbrains.research.refactorinsight.ui.windows.GitWindow; import org.jetbrains.research.refactorinsight.utils.Utils; -import static org.jetbrains.research.refactorinsight.utils.IdeUtils.runWithCheckCanceled; - /** * This is the MiningService. * It computes, process and stores the data retrieved from RefactoringMiner. @@ -52,6 +50,7 @@ public class MiningService implements PersistentStateComponent(); private boolean mining = false; private MyState innerState = new MyState(); + private SingleCommitRefactoringTask task = null; private final Logger logger = Logger.getInstance(MiningService.class); public MiningService() { @@ -189,33 +188,14 @@ public void mineAndWait(GitRepository repository) { * * @param commit to be mined. * @param project current project. - * @param info to be updated. + * @param window to be updated. */ - public void mineAtCommit(VcsCommitMetadata commit, Project project, GitWindow info) { - ProgressManager.getInstance() - .run(new Task.Backgroundable(project, String.format( - RefactorInsightBundle.message("mining.at"), commit.getId().asString())) { - - @Override - public void onFinished() { - if (containsCommit(commit.getId().asString())) { - ApplicationManager.getApplication() - .invokeLater(() -> info.refresh(commit.getId().asString())); - } - } - - @Override - public void run(@NotNull ProgressIndicator progressIndicator) { - try { - runWithCheckCanceled( - () -> CommitMiner.mineAtCommit(commit, innerState.refactoringsMap.map, project), - progressIndicator, commit, project - ); - } catch (Exception e) { - logger.info(String.format("The mining of refactorings at the commit %s was canceled", commit.getId())); - } - } - }); + public void mineAtCommit(VcsCommitMetadata commit, Project project, GitWindow window) { + if (task != null) { + task.cancel(); + } + task = new SingleCommitRefactoringTask(project, commit, window); + ProgressManager.getInstance().run(task); } public Map> getRefactoringHistory() { diff --git a/src/main/java/org/jetbrains/research/refactorinsight/utils/IdeUtils.java b/src/main/java/org/jetbrains/research/refactorinsight/utils/IdeUtils.java index 91e8ecd7..e69de29b 100644 --- a/src/main/java/org/jetbrains/research/refactorinsight/utils/IdeUtils.java +++ b/src/main/java/org/jetbrains/research/refactorinsight/utils/IdeUtils.java @@ -1,81 +0,0 @@ -package org.jetbrains.research.refactorinsight.utils; - -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.progress.ProcessCanceledException; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Ref; -import com.intellij.util.ExceptionUtil; -import com.intellij.vcs.log.VcsCommitMetadata; -import java.util.ArrayList; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import org.jetbrains.annotations.NotNull; - -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import org.jetbrains.research.refactorinsight.data.RefactoringEntry; -import org.jetbrains.research.refactorinsight.services.MiningService; - -public class IdeUtils { - - private static final Logger logger = Logger.getInstance(IdeUtils.class); - - /** - * Allows to interrupt a process which does not performs checkCancelled() calls by itself. - */ - public static void runWithCheckCanceled(@NotNull final Runnable runnable, - @NotNull final ProgressIndicator indicator, - VcsCommitMetadata commit, Project project) throws Exception { - final Ref error = Ref.create(); - Future future = ApplicationManager.getApplication().executeOnPooledThread( - () -> ProgressManager.getInstance().executeProcessUnderProgress(() -> { - try { - runnable.run(); - } catch (Throwable t) { - error.set(t); - } - }, indicator) - ); - try { - runWithCheckCanceled(future, indicator, commit, project); - ExceptionUtil.rethrowAll(error.get()); - } catch (ProcessCanceledException e) { - future.cancel(true); - throw e; - } - } - - /** - * Waits for {@code future} to be complete or reach the maximum allowed mining time of 60 sec, - * or the current thread's indicator to be canceled. - */ - private static void runWithCheckCanceled(@NotNull Future future, - @NotNull final ProgressIndicator indicator, - VcsCommitMetadata commit, Project project) throws ExecutionException { - int timeout = 6000; - while (timeout > 0) { - indicator.checkCanceled(); - try { - future.get(10, TimeUnit.MILLISECONDS); - return; - } catch (InterruptedException e) { - throw new ProcessCanceledException(e); - } catch (TimeoutException ignored) { - logger.info("The timeout has been exceeded while checking task cancellation"); - } - timeout -= 1; - } - if (timeout == 0) { - RefactoringEntry refactoringEntry = RefactoringEntry - .convert(new ArrayList<>(), commit, project); - refactoringEntry.setTimeout(true); - MiningService.getInstance(project).getState().refactoringsMap.map.put(commit.getId().asString(), - refactoringEntry); - } - } -} From 3e5fbd8dbf688e506315dd8088247e45fc37b32c Mon Sep 17 00:00:00 2001 From: Bob Brockbernd Date: Tue, 11 Aug 2020 00:55:19 +0200 Subject: [PATCH 7/7] Shared jgit instance for all mining tasks --- .../processors/CommitMiner.java | 70 ++----------------- .../SingleCommitRefactoringTask.java | 15 ++-- .../services/MiningService.java | 25 ++++++- 3 files changed, 38 insertions(+), 72 deletions(-) diff --git a/src/main/java/org/jetbrains/research/refactorinsight/processors/CommitMiner.java b/src/main/java/org/jetbrains/research/refactorinsight/processors/CommitMiner.java index 933f1c1e..d95ea7c1 100644 --- a/src/main/java/org/jetbrains/research/refactorinsight/processors/CommitMiner.java +++ b/src/main/java/org/jetbrains/research/refactorinsight/processors/CommitMiner.java @@ -1,5 +1,6 @@ package org.jetbrains.research.refactorinsight.processors; +import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; @@ -19,6 +20,7 @@ import org.eclipse.jgit.lib.Repository; import org.jetbrains.research.refactorinsight.data.RefactoringEntry; import org.jetbrains.research.refactorinsight.RefactorInsightBundle; +import org.jetbrains.research.refactorinsight.services.MiningService; import org.refactoringminer.api.GitHistoryRefactoringMiner; import org.refactoringminer.api.GitService; import org.refactoringminer.api.Refactoring; @@ -51,40 +53,29 @@ public class CommitMiner implements Consumer { public CommitMiner(ExecutorService pool, Map map, GitRepository repository, AtomicInteger commitsDone, ProgressIndicator progressIndicator, int limit) { - this.pool = pool; this.map = map; myProject = repository.getProject(); //NB: nullable, check if initialized correctly - myRepository = openRepository(myProject.getBasePath()); + myRepository = ServiceManager.getService(myProject, MiningService.class).getRepository(); this.commitsDone = commitsDone; this.progressIndicator = progressIndicator; this.limit = limit; } - private static Repository openRepository(final String path) { - try { - return new GitServiceImpl().openRepository(path); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - /** * Method that mines only one commit. * * @param commit commit metadata * @param map the inner map that should be updated * @param project the current project + * @param repository Git Repository */ public static void mineAtCommit(VcsCommitMetadata commit, Map map, - Project project) { - GitService gitService = new GitServiceImpl(); + Project project, Repository repository) { GitHistoryRefactoringMiner miner = new GitHistoryRefactoringMinerImpl(); try { - miner.detectAtCommit(gitService.openRepository(project.getBasePath()), - commit.getId().asString(), + miner.detectAtCommit(repository, commit.getId().asString(), new RefactoringHandler() { @Override public void handle(String commitId, List refactorings) { @@ -97,55 +88,6 @@ public void handle(String commitId, List refactorings) { } } - /** - * Mines a single commit if the mining process does not take longer than - * 60 seconds. - * - * @param commit to be mined - * @param map refactorings map - * @param project the current project - */ - public static void mineAtCommitTimeout(VcsCommitMetadata commit, - Map map, - Project project) { - GitService gitService = new GitServiceImpl(); - GitHistoryRefactoringMiner miner = new GitHistoryRefactoringMinerImpl(); - ExecutorService service = Executors.newSingleThreadExecutor(); - Future f = null; - final Repository repository; - try { - repository = gitService.openRepository(project.getBasePath()); - } catch (Exception e) { - return; - } - try { - Runnable r = () -> { - miner.detectAtCommit(repository, - commit.getId().asString(), new RefactoringHandler() { - @Override - public void handle(String commitId, List refactorings) { - map.put(commitId, - RefactoringEntry - .convert(refactorings, commit, project)); - } - }); - }; - f = service.submit(r); - f.get(60, TimeUnit.SECONDS); - } catch (TimeoutException e) { - if (f.cancel(true)) { - RefactoringEntry refactoringEntry = RefactoringEntry - .convert(new ArrayList<>(), commit, project); - refactoringEntry.setTimeout(true); - map.put(commit.getId().asString(), refactoringEntry); - } - } catch (Exception e) { - e.printStackTrace(); - } finally { - service.shutdown(); - } - } - /** * Mines a gitCommit. * Method that calls RefactoringMiner and updates the refactoring map. diff --git a/src/main/java/org/jetbrains/research/refactorinsight/processors/SingleCommitRefactoringTask.java b/src/main/java/org/jetbrains/research/refactorinsight/processors/SingleCommitRefactoringTask.java index b1f4dcf1..cfbbeaf0 100644 --- a/src/main/java/org/jetbrains/research/refactorinsight/processors/SingleCommitRefactoringTask.java +++ b/src/main/java/org/jetbrains/research/refactorinsight/processors/SingleCommitRefactoringTask.java @@ -16,6 +16,7 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.eclipse.jgit.lib.Repository; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.research.refactorinsight.RefactorInsightBundle; @@ -25,10 +26,11 @@ public class SingleCommitRefactoringTask extends Task.Backgroundable { - private Project project; - private VcsCommitMetadata commit; - private GitWindow window; - private MiningService service; + private final Project project; + private final VcsCommitMetadata commit; + private final GitWindow window; + private final MiningService service; + private final Repository myRepository; private boolean canceled = false; private final Logger logger = Logger.getInstance(SingleCommitRefactoringTask.class); @@ -44,11 +46,12 @@ public SingleCommitRefactoringTask( VcsCommitMetadata commit, GitWindow window) { super(project, - String.format(RefactorInsightBundle.message("mining.at"), commit.getId().asString())); + String.format(RefactorInsightBundle.message("mining.at"), commit.getId().toShortString())); this.project = project; this.commit = commit; this.window = window; this.service = ServiceManager.getService(project, MiningService.class); + this.myRepository = service.getRepository(); } @Override @@ -70,7 +73,7 @@ public void onFinished() { public void run(@NotNull ProgressIndicator progressIndicator) { try { runWithCheckCanceled( - () -> CommitMiner.mineAtCommit(commit, service.getState().refactoringsMap.map, project), + () -> CommitMiner.mineAtCommit(commit, service.getState().refactoringsMap.map, project, myRepository), progressIndicator, commit, project ); } catch (Exception e) { diff --git a/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java b/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java index 3b8efd9b..b34a3f0c 100644 --- a/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java +++ b/src/main/java/org/jetbrains/research/refactorinsight/services/MiningService.java @@ -9,7 +9,6 @@ import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.util.xmlb.annotations.OptionTag; import com.intellij.vcs.log.VcsCommitMetadata; import git4idea.history.GitHistoryUtils; @@ -26,6 +25,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import org.eclipse.jgit.lib.Repository; import org.jetbrains.annotations.NotNull; import org.jetbrains.research.refactorinsight.RefactorInsightBundle; import org.jetbrains.research.refactorinsight.data.RefactoringEntry; @@ -34,6 +34,7 @@ import org.jetbrains.research.refactorinsight.processors.SingleCommitRefactoringTask; import org.jetbrains.research.refactorinsight.ui.windows.GitWindow; import org.jetbrains.research.refactorinsight.utils.Utils; +import org.refactoringminer.util.GitServiceImpl; /** * This is the MiningService. @@ -51,7 +52,7 @@ public class MiningService implements PersistentStateComponent