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;
This commit is contained in:
Denis.Zhdanov
2012-01-24 10:32:36 +04:00
parent a553915216
commit 302dda9ada
22 changed files with 424 additions and 122 deletions
@@ -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
@@ -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);
}
}
});
}
}
@@ -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<GradleModule, Module> importModules(@NotNull final Iterable<GradleModule> 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<GradleModule> modules) {
@@ -107,8 +103,7 @@ public class GradleModulesImporter {
public Map<GradleModule, Module> importModules(@NotNull final Iterable<GradleModule> modules,
@NotNull final ModifiableModuleModel model,
@NotNull final Project intellijProject,
@NotNull final String gradleProjectPath,
@NotNull final GradleProgressNotificationManager progressManager)
@NotNull final String gradleProjectPath)
{
final Map<GradleModule, Module> result = new HashMap<GradleModule, Module>();
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<GradleModule, Module> 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<GradleProject> gradleProjectRef = new Ref<GradleProject>();
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<GradleModule> myModules;
private final String myGradleProjectPath;
private final GradleProgressNotificationManager myProgressManger;
ImportModulesTask(@NotNull Project project, @NotNull Iterable<GradleModule> modules, @NotNull String gradleProjectPath,
@NotNull GradleProgressNotificationManager progressManager) {
ImportModulesTask(@NotNull Project project, @NotNull Iterable<GradleModule> 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<GradleModule, Module> myModules;
private final String myGradleProjectPath;
private final Project myIntellijProject;
private final GradleProgressNotificationManager myProgressManager;
SetupExternalLibrariesTask(@NotNull Map<GradleModule, Module> 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);
}
}
}
@@ -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<GradleProje
/** @see #setModuleMappings(Map) */
private final Map<GradleModule/*origin*/, GradleModule/*adjusted*/> myModuleMappings = new HashMap<GradleModule, GradleModule>();
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<GradleProje
}
GradleModulesImporter importer = new GradleModulesImporter();
Map<GradleModule, Module> mappings =
importer.importModules(myModuleMappings.values(), project, model, myProjectFile.getAbsolutePath(), myProgressManager);
importer.importModules(myModuleMappings.values(), project, model, myProjectFile.getAbsolutePath());
return new ArrayList<Module>(mappings.values());
}
@@ -165,48 +155,36 @@ public class GradleProjectImportBuilder extends ProjectImportBuilder<GradleProje
}
final Ref<String> errorReason = new Ref<String>();
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);
}
}
});
@@ -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
@@ -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;
@@ -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;
@@ -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.
@@ -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
@@ -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 <code>true</code> if a task with the given id is executed at the moment; <code>false</code> 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<GradleTaskId> getTasksInProgress(@NotNull GradleTaskType type) throws RemoteException;
}
@@ -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;
@@ -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;
@@ -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 <code>true</code> if a task with the given id is executed at the moment by the current service;
* <code>false</code> 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<GradleTaskId> getTasksInProgress(@NotNull GradleTaskType type) throws RemoteException;
/**
* Provides the service settings to use.
*
@@ -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<Class<?>, RemoteGradleService>();
private final AtomicReference<RemoteGradleProcessSettings> mySettings
= new AtomicReference<RemoteGradleProcessSettings>();
private final AtomicReference<GradleTaskNotificationListener> myNotificationListener
private final AtomicReference<GradleTaskNotificationListener> myNotificationListener
= new AtomicReference<GradleTaskNotificationListener>();
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<GradleTaskId> getTasksInProgress(@NotNull GradleTaskType type) throws RemoteException {
Set<GradleTaskId> result = null;
for (RemoteGradleService service : myRemotes.values()) {
final Collection<GradleTaskId> tasks = service.getTasksInProgress(type);
if (tasks.isEmpty()) {
continue;
}
if (result == null) {
result = new HashSet<GradleTaskId>();
}
result.addAll(tasks);
}
return result == null ? Collections.<GradleTaskId>emptySet() : result;
}
@Override
public void applySettings(@NotNull RemoteGradleProcessSettings settings) throws RemoteException {
mySettings.set(settings);
@@ -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<ProjectConnection> myCurrentConnection = new ThreadLocal<ProjectConnection>();
private final BlockingQueue<ProjectConnection> myConnections = new LinkedBlockingQueue<ProjectConnection>();
private final AtomicReference<RemoteGradleProcessSettings> mySettings = new AtomicReference<RemoteGradleProcessSettings>();
private final GradleLibraryNamesMixer myLibraryNamesMixer = new GradleLibraryNamesMixer();
private final AtomicReference<GradleTaskNotificationListener> myNotificationListener =
new AtomicReference<GradleTaskNotificationListener>();
private final ThreadLocal<ProjectConnection> myCurrentConnection = new ThreadLocal<ProjectConnection>();
private final BlockingQueue<ProjectConnection> myConnections = new LinkedBlockingQueue<ProjectConnection>();
private final AtomicReference<RemoteGradleProcessSettings> mySettings = new AtomicReference<RemoteGradleProcessSettings>();
private final GradleLibraryNamesMixer myLibraryNamesMixer = new GradleLibraryNamesMixer();
private final ConcurrentHashSet<GradleTaskId> myTasksInProgress = new ConcurrentHashSet<GradleTaskId>();
private final AtomicReference<GradleTaskNotificationListener> myNotificationListener
= new AtomicReference<GradleTaskNotificationListener>();
@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<GradleTaskId> getTasksInProgress(@NotNull GradleTaskType type) {
if (type != GradleTaskType.RESOLVE_PROJECT || myTasksInProgress.isEmpty()) {
return Collections.emptySet();
}
return new HashSet<GradleTaskId>(myTasksInProgress);
}
@NotNull
private GradleProject doResolveProjectInfo(@NotNull final GradleTaskId id, @NotNull String projectPath, boolean downloadLibraries)
throws RemoteException, IllegalArgumentException, IllegalStateException
@@ -42,7 +42,7 @@ public class GradleProjectStructureChangesModel extends AbstractProjectComponent
* <li>{@link #addListener(GradleProjectStructureChangeListener) Registered listeners} are notified if any new change is detected;</li>
* </ol>
*
* @param gradleProject
* @param gradleProject gradle project to sync with
*/
public void update(@NotNull GradleProject gradleProject) {
//TODO den implement
@@ -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.
* <p/>
* 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<GradleTaskState> myState = new AtomicReference<GradleTaskState>(GradleTaskState.NOT_STARTED);
private final AtomicReference<Throwable> myError = new AtomicReference<Throwable>();
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);
}
}
@@ -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<GradleProject> myGradleProject = new AtomicReference<GradleProject>();
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();
}
}
@@ -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();
}
@@ -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;
}
@@ -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
}
@@ -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.