blob: 88b92ae679a62a9f2bc955974a3af954e9a03dcc [file] [log] [blame]
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.maven.project;
import com.intellij.ide.startup.StartupManagerEx;
import com.intellij.openapi.application.AccessToken;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.compiler.CompileContext;
import com.intellij.openapi.compiler.CompileTask;
import com.intellij.openapi.compiler.CompilerManager;
import com.intellij.openapi.components.*;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.DumbAwareRunnable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.util.Alarm;
import com.intellij.util.EventDispatcher;
import com.intellij.util.NullableConsumer;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.update.Update;
import gnu.trove.THashMap;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import org.jetbrains.idea.maven.importing.MavenDefaultModifiableModelsProvider;
import org.jetbrains.idea.maven.importing.MavenFoldersImporter;
import org.jetbrains.idea.maven.importing.MavenModifiableModelsProvider;
import org.jetbrains.idea.maven.importing.MavenProjectImporter;
import org.jetbrains.idea.maven.model.*;
import org.jetbrains.idea.maven.server.MavenEmbedderWrapper;
import org.jetbrains.idea.maven.server.NativeMavenProjectHolder;
import org.jetbrains.idea.maven.utils.*;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
@State(name = "MavenProjectsManager", storages = {@Storage(file = StoragePathMacros.PROJECT_FILE)})
public class MavenProjectsManager extends MavenSimpleProjectComponent
implements PersistentStateComponent<MavenProjectsManagerState>, SettingsSavingComponent {
private static final int IMPORT_DELAY = 1000;
private final AtomicBoolean isInitialized = new AtomicBoolean();
private MavenProjectsManagerState myState = new MavenProjectsManagerState();
private final Alarm myInitializationAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD, myProject);
private final MavenEmbeddersManager myEmbeddersManager;
private MavenProjectsTree myProjectsTree;
private MavenProjectsManagerWatcher myWatcher;
private MavenProjectsProcessor myReadingProcessor;
private MavenProjectsProcessor myResolvingProcessor;
private MavenProjectsProcessor myPluginsResolvingProcessor;
private MavenProjectsProcessor myFoldersResolvingProcessor;
private MavenProjectsProcessor myArtifactsDownloadingProcessor;
private MavenProjectsProcessor myPostProcessor;
private MavenMergingUpdateQueue myImportingQueue;
private final Object myImportingDataLock = new Object();
private final Map<MavenProject, MavenProjectChanges> myProjectsToImport = new LinkedHashMap<MavenProject, MavenProjectChanges>();
private final Set<MavenProject> myProjectsToResolve = new LinkedHashSet<MavenProject>();
private boolean myImportModuleGroupsRequired = false;
private final EventDispatcher<MavenProjectsTree.Listener> myProjectsTreeDispatcher =
EventDispatcher.create(MavenProjectsTree.Listener.class);
private final List<Listener> myManagerListeners = ContainerUtil.createLockFreeCopyOnWriteList();
private ModificationTracker myModificationTracker;
private MavenWorkspaceSettings myWorkspaceSettings;
public static MavenProjectsManager getInstance(Project p) {
return p.getComponent(MavenProjectsManager.class);
}
public MavenProjectsManager(Project project) {
super(project);
myEmbeddersManager = new MavenEmbeddersManager(myProject);
myModificationTracker = new MavenModificationTracker(this);
}
public MavenProjectsManagerState getState() {
if (isInitialized()) {
applyTreeToState();
}
return myState;
}
public void loadState(MavenProjectsManagerState state) {
myState = state;
if (isInitialized()) {
applyStateToTree();
scheduleUpdateAllProjects(false);
}
}
public ModificationTracker getModificationTracker() {
return myModificationTracker;
}
public MavenGeneralSettings getGeneralSettings() {
return getWorkspaceSettings().generalSettings;
}
public MavenImportingSettings getImportingSettings() {
return getWorkspaceSettings().importingSettings;
}
private MavenWorkspaceSettings getWorkspaceSettings() {
if (myWorkspaceSettings == null) {
myWorkspaceSettings = MavenWorkspaceSettingsComponent.getInstance(myProject).getSettings();
}
return myWorkspaceSettings;
}
public File getLocalRepository() {
return getGeneralSettings().getEffectiveLocalRepository();
}
@Override
public void initComponent() {
if (!isNormalProject()) return;
StartupManagerEx startupManager = StartupManagerEx.getInstanceEx(myProject);
startupManager.registerStartupActivity(new Runnable() {
public void run() {
boolean wasMavenized = !myState.originalFiles.isEmpty();
if (!wasMavenized) return;
initMavenized();
}
});
startupManager.registerPostStartupActivity(new Runnable() {
@Override
public void run() {
CompilerManager.getInstance(myProject).addBeforeTask(new CompileTask() {
@Override
public boolean execute(CompileContext context) {
AccessToken token = ReadAction.start();
try {
new MavenResourceCompilerConfigurationGenerator(myProject, myProjectsTree).generateBuildConfiguration(context.isRebuild());
}
finally {
token.finish();
}
return true;
}
});
}
});
}
private void initMavenized() {
doInit(false);
}
private void initNew(List<VirtualFile> files, MavenExplicitProfiles explicitProfiles) {
myState.originalFiles = MavenUtil.collectPaths(files);
getWorkspaceSettings().setEnabledProfiles(explicitProfiles.getEnabledProfiles());
getWorkspaceSettings().setDisabledProfiles(explicitProfiles.getDisabledProfiles());
doInit(true);
}
@TestOnly
public void initForTests() {
doInit(false);
}
private void doInit(final boolean isNew) {
synchronized (isInitialized) {
if (isInitialized.getAndSet(true)) return;
initProjectsTree(!isNew);
initWorkers();
listenForSettingsChanges();
listenForProjectsTreeChanges();
MavenUtil.runWhenInitialized(myProject, new DumbAwareRunnable() {
public void run() {
if (!isUnitTestMode()) {
fireActivated();
listenForExternalChanges();
}
scheduleUpdateAllProjects(isNew);
}
});
}
}
private void initProjectsTree(boolean tryToLoadExisting) {
if (tryToLoadExisting) {
File file = getProjectsTreeFile();
try {
if (file.exists()) {
myProjectsTree = MavenProjectsTree.read(file);
}
}
catch (IOException e) {
MavenLog.LOG.info(e);
}
}
if (myProjectsTree == null) myProjectsTree = new MavenProjectsTree();
applyStateToTree();
myProjectsTree.addListener(myProjectsTreeDispatcher.getMulticaster());
}
private void applyTreeToState() {
myState.originalFiles = myProjectsTree.getManagedFilesPaths();
myState.ignoredFiles = new THashSet<String>(myProjectsTree.getIgnoredFilesPaths());
myState.ignoredPathMasks = myProjectsTree.getIgnoredFilesPatterns();
}
private void applyStateToTree() {
MavenWorkspaceSettings settings = getWorkspaceSettings();
MavenExplicitProfiles explicitProfiles = new MavenExplicitProfiles(settings.enabledProfiles, settings.disabledProfiles);
myProjectsTree.resetManagedFilesPathsAndProfiles(myState.originalFiles, explicitProfiles);
myProjectsTree.setIgnoredFilesPaths(new ArrayList<String>(myState.ignoredFiles));
myProjectsTree.setIgnoredFilesPatterns(myState.ignoredPathMasks);
}
public void save() {
if (myProjectsTree != null) {
try {
myProjectsTree.save(getProjectsTreeFile());
}
catch (IOException e) {
MavenLog.LOG.info(e);
}
}
}
private File getProjectsTreeFile() {
return new File(getProjectsTreesDir(), myProject.getLocationHash() + "/tree.dat");
}
private static File getProjectsTreesDir() {
return MavenUtil.getPluginSystemDir("Projects");
}
private void initWorkers() {
myReadingProcessor = new MavenProjectsProcessor(myProject, ProjectBundle.message("maven.reading"), false, myEmbeddersManager);
myResolvingProcessor = new MavenProjectsProcessor(myProject, ProjectBundle.message("maven.resolving"), true, myEmbeddersManager);
myPluginsResolvingProcessor =
new MavenProjectsProcessor(myProject, ProjectBundle.message("maven.downloading.plugins"), true, myEmbeddersManager);
myFoldersResolvingProcessor =
new MavenProjectsProcessor(myProject, ProjectBundle.message("maven.updating.folders"), true, myEmbeddersManager);
myArtifactsDownloadingProcessor =
new MavenProjectsProcessor(myProject, ProjectBundle.message("maven.downloading"), true, myEmbeddersManager);
myPostProcessor = new MavenProjectsProcessor(myProject, ProjectBundle.message("maven.post.processing"), true, myEmbeddersManager);
myWatcher =
new MavenProjectsManagerWatcher(myProject, this, myProjectsTree, getGeneralSettings(), myReadingProcessor, myEmbeddersManager);
myImportingQueue = new MavenMergingUpdateQueue(getComponentName() + ": Importing queue", IMPORT_DELAY, !isUnitTestMode(), myProject);
myImportingQueue.setPassThrough(false);
myImportingQueue.makeUserAware(myProject);
myImportingQueue.makeDumbAware(myProject);
myImportingQueue.makeModalAware(myProject);
}
private void listenForSettingsChanges() {
getImportingSettings().addListener(new MavenImportingSettings.Listener() {
public void autoImportChanged() {
if (myProject.isDisposed()) return;
if (getImportingSettings().isImportAutomatically()) {
scheduleImportAndResolve();
}
}
public void createModuleGroupsChanged() {
scheduleImportSettings(true);
}
public void createModuleForAggregatorsChanged() {
scheduleImportSettings();
}
});
}
private void listenForProjectsTreeChanges() {
myProjectsTree.addListener(new MavenProjectsTree.ListenerAdapter() {
@Override
public void projectsIgnoredStateChanged(List<MavenProject> ignored, List<MavenProject> unignored, boolean fromImport) {
if (!fromImport) scheduleImport();
}
@Override
public void projectsUpdated(List<Pair<MavenProject, MavenProjectChanges>> updated, List<MavenProject> deleted) {
myEmbeddersManager.clearCaches();
unscheduleAllTasks(deleted);
List<MavenProject> updatedProjects = MavenUtil.collectFirsts(updated);
// import only updated projects and dependents of them (we need to update faced-deps, packaging etc);
List<Pair<MavenProject, MavenProjectChanges>> toImport = new ArrayList<Pair<MavenProject, MavenProjectChanges>>(updated);
for (MavenProject eachDependent : myProjectsTree.getDependentProjects(updatedProjects)) {
toImport.add(Pair.create(eachDependent, MavenProjectChanges.DEPENDENCIES));
}
// resolve updated, theirs dependents, and dependents of deleted
Set<MavenProject> toResolve = new THashSet<MavenProject>(updatedProjects);
toResolve.addAll(myProjectsTree.getDependentProjects(ContainerUtil.concat(updatedProjects, deleted)));
// do not try to resolve projects with syntactic errors
Iterator<MavenProject> it = toResolve.iterator();
while (it.hasNext()) {
MavenProject each = it.next();
if (each.hasReadingProblems()) it.remove();
}
if (haveChanges(toImport) || !deleted.isEmpty()) {
scheduleForNextImport(toImport);
}
scheduleForNextResolve(toResolve);
fireProjectScheduled();
}
private boolean haveChanges(List<Pair<MavenProject, MavenProjectChanges>> projectsWithChanges) {
for (MavenProjectChanges each : MavenUtil.collectSeconds(projectsWithChanges)) {
if (each.hasChanges()) return true;
}
return false;
}
@Override
public void projectResolved(Pair<MavenProject, MavenProjectChanges> projectWithChanges,
@Nullable NativeMavenProjectHolder nativeMavenProject) {
if (nativeMavenProject != null) {
if (shouldScheduleProject(projectWithChanges)) {
scheduleForNextImport(projectWithChanges);
MavenImportingSettings importingSettings;
AccessToken token = ReadAction.start();
try {
if (myProject.isDisposed()) return;
importingSettings = getImportingSettings();
}
finally {
token.finish();
}
scheduleArtifactsDownloading(Collections.singleton(projectWithChanges.first),
null,
importingSettings.isDownloadSourcesAutomatically(),
importingSettings.isDownloadDocsAutomatically(),
null);
}
if (!projectWithChanges.first.hasReadingProblems() && projectWithChanges.first.hasUnresolvedPlugins()) {
schedulePluginsResolve(projectWithChanges.first, nativeMavenProject);
}
}
}
@Override
public void foldersResolved(Pair<MavenProject, MavenProjectChanges> projectWithChanges) {
if (shouldScheduleProject(projectWithChanges)) {
scheduleForNextImport(projectWithChanges);
}
}
private boolean shouldScheduleProject(Pair<MavenProject, MavenProjectChanges> projectWithChanges) {
return !projectWithChanges.first.hasReadingProblems() && projectWithChanges.second.hasChanges();
}
});
}
public void listenForExternalChanges() {
myWatcher.start();
}
@Override
public void projectClosed() {
synchronized (isInitialized) {
if (!isInitialized.getAndSet(false)) return;
Disposer.dispose(myImportingQueue);
myWatcher.stop();
myReadingProcessor.stop();
myResolvingProcessor.stop();
myPluginsResolvingProcessor.stop();
myFoldersResolvingProcessor.stop();
myArtifactsDownloadingProcessor.stop();
myPostProcessor.stop();
if (isUnitTestMode()) {
FileUtil.delete(getProjectsTreesDir());
}
}
}
public MavenEmbeddersManager getEmbeddersManager() {
return myEmbeddersManager;
}
private boolean isInitialized() {
return isInitialized.get();
}
public boolean isMavenizedProject() {
return isInitialized();
}
public boolean isMavenizedModule(final Module m) {
AccessToken accessToken = ApplicationManager.getApplication().acquireReadActionLock();
try {
return !m.isDisposed() && "true".equals(m.getOptionValue(getMavenizedModuleOptionName()));
}
finally {
accessToken.finish();
}
}
public void setMavenizedModules(Collection<Module> modules, boolean mavenized) {
ApplicationManager.getApplication().assertWriteAccessAllowed();
for (Module m : modules) {
if (m.isDisposed()) continue;
if (mavenized) {
m.setOption(getMavenizedModuleOptionName(), "true");
// clear external system API options
// see com.intellij.openapi.externalSystem.service.project.manage.ModuleDataService#setModuleOptions
m.clearOption("external.system.id");
m.clearOption("external.linked.project.path");
m.clearOption("external.root.project.path");
m.clearOption("external.system.module.group");
m.clearOption("external.system.module.version");
}
else {
m.clearOption(getMavenizedModuleOptionName());
}
}
}
private static String getMavenizedModuleOptionName() {
return "org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule";
}
@TestOnly
public void resetManagedFilesAndProfilesInTests(List<VirtualFile> files, MavenExplicitProfiles profiles) {
myWatcher.resetManagedFilesAndProfilesInTests(files, profiles);
}
public void addManagedFilesWithProfiles(List<VirtualFile> files, MavenExplicitProfiles profiles) {
if (!isInitialized()) {
initNew(files, profiles);
}
else {
myWatcher.addManagedFilesWithProfiles(files, profiles);
}
}
public void addManagedFiles(@NotNull List<VirtualFile> files) {
addManagedFilesWithProfiles(files, MavenExplicitProfiles.NONE);
}
public void addManagedFilesOrUnignore(@NotNull List<VirtualFile> files) {
removeIgnoredFilesPaths(MavenUtil.collectPaths(files));
addManagedFiles(files);
}
public void removeManagedFiles(@NotNull List<VirtualFile> files) {
myWatcher.removeManagedFiles(files);
}
public boolean isManagedFile(@NotNull VirtualFile f) {
if (!isInitialized()) return false;
return myProjectsTree.isManagedFile(f);
}
@NotNull
public MavenExplicitProfiles getExplicitProfiles() {
if (!isInitialized()) return MavenExplicitProfiles.NONE;
return myProjectsTree.getExplicitProfiles();
}
public void setExplicitProfiles(@NotNull MavenExplicitProfiles profiles) {
myWatcher.setExplicitProfiles(profiles);
}
@NotNull
public Collection<String> getAvailableProfiles() {
if (!isInitialized()) return Collections.emptyList();
return myProjectsTree.getAvailableProfiles();
}
@NotNull
public Collection<Pair<String, MavenProfileKind>> getProfilesWithStates() {
if (!isInitialized()) return Collections.emptyList();
return myProjectsTree.getProfilesWithStates();
}
public boolean hasProjects() {
if (!isInitialized()) return false;
return myProjectsTree.hasProjects();
}
@NotNull
public List<MavenProject> getProjects() {
if (!isInitialized()) return Collections.emptyList();
return myProjectsTree.getProjects();
}
@NotNull
public List<MavenProject> getRootProjects() {
if (!isInitialized()) return Collections.emptyList();
return myProjectsTree.getRootProjects();
}
@NotNull
public List<MavenProject> getNonIgnoredProjects() {
if (!isInitialized()) return Collections.emptyList();
return myProjectsTree.getNonIgnoredProjects();
}
@NotNull
public List<VirtualFile> getProjectsFiles() {
if (!isInitialized()) return Collections.emptyList();
return myProjectsTree.getProjectsFiles();
}
@Nullable
public MavenProject findProject(@NotNull VirtualFile f) {
if (!isInitialized()) return null;
return myProjectsTree.findProject(f);
}
@Nullable
public MavenProject findProject(@NotNull MavenId id) {
if (!isInitialized()) return null;
return myProjectsTree.findProject(id);
}
@Nullable
public MavenProject findProject(@NotNull MavenArtifact artifact) {
if (!isInitialized()) return null;
return myProjectsTree.findProject(artifact);
}
@Nullable
public MavenProject findProject(@NotNull Module module) {
VirtualFile f = findPomFile(module, new MavenModelsProvider() {
public Module[] getModules() {
throw new UnsupportedOperationException();
}
public VirtualFile[] getContentRoots(Module module) {
return ModuleRootManager.getInstance(module).getContentRoots();
}
});
return f == null ? null : findProject(f);
}
@Nullable
public Module findModule(@NotNull MavenProject project) {
if (!isInitialized()) return null;
return ProjectRootManager.getInstance(myProject).getFileIndex().getModuleForFile(project.getFile());
}
@NotNull
public Collection<MavenProject> findInheritors(@Nullable MavenProject parent) {
if (parent == null || !isInitialized()) return Collections.emptyList();
return myProjectsTree.findInheritors(parent);
}
@Nullable
public MavenProject findContainingProject(@NotNull VirtualFile file) {
if (!isInitialized()) return null;
Module module = ProjectRootManager.getInstance(myProject).getFileIndex().getModuleForFile(file);
return module == null ? null : findProject(module);
}
@Nullable
private static VirtualFile findPomFile(@NotNull Module module, @NotNull MavenModelsProvider modelsProvider) {
for (VirtualFile root : modelsProvider.getContentRoots(module)) {
final VirtualFile virtualFile = root.findChild(MavenConstants.POM_XML);
if (virtualFile != null) {
return virtualFile;
}
}
return null;
}
@Nullable
public MavenProject findAggregator(@NotNull MavenProject module) {
if (!isInitialized()) return null;
return myProjectsTree.findAggregator(module);
}
@NotNull
public List<MavenProject> getModules(@NotNull MavenProject aggregator) {
if (!isInitialized()) return Collections.emptyList();
return myProjectsTree.getModules(aggregator);
}
@NotNull
public List<String> getIgnoredFilesPaths() {
if (!isInitialized()) return Collections.emptyList();
return myProjectsTree.getIgnoredFilesPaths();
}
public void setIgnoredFilesPaths(@NotNull List<String> paths) {
if (!isInitialized()) return;
myProjectsTree.setIgnoredFilesPaths(paths);
}
public void removeIgnoredFilesPaths(final Collection<String> paths) {
if (!isInitialized()) return;
myProjectsTree.removeIgnoredFilesPaths(paths);
}
public boolean getIgnoredState(@NotNull MavenProject project) {
if (!isInitialized()) return false;
return myProjectsTree.getIgnoredState(project);
}
public void setIgnoredState(@NotNull List<MavenProject> projects, boolean ignored) {
if (!isInitialized()) return;
myProjectsTree.setIgnoredState(projects, ignored);
}
@NotNull
public List<String> getIgnoredFilesPatterns() {
if (!isInitialized()) return Collections.emptyList();
return myProjectsTree.getIgnoredFilesPatterns();
}
public void setIgnoredFilesPatterns(@NotNull List<String> patterns) {
if (!isInitialized()) return;
myProjectsTree.setIgnoredFilesPatterns(patterns);
}
public boolean isIgnored(@NotNull MavenProject project) {
if (!isInitialized()) return false;
return myProjectsTree.isIgnored(project);
}
public Set<MavenRemoteRepository> getRemoteRepositories() {
Set<MavenRemoteRepository> result = new THashSet<MavenRemoteRepository>();
for (MavenProject each : getProjects()) {
for (MavenRemoteRepository eachRepository : each.getRemoteRepositories()) {
result.add(eachRepository);
}
}
return result;
}
@TestOnly
public MavenProjectsTree getProjectsTreeForTests() {
return myProjectsTree;
}
private void scheduleUpdateAllProjects(boolean forceImportAndResolve) {
doScheduleUpdateProjects(null, false, forceImportAndResolve);
}
public void forceUpdateProjects(@NotNull Collection<MavenProject> projects) {
doScheduleUpdateProjects(projects, true, true);
}
public void forceUpdateAllProjectsOrFindAllAvailablePomFiles() {
if (!isMavenizedProject()) {
addManagedFiles(collectAllAvailablePomFiles());
}
doScheduleUpdateProjects(null, true, true);
}
private void doScheduleUpdateProjects(final Collection<MavenProject> projects,
final boolean forceUpdate,
final boolean forceImportAndResolve) {
MavenUtil.runWhenInitialized(myProject, new DumbAwareRunnable() {
public void run() {
if (projects == null) {
myWatcher.scheduleUpdateAll(forceUpdate, forceImportAndResolve);
}
else {
myWatcher.scheduleUpdate(MavenUtil.collectFiles(projects),
Collections.<VirtualFile>emptyList(),
forceUpdate,
forceImportAndResolve);
}
}
});
}
public void scheduleImportAndResolve() {
scheduleImport();
scheduleResolve();
fireImportAndResolveScheduled();
}
private void scheduleResolve() {
runWhenFullyOpen(new Runnable() {
public void run() {
LinkedHashSet<MavenProject> toResolve;
synchronized (myImportingDataLock) {
toResolve = new LinkedHashSet<MavenProject>(myProjectsToResolve);
myProjectsToResolve.clear();
}
final ResolveContext context = new ResolveContext();
Iterator<MavenProject> it = toResolve.iterator();
while (it.hasNext()) {
MavenProject each = it.next();
Runnable onCompletion = it.hasNext() ? null : new Runnable() {
@Override
public void run() {
if (hasScheduledProjects()) scheduleImport();
}
};
myResolvingProcessor.scheduleTask(
new MavenProjectsProcessorResolvingTask(each, myProjectsTree, getGeneralSettings(), onCompletion, context));
}
}
});
}
public void evaluateEffectivePom(@NotNull final MavenProject mavenProject, @NotNull final NullableConsumer<String> consumer) {
runWhenFullyOpen(new Runnable() {
@Override
public void run() {
myResolvingProcessor.scheduleTask(new MavenProjectsProcessorTask() {
@Override
public void perform(Project project,
MavenEmbeddersManager embeddersManager,
MavenConsole console,
MavenProgressIndicator indicator)
throws MavenProcessCanceledException {
indicator.setText("Evaluating effective POM");
myProjectsTree.executeWithEmbedder(mavenProject,
getEmbeddersManager(),
MavenEmbeddersManager.FOR_DEPENDENCIES_RESOLVE,
console,
indicator,
new MavenProjectsTree.EmbedderTask() {
@Override
public void run(MavenEmbedderWrapper embedder) throws MavenProcessCanceledException {
try {
MavenExplicitProfiles profiles = mavenProject.getActivatedProfilesIds();
String res =
embedder.evaluateEffectivePom(mavenProject.getFile(), profiles.getEnabledProfiles(),
profiles.getDisabledProfiles());
consumer.consume(res);
}
catch (UnsupportedOperationException e) {
consumer.consume(null); // null means UnsupportedOperationException
}
}
});
}
});
}
});
}
@TestOnly
public void scheduleResolveInTests(Collection<MavenProject> projects) {
scheduleForNextResolve(projects);
scheduleResolve();
}
@TestOnly
public void scheduleResolveAllInTests() {
scheduleResolveInTests(getProjects());
}
public void scheduleFoldersResolve(final Collection<MavenProject> projects) {
runWhenFullyOpen(new Runnable() {
public void run() {
Iterator<MavenProject> it = projects.iterator();
while (it.hasNext()) {
MavenProject each = it.next();
Runnable onCompletion = it.hasNext() ? null : new Runnable() {
@Override
public void run() {
if (hasScheduledProjects()) scheduleImport();
}
};
myFoldersResolvingProcessor.scheduleTask(
new MavenProjectsProcessorFoldersResolvingTask(each, getImportingSettings(), myProjectsTree, onCompletion));
}
}
});
}
public void scheduleFoldersResolveForAllProjects() {
scheduleFoldersResolve(getProjects());
}
private void schedulePluginsResolve(final MavenProject project, final NativeMavenProjectHolder nativeMavenProject) {
runWhenFullyOpen(new Runnable() {
public void run() {
myPluginsResolvingProcessor
.scheduleTask(new MavenProjectsProcessorPluginsResolvingTask(project, nativeMavenProject, myProjectsTree));
}
});
}
public void scheduleArtifactsDownloading(final Collection<MavenProject> projects,
@Nullable final Collection<MavenArtifact> artifacts,
final boolean sources, final boolean docs,
@Nullable final AsyncResult<MavenArtifactDownloader.DownloadResult> result) {
if (!sources && !docs) return;
runWhenFullyOpen(new Runnable() {
public void run() {
myArtifactsDownloadingProcessor
.scheduleTask(new MavenProjectsProcessorArtifactsDownloadingTask(projects, artifacts, myProjectsTree, sources, docs, result));
}
});
}
private void scheduleImportSettings() {
scheduleImportSettings(false);
}
private void scheduleImportSettings(boolean importModuleGroupsRequired) {
synchronized (myImportingDataLock) {
myImportModuleGroupsRequired = importModuleGroupsRequired;
}
scheduleImport();
}
private void scheduleImport() {
runWhenFullyOpen(new Runnable() {
public void run() {
myImportingQueue.queue(new Update(MavenProjectsManager.this) {
public void run() {
importProjects();
}
});
}
});
}
@TestOnly
public void scheduleImportInTests(List<VirtualFile> projectFiles) {
List<Pair<MavenProject, MavenProjectChanges>> toImport = new ArrayList<Pair<MavenProject, MavenProjectChanges>>();
for (VirtualFile each : projectFiles) {
MavenProject project = findProject(each);
if (project != null) {
toImport.add(Pair.create(project, MavenProjectChanges.ALL));
}
}
scheduleForNextImport(toImport);
scheduleImport();
}
private void scheduleForNextImport(Pair<MavenProject, MavenProjectChanges> projectWithChanges) {
scheduleForNextImport(Collections.singletonList(projectWithChanges));
}
private void scheduleForNextImport(Collection<Pair<MavenProject, MavenProjectChanges>> projectsWithChanges) {
synchronized (myImportingDataLock) {
for (Pair<MavenProject, MavenProjectChanges> each : projectsWithChanges) {
MavenProjectChanges changes = each.second.mergedWith(myProjectsToImport.get(each.first));
myProjectsToImport.put(each.first, changes);
}
}
}
private void scheduleForNextResolve(Collection<MavenProject> projects) {
synchronized (myImportingDataLock) {
myProjectsToResolve.addAll(projects);
}
}
public boolean hasScheduledProjects() {
if (!isInitialized()) return false;
synchronized (myImportingDataLock) {
return !myProjectsToImport.isEmpty() || !myProjectsToResolve.isEmpty();
}
}
@TestOnly
public boolean hasScheduledImportsInTests() {
if (!isInitialized()) return false;
return !myImportingQueue.isEmpty();
}
@TestOnly
public void performScheduledImportInTests() {
if (!isInitialized()) return;
runWhenFullyOpen(new Runnable() {
public void run() {
myImportingQueue.flush(false);
}
});
}
private void runWhenFullyOpen(final Runnable runnable) {
if (!isInitialized()) return; // may be called from scheduleImport after project started closing and before it is closed.
if (isNoBackgroundMode()) {
runnable.run();
return;
}
final Ref<Runnable> wrapper = new Ref<Runnable>();
wrapper.set(new Runnable() {
public void run() {
if (!StartupManagerEx.getInstanceEx(myProject).postStartupActivityPassed()) {
myInitializationAlarm.addRequest(new Runnable() { // should not remove previously schedules tasks
public void run() {
wrapper.get().run();
}
}, 1000);
return;
}
runnable.run();
}
});
MavenUtil.runWhenInitialized(myProject, wrapper.get());
}
private void schedulePostImportTasks(List<MavenProjectsProcessorTask> postTasks) {
for (MavenProjectsProcessorTask each : postTasks) {
myPostProcessor.scheduleTask(each);
}
}
private void unscheduleAllTasks(List<MavenProject> projects) {
for (MavenProject each : projects) {
MavenProjectsProcessorEmptyTask dummyTask = new MavenProjectsProcessorEmptyTask(each);
synchronized (myImportingDataLock) {
myProjectsToImport.remove(each);
myProjectsToResolve.remove(each);
}
myResolvingProcessor.removeTask(dummyTask);
myPluginsResolvingProcessor.removeTask(dummyTask);
myFoldersResolvingProcessor.removeTask(dummyTask);
myPostProcessor.removeTask(dummyTask);
}
}
@TestOnly
public void unscheduleAllTasksInTests() {
unscheduleAllTasks(getProjects());
}
public void waitForReadingCompletion() {
waitForTasksCompletion(null);
}
public void waitForResolvingCompletion() {
waitForTasksCompletion(myResolvingProcessor);
}
public void waitForFoldersResolvingCompletion() {
waitForTasksCompletion(myFoldersResolvingProcessor);
}
public void waitForPluginsResolvingCompletion() {
waitForTasksCompletion(myPluginsResolvingProcessor);
}
public void waitForArtifactsDownloadingCompletion() {
waitForTasksCompletion(myArtifactsDownloadingProcessor);
}
public void waitForPostImportTasksCompletion() {
myPostProcessor.waitForCompletion();
}
private void waitForTasksCompletion(MavenProjectsProcessor processor) {
FileDocumentManager.getInstance().saveAllDocuments();
myReadingProcessor.waitForCompletion();
if (processor != null) processor.waitForCompletion();
}
public void updateProjectTargetFolders() {
updateProjectFolders(true);
}
private void updateProjectFolders(final boolean targetFoldersOnly) {
UIUtil.invokeLaterIfNeeded(new Runnable() {
public void run() {
if (myProject.isDisposed()) return;
MavenFoldersImporter.updateProjectFolders(myProject, targetFoldersOnly);
VirtualFileManager.getInstance().asyncRefresh(null);
}
});
}
public List<Module> importProjects() {
return importProjects(new MavenDefaultModifiableModelsProvider(myProject));
}
public List<Module> importProjects(final MavenModifiableModelsProvider modelsProvider) {
final Map<MavenProject, MavenProjectChanges> projectsToImportWithChanges;
final boolean importModuleGroupsRequired;
synchronized (myImportingDataLock) {
projectsToImportWithChanges = new LinkedHashMap<MavenProject, MavenProjectChanges>(myProjectsToImport);
myProjectsToImport.clear();
importModuleGroupsRequired = myImportModuleGroupsRequired;
myImportModuleGroupsRequired = false;
}
final Ref<MavenProjectImporter> importer = new Ref<MavenProjectImporter>();
final Ref<List<MavenProjectsProcessorTask>> postTasks = new Ref<List<MavenProjectsProcessorTask>>();
final Runnable r = new Runnable() {
public void run() {
MavenProjectImporter projectImporter = new MavenProjectImporter(myProject,
myProjectsTree,
getFileToModuleMapping(modelsProvider),
projectsToImportWithChanges,
importModuleGroupsRequired,
modelsProvider,
getImportingSettings());
importer.set(projectImporter);
postTasks.set(projectImporter.importProject());
}
};
// called from wizard or ui
if (ApplicationManager.getApplication().isDispatchThread()) {
r.run();
}
else {
MavenUtil.runInBackground(myProject, ProjectBundle.message("maven.project.importing"), false, new MavenTask() {
public void run(MavenProgressIndicator indicator) throws MavenProcessCanceledException {
r.run();
}
}).waitFor();
}
VirtualFileManager fm = VirtualFileManager.getInstance();
if (isNormalProject()) {
fm.asyncRefresh(null);
}
else {
fm.syncRefresh();
}
if (postTasks.get() != null /*may be null if importing is cancelled*/) {
schedulePostImportTasks(postTasks.get());
}
// do not block user too often
myImportingQueue.restartTimer();
MavenProjectImporter projectImporter = importer.get();
if (projectImporter == null) return Collections.emptyList();
return projectImporter.getCreatedModules();
}
private Map<VirtualFile, Module> getFileToModuleMapping(MavenModelsProvider modelsProvider) {
Map<VirtualFile, Module> result = new THashMap<VirtualFile, Module>();
for (Module each : modelsProvider.getModules()) {
VirtualFile f = findPomFile(each, modelsProvider);
if (f != null) result.put(f, each);
}
return result;
}
private List<VirtualFile> collectAllAvailablePomFiles() {
List<VirtualFile> result = new ArrayList<VirtualFile>(getFileToModuleMapping(new MavenDefaultModelsProvider(myProject)).keySet());
VirtualFile pom = myProject.getBaseDir().findChild(MavenConstants.POM_XML);
if (pom != null) result.add(pom);
return result;
}
public void addManagerListener(Listener listener) {
myManagerListeners.add(listener);
}
public void addProjectsTreeListener(MavenProjectsTree.Listener listener) {
myProjectsTreeDispatcher.addListener(listener);
}
@TestOnly
public void fireActivatedInTests() {
fireActivated();
}
private void fireActivated() {
for (Listener each : myManagerListeners) {
each.activated();
}
}
private void fireProjectScheduled() {
for (Listener each : myManagerListeners) {
each.projectsScheduled();
}
}
private void fireImportAndResolveScheduled() {
for (Listener each : myManagerListeners) {
each.importAndResolveScheduled();
}
}
public interface Listener {
void activated();
void projectsScheduled();
void importAndResolveScheduled();
}
}