From 302dda9ada8b8f4530f59e922e2c8e39180252db Mon Sep 17 00:00:00 2001 From: "Denis.Zhdanov" Date: Tue, 24 Jan 2012 10:32:36 +0400 Subject: [PATCH] IDEA-76142: Gradle support - cannot update IDEA projects once one of build.gradle files changes 1. Introduced 'gradle task' concept; 2. Introduced 'resolve gradle project' task; 3. Introduced 'resolve gradle project' action; 4. Refactored notifications processing; --- .../resources/i18n/GradleBundle.properties | 1 + .../action/GradleRefreshProjectAction.java | 34 ++++- .../importing/GradleModulesImporter.java | 66 +++------- .../importing/GradleProjectImportBuilder.java | 72 ++++------ .../GradleProgressNotificationManager.java | 1 + ...GradleProgressNotificationManagerImpl.java | 1 + .../GradleTaskNotificationEvent.java | 1 + .../GradleTaskNotificationListener.java | 1 + ...GradleTaskNotificationListenerAdapter.java | 1 + .../gradle/remote/GradleApiFacade.java | 22 ++++ .../gradle/remote/GradleProjectResolver.java | 2 +- ...moteGradleProgressNotificationManager.java | 2 +- .../gradle/remote/RemoteGradleService.java | 23 ++++ .../remote/impl/GradleApiFacadeImpl.java | 35 ++++- .../impl/GradleProjectResolverImpl.java | 38 ++++-- .../GradleProjectStructureChangesModel.java | 2 +- .../gradle/task/AbstractGradleTask.java | 124 ++++++++++++++++++ .../gradle/task/GradleResolveProjectTask.java | 53 ++++++++ .../plugins/gradle/task/GradleTask.java | 44 +++++++ .../{notification => task}/GradleTaskId.java | 11 +- .../plugins/gradle/task/GradleTaskState.java | 10 ++ .../GradleTaskType.java | 2 +- 22 files changed, 424 insertions(+), 122 deletions(-) create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/task/AbstractGradleTask.java create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/task/GradleResolveProjectTask.java create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/task/GradleTask.java rename plugins/gradle/src/org/jetbrains/plugins/gradle/{notification => task}/GradleTaskId.java (95%) create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/task/GradleTaskState.java rename plugins/gradle/src/org/jetbrains/plugins/gradle/{notification => task}/GradleTaskType.java (79%) diff --git a/plugins/gradle/resources/i18n/GradleBundle.properties b/plugins/gradle/resources/i18n/GradleBundle.properties index 2f02198626e0..3e5d96735950 100644 --- a/plugins/gradle/resources/i18n/GradleBundle.properties +++ b/plugins/gradle/resources/i18n/GradleBundle.properties @@ -47,6 +47,7 @@ gradle.import.text.error.file.module.test.output.location=Module test output loc gradle.import.text.incomplete.tooling.api=Gradle version contains incomplete tooling API gradle.sync.title.tab=project structure changes +gradle.sync.progress.text=Refreshing gradle project gradle.sync.change.type.gradle=Gradle local setting gradle.sync.change.type.intellij={0} local setting gradle.sync.change.type.conflict=Settings conflict diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleRefreshProjectAction.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleRefreshProjectAction.java index 100e52434711..d70525040139 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleRefreshProjectAction.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleRefreshProjectAction.java @@ -3,12 +3,18 @@ package org.jetbrains.plugins.gradle.action; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.gradle.config.GradleSettings; +import org.jetbrains.plugins.gradle.task.GradleResolveProjectTask; import org.jetbrains.plugins.gradle.util.GradleBundle; import java.io.File; +import java.util.concurrent.atomic.AtomicBoolean; /** * Forces the 'gradle' plugin to retrieve the most up-to-date info about the @@ -20,6 +26,8 @@ import java.io.File; */ public class GradleRefreshProjectAction extends AnAction implements DumbAware { + private final AtomicBoolean myInProgress = new AtomicBoolean(); + public GradleRefreshProjectAction() { getTemplatePresentation().setText(GradleBundle.message("gradle.action.refresh.project.text")); getTemplatePresentation().setDescription(GradleBundle.message("gradle.action.refresh.project.description")); @@ -33,12 +41,32 @@ public class GradleRefreshProjectAction extends AnAction implements DumbAware { return; } final String path = GradleSettings.getInstance(project).LINKED_PROJECT_FILE_PATH; - e.getPresentation().setVisible(path != null && new File(path).isFile()); + final boolean visible = path != null && new File(path).isFile(); + e.getPresentation().setVisible(visible); + e.getPresentation().setEnabled(!myInProgress.get()); } @Override public void actionPerformed(AnActionEvent e) { - // TODO den implement - int i = 1; + final Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext()); + if (project == null) { + e.getPresentation().setVisible(false); + return; + } + // Assuming that the linked project is available if this action is called (update() is successful) + final String projectPath = GradleSettings.getInstance(project).LINKED_PROJECT_FILE_PATH; + myInProgress.set(true); + ProgressManager.getInstance().run(new Task.Backgroundable(project, GradleBundle.message("gradle.sync.progress.text")) { + @Override + public void run(@NotNull final ProgressIndicator indicator) { + try { + GradleResolveProjectTask task = new GradleResolveProjectTask(project, projectPath, true); + task.execute(indicator); + } + finally { + myInProgress.set(false); + } + } + }); } } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/GradleModulesImporter.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/GradleModulesImporter.java index a6319895a139..0753b80c148f 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/GradleModulesImporter.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/GradleModulesImporter.java @@ -1,7 +1,6 @@ package org.jetbrains.plugins.gradle.importing; import com.intellij.openapi.application.*; -import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.module.ModifiableModuleModel; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; @@ -25,9 +24,7 @@ import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.model.*; -import org.jetbrains.plugins.gradle.notification.*; -import org.jetbrains.plugins.gradle.remote.GradleApiFacadeManager; -import org.jetbrains.plugins.gradle.remote.GradleProjectResolver; +import org.jetbrains.plugins.gradle.task.GradleResolveProjectTask; import org.jetbrains.plugins.gradle.util.GradleBundle; import org.jetbrains.plugins.gradle.util.GradleLog; @@ -73,21 +70,20 @@ public class GradleModulesImporter { */ @NotNull public Map importModules(@NotNull final Iterable modules, @Nullable final Project project, - @Nullable final ModifiableModuleModel model, @NotNull String gradleProjectPath, - @NotNull GradleProgressNotificationManager progressManager) + @Nullable final ModifiableModuleModel model, @NotNull String gradleProjectPath) { if (project == null) { return Collections.emptyMap(); } removeExistingModulesSettings(modules); if (!project.isInitialized()) { - myAlarm.addRequest(new ImportModulesTask(project, modules, gradleProjectPath, progressManager), PROJECT_INITIALISATION_DELAY_MS); + myAlarm.addRequest(new ImportModulesTask(project, modules, gradleProjectPath), PROJECT_INITIALISATION_DELAY_MS); return Collections.emptyMap(); } if (model == null) { return Collections.emptyMap(); } - return importModules(modules, model, project, gradleProjectPath, progressManager); + return importModules(modules, model, project, gradleProjectPath); } private static void removeExistingModulesSettings(@NotNull Iterable modules) { @@ -107,8 +103,7 @@ public class GradleModulesImporter { public Map importModules(@NotNull final Iterable modules, @NotNull final ModifiableModuleModel model, @NotNull final Project intellijProject, - @NotNull final String gradleProjectPath, - @NotNull final GradleProgressNotificationManager progressManager) + @NotNull final String gradleProjectPath) { final Map result = new HashMap(); UIUtil.invokeAndWaitIfNeeded(new Runnable() { @@ -123,7 +118,7 @@ public class GradleModulesImporter { result.putAll(moduleMappings); myAlarm.cancelAllRequests(); myAlarm.addRequest( - new SetupExternalLibrariesTask(moduleMappings, gradleProjectPath, intellijProject, progressManager), + new SetupExternalLibrariesTask(moduleMappings, gradleProjectPath, intellijProject), PROJECT_INITIALISATION_DELAY_MS ); } @@ -142,7 +137,7 @@ public class GradleModulesImporter { } /** - * Actual implementation of {@link #importModules(Iterable, Project, ModifiableModuleModel, String, GradleProgressNotificationManager)}. + * Actual implementation of {@link #importModules(Iterable, Project, ModifiableModuleModel, String)}. * Insists on all arguments to be ready to use. * * @param modules modules to import @@ -291,10 +286,8 @@ public class GradleModulesImporter { */ private static void setupLibraries(@NotNull final Map moduleMappings, @NotNull final Project intellijProject, - @NotNull final String gradleProjectPath, - @NotNull final GradleProgressNotificationManager progressManager) + @NotNull final String gradleProjectPath) { - final GradleApiFacadeManager manager = ServiceManager.getService(GradleApiFacadeManager.class); final Ref gradleProjectRef = new Ref(); final Runnable setupExternalDependenciesTask = new Runnable() { @@ -323,28 +316,11 @@ public class GradleModulesImporter { new Task.Backgroundable(intellijProject, GradleBundle.message("gradle.library.resolve.progress.text"), false) { @Override public void run(@NotNull final ProgressIndicator indicator) { - indicator.setIndeterminate(true); - GradleTaskNotificationListener progressListener = new GradleTaskNotificationListenerAdapter() { - @Override - public void onStatusChange(@NotNull GradleTaskNotificationEvent event) { - indicator.setText2(event.getDescription()); - } - }; - GradleTaskId taskId = GradleTaskId.create(GradleTaskType.RESOLVE_PROJECT); - progressManager.addNotificationListener(taskId, progressListener); - try { - GradleProjectResolver resolver = manager.getFacade().getResolver(); - - GradleProject projectWithResolvedLibraries = resolver.resolveProjectInfo(taskId, gradleProjectPath, true); - gradleProjectRef.set(projectWithResolvedLibraries); - ApplicationManager.getApplication().invokeLater(setupExternalDependenciesTask, ModalityState.NON_MODAL); - } - catch (Exception e) { - GradleLog.LOG.warn("Can't resolve external dependencies of the target gradle project (" + gradleProjectPath + ")", e); - } - finally { - progressManager.removeNotificationListener(progressListener); - } + GradleResolveProjectTask task = new GradleResolveProjectTask(intellijProject, gradleProjectPath, true); + task.execute(indicator); + GradleProject projectWithResolvedLibraries = task.getProject(); + gradleProjectRef.set(projectWithResolvedLibraries); + ApplicationManager.getApplication().invokeLater(setupExternalDependenciesTask, ModalityState.NON_MODAL); } }); } @@ -497,14 +473,11 @@ public class GradleModulesImporter { private final Project myProject; private final Iterable myModules; private final String myGradleProjectPath; - private final GradleProgressNotificationManager myProgressManger; - ImportModulesTask(@NotNull Project project, @NotNull Iterable modules, @NotNull String gradleProjectPath, - @NotNull GradleProgressNotificationManager progressManager) { + ImportModulesTask(@NotNull Project project, @NotNull Iterable modules, @NotNull String gradleProjectPath) { myProject = project; myModules = modules; myGradleProjectPath = gradleProjectPath; - myProgressManger = progressManager; } @Override @@ -512,7 +485,7 @@ public class GradleModulesImporter { myAlarm.cancelAllRequests(); if (!myProject.isInitialized()) { myAlarm.addRequest( - new ImportModulesTask(myProject, myModules, myGradleProjectPath, myProgressManger), + new ImportModulesTask(myProject, myModules, myGradleProjectPath), PROJECT_INITIALISATION_DELAY_MS ); return; @@ -524,7 +497,7 @@ public class GradleModulesImporter { } }.execute().getResultObject(); - importModules(myModules, model, myProject, myGradleProjectPath, myProgressManger); + importModules(myModules, model, myProject, myGradleProjectPath); } } @@ -533,22 +506,19 @@ public class GradleModulesImporter { private final Map myModules; private final String myGradleProjectPath; private final Project myIntellijProject; - private final GradleProgressNotificationManager myProgressManager; SetupExternalLibrariesTask(@NotNull Map modules, @NotNull String gradleProjectPath, - @NotNull Project intellijProject, - @NotNull GradleProgressNotificationManager manager) + @NotNull Project intellijProject) { myModules = modules; myGradleProjectPath = gradleProjectPath; myIntellijProject = intellijProject; - myProgressManager = manager; } @Override public void run() { - setupLibraries(myModules, myIntellijProject, myGradleProjectPath, myProgressManager); + setupLibraries(myModules, myIntellijProject, myGradleProjectPath); } } } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/GradleProjectImportBuilder.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/GradleProjectImportBuilder.java index 9c7a99224f4a..d8a5a8d4a29c 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/GradleProjectImportBuilder.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/importing/GradleProjectImportBuilder.java @@ -3,11 +3,9 @@ package org.jetbrains.plugins.gradle.importing; import com.intellij.execution.rmi.RemoteUtil; import com.intellij.ide.util.PropertiesComponent; import com.intellij.ide.util.projectWizard.WizardContext; -import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.module.ModifiableModuleModel; import com.intellij.openapi.module.Module; import com.intellij.openapi.options.ConfigurationException; -import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; @@ -28,10 +26,8 @@ import org.jetbrains.plugins.gradle.config.GradleSettings; import org.jetbrains.plugins.gradle.model.GradleEntity; import org.jetbrains.plugins.gradle.model.GradleModule; import org.jetbrains.plugins.gradle.model.GradleProject; -import org.jetbrains.plugins.gradle.notification.*; import org.jetbrains.plugins.gradle.remote.GradleApiException; -import org.jetbrains.plugins.gradle.remote.GradleApiFacadeManager; -import org.jetbrains.plugins.gradle.remote.GradleProjectResolver; +import org.jetbrains.plugins.gradle.task.GradleResolveProjectTask; import org.jetbrains.plugins.gradle.ui.GradleIcons; import org.jetbrains.plugins.gradle.util.GradleBundle; import org.jetbrains.plugins.gradle.util.GradleLog; @@ -54,15 +50,9 @@ public class GradleProjectImportBuilder extends ProjectImportBuilder myModuleMappings = new HashMap(); - private final GradleProgressNotificationManager myProgressManager; - private GradleProject myGradleProject; private File myProjectFile; - public GradleProjectImportBuilder(@NotNull GradleProgressNotificationManager progressManager) { - myProgressManager = progressManager; - } - @Override public String getName() { return GradleBundle.message("gradle.name"); @@ -107,7 +97,7 @@ public class GradleProjectImportBuilder extends ProjectImportBuilder mappings = - importer.importModules(myModuleMappings.values(), project, model, myProjectFile.getAbsolutePath(), myProgressManager); + importer.importModules(myModuleMappings.values(), project, model, myProjectFile.getAbsolutePath()); return new ArrayList(mappings.values()); } @@ -165,48 +155,36 @@ public class GradleProjectImportBuilder extends ProjectImportBuilder errorReason = new Ref(); try { - Project project = getProject(wizardContext); + final Project project = getProject(wizardContext); ProgressManager.getInstance().run(new Task.Modal(project, GradleBundle.message("gradle.import.progress.text"), true) { @SuppressWarnings("ThrowableResultOfMethodCallIgnored") @Override public void run(@NotNull final ProgressIndicator indicator) { - indicator.setIndeterminate(true); - GradleApiFacadeManager manager = ServiceManager.getService(GradleApiFacadeManager.class); - GradleTaskNotificationListener gradleProgressListener = new GradleTaskNotificationListenerAdapter() { - @Override - public void onStatusChange(@NotNull GradleTaskNotificationEvent event) { - indicator.setText2(event.getDescription()); - } - }; - GradleTaskId taskId = GradleTaskId.create(GradleTaskType.RESOLVE_PROJECT); - myProgressManager.addNotificationListener(taskId, gradleProgressListener); - try { - GradleProjectResolver resolver = manager.getFacade().getResolver(); - myGradleProject = resolver.resolveProjectInfo(taskId, myProjectFile.getAbsolutePath(), false); + GradleResolveProjectTask task = new GradleResolveProjectTask(project, myProjectFile.getAbsolutePath(), false); + task.execute(indicator); + myGradleProject = task.getProject(); + if (myGradleProject != null) { + return; } - catch (ProcessCanceledException e) { - // Ignore + final Throwable error = task.getError(); + if (error == null) { + return; } - catch (Exception e) { - Throwable unwrapped = RemoteUtil.unwrap(e); - String reason = unwrapped.getLocalizedMessage(); - if (!StringUtil.isEmpty(reason)) { - errorReason.set(reason); - } - if (unwrapped.getClass() == NoClassDefFoundError.class) { - errorReason.set(GradleBundle.message("gradle.import.text.incomplete.tooling.api")); - } - else if (unwrapped.getClass() == GradleApiException.class) { - GradleLog.LOG.warn("Can't resolve gradle project. Reason: gradle api threw an exception:\n" - + ((GradleApiException)unwrapped).getOriginalReason() - ); - } - else { - GradleLog.LOG.warn("Can't resolve gradle project", e); - } + Throwable unwrapped = RemoteUtil.unwrap(error); + String reason = unwrapped.getLocalizedMessage(); + if (!StringUtil.isEmpty(reason)) { + errorReason.set(reason); } - finally { - myProgressManager.removeNotificationListener(gradleProgressListener); + if (unwrapped.getClass() == NoClassDefFoundError.class) { + errorReason.set(GradleBundle.message("gradle.import.text.incomplete.tooling.api")); + } + else if (unwrapped.getClass() == GradleApiException.class) { + GradleLog.LOG.warn("Can't resolve gradle project. Reason: gradle api threw an exception:\n" + + ((GradleApiException)unwrapped).getOriginalReason() + ); + } + else { + GradleLog.LOG.warn("Can't resolve gradle project", unwrapped); } } }); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleProgressNotificationManager.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleProgressNotificationManager.java index ad97ed30e638..7c134a94f968 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleProgressNotificationManager.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleProgressNotificationManager.java @@ -1,6 +1,7 @@ package org.jetbrains.plugins.gradle.notification; import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.gradle.task.GradleTaskId; /** * @author Denis Zhdanov diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleProgressNotificationManagerImpl.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleProgressNotificationManagerImpl.java index 27a39c3c2c9a..80787d206f06 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleProgressNotificationManagerImpl.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleProgressNotificationManagerImpl.java @@ -5,6 +5,7 @@ import com.intellij.util.containers.ConcurrentHashMap; import com.intellij.util.containers.ConcurrentHashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.gradle.remote.RemoteGradleProgressNotificationManager; +import org.jetbrains.plugins.gradle.task.GradleTaskId; import java.util.Map; import java.util.Set; diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleTaskNotificationEvent.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleTaskNotificationEvent.java index 87351365314d..3769a310e4c3 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleTaskNotificationEvent.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleTaskNotificationEvent.java @@ -1,6 +1,7 @@ package org.jetbrains.plugins.gradle.notification; import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.gradle.task.GradleTaskId; import java.io.Serializable; diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleTaskNotificationListener.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleTaskNotificationListener.java index 1f5841011142..1b4bda67e8ec 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleTaskNotificationListener.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleTaskNotificationListener.java @@ -1,6 +1,7 @@ package org.jetbrains.plugins.gradle.notification; import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.gradle.task.GradleTaskId; /** * Defines contract for callback to listen gradle task notifications. diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleTaskNotificationListenerAdapter.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleTaskNotificationListenerAdapter.java index be2942680f39..a5ff4965a90d 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleTaskNotificationListenerAdapter.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleTaskNotificationListenerAdapter.java @@ -1,6 +1,7 @@ package org.jetbrains.plugins.gradle.notification; import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.gradle.task.GradleTaskId; /** * @author Denis Zhdanov diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/GradleApiFacade.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/GradleApiFacade.java index 49ccedbfe98c..d2d5dc0e3072 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/GradleApiFacade.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/GradleApiFacade.java @@ -1,9 +1,12 @@ package org.jetbrains.plugins.gradle.remote; import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.gradle.task.GradleTaskId; +import org.jetbrains.plugins.gradle.task.GradleTaskType; import java.rmi.Remote; import java.rmi.RemoteException; +import java.util.Collection; /** * Serves as a facade for working with @@ -42,4 +45,23 @@ public interface GradleApiFacade extends Remote { * @throws RemoteException in case of unexpected I/O exception during processing */ void applyProgressManager(@NotNull RemoteGradleProgressNotificationManager progressManager) throws RemoteException; + + /** + * Asks remote gradle process to check if a task with the given id is being executed right now. + * + * @param id target task's id + * @return true if a task with the given id is executed at the moment; false otherwise + * @throws RemoteException in case of unexpected I/O exception during processing + */ + boolean isTaskInProgress(@NotNull GradleTaskId id) throws RemoteException; + + /** + * Allows to ask remote gradle process for the ids of the tasks with the given type being executed now. + * + * @param type target task type + * @return ids of the tasks of the target type being executed at the moment (if any) + * @throws RemoteException in case of unexpected I/O exception during processing + */ + @NotNull + Collection getTasksInProgress(@NotNull GradleTaskType type) throws RemoteException; } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/GradleProjectResolver.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/GradleProjectResolver.java index 6030799c9bb5..58fae0907f16 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/GradleProjectResolver.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/GradleProjectResolver.java @@ -2,7 +2,7 @@ package org.jetbrains.plugins.gradle.remote; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.gradle.model.GradleProject; -import org.jetbrains.plugins.gradle.notification.GradleTaskId; +import org.jetbrains.plugins.gradle.task.GradleTaskId; import java.rmi.RemoteException; diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/RemoteGradleProgressNotificationManager.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/RemoteGradleProgressNotificationManager.java index 34527b65f2d5..22b87a335e12 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/RemoteGradleProgressNotificationManager.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/RemoteGradleProgressNotificationManager.java @@ -1,7 +1,7 @@ package org.jetbrains.plugins.gradle.remote; import org.jetbrains.annotations.NotNull; -import org.jetbrains.plugins.gradle.notification.GradleTaskId; +import org.jetbrains.plugins.gradle.task.GradleTaskId; import org.jetbrains.plugins.gradle.notification.GradleTaskNotificationEvent; import java.rmi.Remote; diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/RemoteGradleService.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/RemoteGradleService.java index 38d775fd0ad0..52d709b0cd7f 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/RemoteGradleService.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/RemoteGradleService.java @@ -2,9 +2,12 @@ package org.jetbrains.plugins.gradle.remote; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.gradle.notification.GradleTaskNotificationListener; +import org.jetbrains.plugins.gradle.task.GradleTaskId; +import org.jetbrains.plugins.gradle.task.GradleTaskType; import java.rmi.Remote; import java.rmi.RemoteException; +import java.util.Collection; /** * Generic interface with common functionality for all remote services that work with gradle tooling api. @@ -14,6 +17,26 @@ import java.rmi.RemoteException; */ public interface RemoteGradleService extends Remote { + /** + * Allows to check if current service executes the target task. + * + * @param id target task's id + * @return true if a task with the given id is executed at the moment by the current service; + * false otherwise + * @throws RemoteException as required by RMI + */ + boolean isTaskInProgress(@NotNull GradleTaskId id) throws RemoteException; + + /** + * Allows to ask current service for the ids of the tasks with the given type being executed now. + * + * @param type target task type + * @return ids of the tasks of the target type being executed at the moment by the current service (if any) + * @throws RemoteException as required by RMI + */ + @NotNull + Collection getTasksInProgress(@NotNull GradleTaskType type) throws RemoteException; + /** * Provides the service settings to use. * diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/impl/GradleApiFacadeImpl.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/impl/GradleApiFacadeImpl.java index 065e8ed37110..82e74734ff4e 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/impl/GradleApiFacadeImpl.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/impl/GradleApiFacadeImpl.java @@ -4,19 +4,19 @@ import com.intellij.execution.rmi.RemoteServer; import com.intellij.util.Alarm; import com.intellij.util.containers.ConcurrentHashMap; import org.jetbrains.annotations.NotNull; -import org.jetbrains.plugins.gradle.notification.GradleTaskId; +import org.jetbrains.plugins.gradle.task.GradleTaskId; import org.jetbrains.plugins.gradle.notification.GradleTaskNotificationEvent; import org.jetbrains.plugins.gradle.notification.GradleTaskNotificationListener; import org.jetbrains.plugins.gradle.remote.RemoteGradleProgressNotificationManager; import org.jetbrains.plugins.gradle.remote.*; +import org.jetbrains.plugins.gradle.task.GradleTaskType; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -35,7 +35,7 @@ public class GradleApiFacadeImpl extends RemoteServer implements GradleApiFacade = new ConcurrentHashMap, RemoteGradleService>(); private final AtomicReference mySettings = new AtomicReference(); - private final AtomicReference myNotificationListener + private final AtomicReference myNotificationListener = new AtomicReference(); private final AtomicLong myTtlMs = new AtomicLong(DEFAULT_REMOTE_GRADLE_PROCESS_TTL_IN_MS); @@ -130,6 +130,33 @@ public class GradleApiFacadeImpl extends RemoteServer implements GradleApiFacade } } + @Override + public boolean isTaskInProgress(@NotNull GradleTaskId id) throws RemoteException { + for (RemoteGradleService service : myRemotes.values()) { + if (service.isTaskInProgress(id)) { + return true; + } + } + return false; + } + + @NotNull + @Override + public Collection getTasksInProgress(@NotNull GradleTaskType type) throws RemoteException { + Set result = null; + for (RemoteGradleService service : myRemotes.values()) { + final Collection tasks = service.getTasksInProgress(type); + if (tasks.isEmpty()) { + continue; + } + if (result == null) { + result = new HashSet(); + } + result.addAll(tasks); + } + return result == null ? Collections.emptySet() : result; + } + @Override public void applySettings(@NotNull RemoteGradleProcessSettings settings) throws RemoteException { mySettings.set(settings); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/impl/GradleProjectResolverImpl.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/impl/GradleProjectResolverImpl.java index eb3157300c84..a265023cc608 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/impl/GradleProjectResolverImpl.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/impl/GradleProjectResolverImpl.java @@ -5,6 +5,7 @@ import com.intellij.openapi.roots.DependencyScope; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import com.intellij.util.PathUtil; +import com.intellij.util.containers.ConcurrentHashSet; import com.intellij.util.containers.HashMap; import org.gradle.tooling.*; import org.gradle.tooling.model.DomainObjectSet; @@ -12,22 +13,21 @@ import org.gradle.tooling.model.idea.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.model.*; -import org.jetbrains.plugins.gradle.notification.GradleTaskId; +import org.jetbrains.plugins.gradle.task.GradleTaskId; import org.jetbrains.plugins.gradle.notification.GradleTaskNotificationEvent; import org.jetbrains.plugins.gradle.notification.GradleTaskNotificationListener; import org.jetbrains.plugins.gradle.remote.GradleApiException; import org.jetbrains.plugins.gradle.remote.GradleProjectResolver; import org.jetbrains.plugins.gradle.remote.RemoteGradleProcessSettings; import org.jetbrains.plugins.gradle.remote.RemoteGradleService; +import org.jetbrains.plugins.gradle.task.GradleTaskType; import org.jetbrains.plugins.gradle.util.GradleBundle; import org.jetbrains.plugins.gradle.util.GradleLog; import org.jetbrains.plugins.gradle.util.GradleUtil; import java.io.File; import java.rmi.RemoteException; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -39,12 +39,14 @@ import java.util.concurrent.atomic.AtomicReference; */ public class GradleProjectResolverImpl extends RemoteObject implements GradleProjectResolver, RemoteGradleService { - private final ThreadLocal myCurrentConnection = new ThreadLocal(); - private final BlockingQueue myConnections = new LinkedBlockingQueue(); - private final AtomicReference mySettings = new AtomicReference(); - private final GradleLibraryNamesMixer myLibraryNamesMixer = new GradleLibraryNamesMixer(); - private final AtomicReference myNotificationListener = - new AtomicReference(); + private final ThreadLocal myCurrentConnection = new ThreadLocal(); + private final BlockingQueue myConnections = new LinkedBlockingQueue(); + private final AtomicReference mySettings = new AtomicReference(); + private final GradleLibraryNamesMixer myLibraryNamesMixer = new GradleLibraryNamesMixer(); + private final ConcurrentHashSet myTasksInProgress = new ConcurrentHashSet(); + private final AtomicReference myNotificationListener + = new AtomicReference(); + @NotNull @Override @@ -71,7 +73,21 @@ public class GradleProjectResolverImpl extends RemoteObject implements GradlePro } } } - + + @Override + public boolean isTaskInProgress(@NotNull GradleTaskId id) { + return myTasksInProgress.contains(id); + } + + @NotNull + @Override + public Collection getTasksInProgress(@NotNull GradleTaskType type) { + if (type != GradleTaskType.RESOLVE_PROJECT || myTasksInProgress.isEmpty()) { + return Collections.emptySet(); + } + return new HashSet(myTasksInProgress); + } + @NotNull private GradleProject doResolveProjectInfo(@NotNull final GradleTaskId id, @NotNull String projectPath, boolean downloadLibraries) throws RemoteException, IllegalArgumentException, IllegalStateException diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureChangesModel.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureChangesModel.java index 58138ec2a9fc..9b16e094a0b4 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureChangesModel.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureChangesModel.java @@ -42,7 +42,7 @@ public class GradleProjectStructureChangesModel extends AbstractProjectComponent *
  • {@link #addListener(GradleProjectStructureChangeListener) Registered listeners} are notified if any new change is detected;
  • * * - * @param gradleProject + * @param gradleProject gradle project to sync with */ public void update(@NotNull GradleProject gradleProject) { //TODO den implement diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/task/AbstractGradleTask.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/task/AbstractGradleTask.java new file mode 100644 index 000000000000..bfbf74f5d633 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/task/AbstractGradleTask.java @@ -0,0 +1,124 @@ +package org.jetbrains.plugins.gradle.task; + +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProgressIndicator; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.gradle.notification.GradleProgressNotificationManager; +import org.jetbrains.plugins.gradle.notification.GradleTaskNotificationEvent; +import org.jetbrains.plugins.gradle.notification.GradleTaskNotificationListener; +import org.jetbrains.plugins.gradle.notification.GradleTaskNotificationListenerAdapter; +import org.jetbrains.plugins.gradle.remote.GradleApiFacade; +import org.jetbrains.plugins.gradle.remote.GradleApiFacadeManager; + +import java.util.concurrent.atomic.AtomicReference; + +/** + * Encapsulates particular task performed by the gradle integration. + *

    + * Thread-safe. + * + * @author Denis Zhdanov + * @since 1/24/12 7:03 AM + */ +public abstract class AbstractGradleTask implements GradleTask { + + private static final Logger LOG = Logger.getInstance("#" + AbstractGradleTask.class.getName()); + + private final AtomicReference myState = new AtomicReference(GradleTaskState.NOT_STARTED); + private final AtomicReference myError = new AtomicReference(); + + private final GradleTaskId myId; + + protected AbstractGradleTask(@NotNull GradleTaskType type) { + myId = GradleTaskId.create(type); + } + + @NotNull + public GradleTaskId getId() { + return myId; + } + + @NotNull + public GradleTaskState getState() { + return myState.get(); + } + + protected void setState(@NotNull GradleTaskState state) { + myState.set(state); + } + + @Override + public Throwable getError() { + return myError.get(); + } + + public void refreshState() { + if (getState() != GradleTaskState.IN_PROGRESS) { + return; + } + final GradleApiFacadeManager manager = ServiceManager.getService(GradleApiFacadeManager.class); + try { + final GradleApiFacade facade = manager.getFacade(); + setState(facade.isTaskInProgress(getId()) ? GradleTaskState.IN_PROGRESS : GradleTaskState.FAILED); + } + catch (Throwable e) { + setState(GradleTaskState.FAILED); + myError.set(e); + LOG.warn(e); + } + } + + @Override + public void execute(@NotNull final ProgressIndicator indicator) { + indicator.setIndeterminate(true); + execute(new GradleTaskNotificationListenerAdapter() { + @Override + public void onStatusChange(@NotNull GradleTaskNotificationEvent event) { + indicator.setText2(event.getDescription()); + } + }); + } + + @Override + public void execute(@NotNull GradleTaskNotificationListener... listeners) { + GradleProgressNotificationManager progressManager = ServiceManager.getService(GradleProgressNotificationManager.class); + for (GradleTaskNotificationListener listener : listeners) { + progressManager.addNotificationListener(getId(), listener); + } + try { + doExecute(); + } + catch (Throwable e) { + setState(GradleTaskState.FAILED); + myError.set(e); + LOG.warn(e); + } + finally { + for (GradleTaskNotificationListener listener : listeners) { + progressManager.addNotificationListener(getId(), listener); + } + } + } + + protected abstract void doExecute() throws Exception; + + @Override + public int hashCode() { + return myId.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AbstractGradleTask task = (AbstractGradleTask)o; + return myId.equals(task.myId); + } + + @Override + public String toString() { + return String.format("%s: %s", myId, myState); + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/task/GradleResolveProjectTask.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/task/GradleResolveProjectTask.java new file mode 100644 index 000000000000..a8d7bd868f8e --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/task/GradleResolveProjectTask.java @@ -0,0 +1,53 @@ +package org.jetbrains.plugins.gradle.task; + +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.gradle.model.GradleProject; +import org.jetbrains.plugins.gradle.remote.GradleApiFacadeManager; +import org.jetbrains.plugins.gradle.remote.GradleProjectResolver; +import org.jetbrains.plugins.gradle.sync.GradleProjectStructureChangesModel; + +import java.util.concurrent.atomic.AtomicReference; + +/** + * Thread-safe. + * + * @author Denis Zhdanov + * @since 1/24/12 7:21 AM + */ +public class GradleResolveProjectTask extends AbstractGradleTask { + + private final AtomicReference myGradleProject = new AtomicReference(); + + private final Project myIntellijProject; + private final String myProjectPath; + private final boolean myResolveLibraries; + + public GradleResolveProjectTask(@Nullable Project project, @NotNull String projectPath, boolean resolveLibraries) { + super(GradleTaskType.RESOLVE_PROJECT); + myIntellijProject = project; + myProjectPath = projectPath; + myResolveLibraries = resolveLibraries; + } + + protected void doExecute() throws Exception { + final GradleApiFacadeManager manager = ServiceManager.getService(GradleApiFacadeManager.class); + GradleProjectResolver resolver = manager.getFacade().getResolver(); + setState(GradleTaskState.IN_PROGRESS); + final GradleProject project = resolver.resolveProjectInfo(getId(), myProjectPath, myResolveLibraries); + myGradleProject.set(project); + setState(GradleTaskState.FINISHED); + if (myIntellijProject == null) { + return; + } + final GradleProjectStructureChangesModel model = myIntellijProject.getComponent(GradleProjectStructureChangesModel.class); + model.update(project); + } + + @Nullable + public GradleProject getProject() { + return myGradleProject.get(); + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/task/GradleTask.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/task/GradleTask.java new file mode 100644 index 000000000000..690a3a0e0e9f --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/task/GradleTask.java @@ -0,0 +1,44 @@ +package org.jetbrains.plugins.gradle.task; + +import com.intellij.openapi.progress.ProgressIndicator; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.gradle.notification.GradleTaskNotificationListener; + +/** + * @author Denis Zhdanov + * @since 1/24/12 7:16 AM + */ +public interface GradleTask { + + @NotNull + GradleTaskId getId(); + + @NotNull + GradleTaskState getState(); + + /** + * @return error occurred during the task execution (if any) + */ + @Nullable + Throwable getError(); + + /** + * Executes current task and updates given indicator's {@link ProgressIndicator#setText2(String) status} during that. + * + * @param indicator target progress indicator + */ + void execute(@NotNull ProgressIndicator indicator); + + /** + * Executes current task at the calling thread, i.e. the call to this method blocks. + * + * @param listeners callbacks to be notified about the task execution update + */ + void execute(@NotNull GradleTaskNotificationListener ... listeners); + + /** + * Forces current task to refresh {@link #getState() its state}. + */ + void refreshState(); +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleTaskId.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/task/GradleTaskId.java similarity index 95% rename from plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleTaskId.java rename to plugins/gradle/src/org/jetbrains/plugins/gradle/task/GradleTaskId.java index e1cfbdfd3dda..577242f3a00e 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleTaskId.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/task/GradleTaskId.java @@ -1,4 +1,4 @@ -package org.jetbrains.plugins.gradle.notification; +package org.jetbrains.plugins.gradle.task; import org.jetbrains.annotations.NotNull; @@ -7,7 +7,7 @@ import java.util.concurrent.atomic.AtomicLong; /** * Represents id of the task enqueued to Gradle API for execution. - * + * * @author Denis Zhdanov * @since 11/10/11 9:09 AM */ @@ -15,7 +15,7 @@ public class GradleTaskId implements Serializable { private static final long serialVersionUID = 1L; private static final AtomicLong COUNTER = new AtomicLong(); - + private final GradleTaskType myType; private final long myId; @@ -26,7 +26,7 @@ public class GradleTaskId implements Serializable { /** * Allows to retrieve distinct task id object of the given type. - * + * * @param type target task type * @return distinct task id object of the given type */ @@ -34,7 +34,8 @@ public class GradleTaskId implements Serializable { public static GradleTaskId create(@NotNull GradleTaskType type) { return new GradleTaskId(type, COUNTER.getAndIncrement()); } - + + @NotNull public GradleTaskType getType() { return myType; } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/task/GradleTaskState.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/task/GradleTaskState.java new file mode 100644 index 000000000000..3a51f6801d56 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/task/GradleTaskState.java @@ -0,0 +1,10 @@ +package org.jetbrains.plugins.gradle.task; + +/** + * @author Denis Zhdanov + * @since 1/24/12 7:08 AM + */ +public enum GradleTaskState { + + NOT_STARTED, IN_PROGRESS, FINISHED, FAILED +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleTaskType.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/task/GradleTaskType.java similarity index 79% rename from plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleTaskType.java rename to plugins/gradle/src/org/jetbrains/plugins/gradle/task/GradleTaskType.java index b9a8ecceb1c0..fb223ad7cd1d 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/notification/GradleTaskType.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/task/GradleTaskType.java @@ -1,4 +1,4 @@ -package org.jetbrains.plugins.gradle.notification; +package org.jetbrains.plugins.gradle.task; /** * Enumerates interested types of tasks that may be enqueued to Gradle API.