From a0963c3bf7017651fc582829f149e36a992671f5 Mon Sep 17 00:00:00 2001 From: Alexander Bubenchikov Date: Fri, 26 Apr 2019 15:08:55 +0300 Subject: [PATCH] IDEA-207382 - maven sync tool window GitOrigin-RevId: f23f23648879f04bc45cf4ef9770662e1831db74 --- plugins/maven/intellij.maven.iml | 3 + .../server/MavenServerProgressIndicator.java | 4 + .../MavenServerProgressIndicatorWrapper.java | 10 + .../maven/server/TransferListenerAdapter.java | 18 ++ .../Maven30TransferListenerAdapter.java | 20 ++ .../maven/server/TransferListenerAdapter.java | 18 ++ .../idea/maven/buildtool/MavenSyncConsole.kt | 206 ++++++++++++++++++ .../execution/MavenExternalParameters.java | 4 + .../maven/indices/MavenIndicesManager.java | 6 +- .../idea/maven/project/MavenProject.java | 1 + .../maven/project/MavenProjectsManager.java | 16 +- .../idea/maven/project/MavenProjectsTree.java | 21 +- .../idea/maven/server/MavenServerManager.java | 10 + .../maven/utils/MavenProgressIndicator.java | 42 +++- .../jetbrains/idea/maven/utils/MavenUtil.java | 42 +++- .../idea/maven/MavenImportingTestCase.java | 6 + .../jetbrains/idea/maven/MavenTestCase.java | 2 +- .../idea/maven/importing/MavenSyncTest.java | 49 +++++ 18 files changed, 459 insertions(+), 19 deletions(-) create mode 100644 plugins/maven/src/main/java/org/jetbrains/idea/maven/buildtool/MavenSyncConsole.kt create mode 100644 plugins/maven/src/test/java/org/jetbrains/idea/maven/importing/MavenSyncTest.java diff --git a/plugins/maven/intellij.maven.iml b/plugins/maven/intellij.maven.iml index d06b2bf492e0..ed23a2c5c07c 100644 --- a/plugins/maven/intellij.maven.iml +++ b/plugins/maven/intellij.maven.iml @@ -68,6 +68,9 @@ + + + diff --git a/plugins/maven/maven-server-api/src/org/jetbrains/idea/maven/server/MavenServerProgressIndicator.java b/plugins/maven/maven-server-api/src/org/jetbrains/idea/maven/server/MavenServerProgressIndicator.java index a455759a3050..6fe31f32d1cb 100644 --- a/plugins/maven/maven-server-api/src/org/jetbrains/idea/maven/server/MavenServerProgressIndicator.java +++ b/plugins/maven/maven-server-api/src/org/jetbrains/idea/maven/server/MavenServerProgressIndicator.java @@ -19,8 +19,12 @@ import java.rmi.Remote; import java.rmi.RemoteException; public interface MavenServerProgressIndicator extends Remote { + String DEPENDENCIES_RESOLVE_PREFIX = "Resolving Maven dependencies--"; + String PLUGINS_RESOLVE_PREFIX = "Downloading Maven plugins--"; void setText(String text) throws RemoteException; void setText2(String text) throws RemoteException; + void startTask(String text) throws RemoteException; + void completeTask(String text, String errorMessage) throws RemoteException; boolean isCanceled() throws RemoteException; diff --git a/plugins/maven/maven-server-api/src/org/jetbrains/idea/maven/server/MavenServerProgressIndicatorWrapper.java b/plugins/maven/maven-server-api/src/org/jetbrains/idea/maven/server/MavenServerProgressIndicatorWrapper.java index ba026247a29a..c385bf6b65c2 100644 --- a/plugins/maven/maven-server-api/src/org/jetbrains/idea/maven/server/MavenServerProgressIndicatorWrapper.java +++ b/plugins/maven/maven-server-api/src/org/jetbrains/idea/maven/server/MavenServerProgressIndicatorWrapper.java @@ -78,4 +78,14 @@ public class MavenServerProgressIndicatorWrapper implements MavenServerProgressI public void setFraction(double fraction) throws RemoteException { myDelegate.setFraction(fraction); } + + @Override + public void startTask(String text) throws RemoteException { + myDelegate.startTask(text); + } + + @Override + public void completeTask(String text, String errorMessage) throws RemoteException { + myDelegate.completeTask(text, errorMessage); + } } diff --git a/plugins/maven/maven3-server-impl/src/org/jetbrains/idea/maven/server/TransferListenerAdapter.java b/plugins/maven/maven3-server-impl/src/org/jetbrains/idea/maven/server/TransferListenerAdapter.java index afc78cebf0af..b01be54a3410 100644 --- a/plugins/maven/maven3-server-impl/src/org/jetbrains/idea/maven/server/TransferListenerAdapter.java +++ b/plugins/maven/maven3-server-impl/src/org/jetbrains/idea/maven/server/TransferListenerAdapter.java @@ -24,6 +24,8 @@ import org.eclipse.aether.transfer.TransferResource; import java.io.File; import java.rmi.RemoteException; +import static org.jetbrains.idea.maven.server.MavenServerProgressIndicator.DEPENDENCIES_RESOLVE_PREFIX; + public class TransferListenerAdapter implements TransferListener { protected final MavenServerProgressIndicator myIndicator; @@ -51,6 +53,13 @@ public class TransferListenerAdapter implements TransferListener { public void transferInitiated(TransferEvent event) { checkCanceled(); + try { + myIndicator.startTask(DEPENDENCIES_RESOLVE_PREFIX + formatResourceName(event)); + } + catch (RemoteException e) { + throw new RuntimeRemoteException(e); + } + try { myIndicator.setIndeterminate(true); myIndicator.setText2(formatResourceName(event)); @@ -100,6 +109,7 @@ public class TransferListenerAdapter implements TransferListener { try { myIndicator.setText2("Checksum failed: " + formatResourceName(event)); myIndicator.setIndeterminate(true); + myIndicator.completeTask(DEPENDENCIES_RESOLVE_PREFIX + formatResourceName(event), "Checksum failed"); } catch (RemoteException e) { throw new RuntimeRemoteException(e); @@ -111,6 +121,7 @@ public class TransferListenerAdapter implements TransferListener { try { myIndicator.setText2("Finished (" + StringUtilRt.formatFileSize(event.getTransferredBytes()) + ") " + formatResourceName(event)); myIndicator.setIndeterminate(true); + myIndicator.completeTask(DEPENDENCIES_RESOLVE_PREFIX + formatResourceName(event), null); Maven3ServerGlobals.getDownloadListener().artifactDownloaded(event.getResource().getFile(), event.getResource().getResourceName()); } @@ -124,6 +135,7 @@ public class TransferListenerAdapter implements TransferListener { try { if (myIndicator.isCanceled()) { myIndicator.setText2("Canceling..."); + myIndicator.completeTask(DEPENDENCIES_RESOLVE_PREFIX + formatResourceName(event), "Cancelled"); return; // Don't throw exception here. } } @@ -134,6 +146,12 @@ public class TransferListenerAdapter implements TransferListener { try { myIndicator.setText2("Failed to download " + formatResourceName(event)); myIndicator.setIndeterminate(true); + String message = "Failed to download"; + if(event.getException()!=null) { + message += ": " + event.getException().getMessage(); + } + myIndicator.completeTask(DEPENDENCIES_RESOLVE_PREFIX + formatResourceName(event), message); + } catch (RemoteException e) { throw new RuntimeRemoteException(e); diff --git a/plugins/maven/maven30-server-impl/src/org/jetbrains/idea/maven/server/Maven30TransferListenerAdapter.java b/plugins/maven/maven30-server-impl/src/org/jetbrains/idea/maven/server/Maven30TransferListenerAdapter.java index d1652c6d8d4c..b1bf2e9f7844 100644 --- a/plugins/maven/maven30-server-impl/src/org/jetbrains/idea/maven/server/Maven30TransferListenerAdapter.java +++ b/plugins/maven/maven30-server-impl/src/org/jetbrains/idea/maven/server/Maven30TransferListenerAdapter.java @@ -24,6 +24,8 @@ import org.sonatype.aether.transfer.TransferResource; import java.io.File; import java.rmi.RemoteException; +import static org.jetbrains.idea.maven.server.MavenServerProgressIndicator.DEPENDENCIES_RESOLVE_PREFIX; + /** * @author Sergey Evdokimov */ @@ -54,6 +56,13 @@ public class Maven30TransferListenerAdapter implements TransferListener { public void transferInitiated(TransferEvent event) { checkCanceled(); + try { + myIndicator.startTask(DEPENDENCIES_RESOLVE_PREFIX + formatResourceName(event)); + } + catch (RemoteException e) { + throw new RuntimeRemoteException(e); + } + try { myIndicator.setIndeterminate(true); myIndicator.setText2(formatResourceName(event)); @@ -103,6 +112,7 @@ public class Maven30TransferListenerAdapter implements TransferListener { try { myIndicator.setText2("Checksum failed: " + formatResourceName(event)); myIndicator.setIndeterminate(true); + myIndicator.completeTask(DEPENDENCIES_RESOLVE_PREFIX + formatResourceName(event), "Checksum failed"); } catch (RemoteException e) { throw new RuntimeRemoteException(e); @@ -114,6 +124,9 @@ public class Maven30TransferListenerAdapter implements TransferListener { try { myIndicator.setText2("Finished (" + StringUtilRt.formatFileSize(event.getTransferredBytes()) + ") " + formatResourceName(event)); myIndicator.setIndeterminate(true); + myIndicator.completeTask(DEPENDENCIES_RESOLVE_PREFIX + formatResourceName(event), null); + + Maven3ServerGlobals.getDownloadListener().artifactDownloaded(event.getResource().getFile(), event.getResource().getResourceName()); } catch (RemoteException e) { throw new RuntimeRemoteException(e); @@ -125,6 +138,7 @@ public class Maven30TransferListenerAdapter implements TransferListener { try { if (myIndicator.isCanceled()) { myIndicator.setText2("Canceling..."); + myIndicator.completeTask(DEPENDENCIES_RESOLVE_PREFIX + formatResourceName(event), "Cancelled"); return; // Don't throw exception here. } } @@ -135,6 +149,12 @@ public class Maven30TransferListenerAdapter implements TransferListener { try { myIndicator.setText2("Failed to download " + formatResourceName(event)); myIndicator.setIndeterminate(true); + String message = "Failed to download"; + if(event.getException()!=null) { + message += ": " + event.getException().getMessage(); + } + myIndicator.completeTask(DEPENDENCIES_RESOLVE_PREFIX + formatResourceName(event), message); + } catch (RemoteException e) { throw new RuntimeRemoteException(e); diff --git a/plugins/maven/maven36-server-impl/src/org/jetbrains/idea/maven/server/TransferListenerAdapter.java b/plugins/maven/maven36-server-impl/src/org/jetbrains/idea/maven/server/TransferListenerAdapter.java index afc78cebf0af..b01be54a3410 100644 --- a/plugins/maven/maven36-server-impl/src/org/jetbrains/idea/maven/server/TransferListenerAdapter.java +++ b/plugins/maven/maven36-server-impl/src/org/jetbrains/idea/maven/server/TransferListenerAdapter.java @@ -24,6 +24,8 @@ import org.eclipse.aether.transfer.TransferResource; import java.io.File; import java.rmi.RemoteException; +import static org.jetbrains.idea.maven.server.MavenServerProgressIndicator.DEPENDENCIES_RESOLVE_PREFIX; + public class TransferListenerAdapter implements TransferListener { protected final MavenServerProgressIndicator myIndicator; @@ -51,6 +53,13 @@ public class TransferListenerAdapter implements TransferListener { public void transferInitiated(TransferEvent event) { checkCanceled(); + try { + myIndicator.startTask(DEPENDENCIES_RESOLVE_PREFIX + formatResourceName(event)); + } + catch (RemoteException e) { + throw new RuntimeRemoteException(e); + } + try { myIndicator.setIndeterminate(true); myIndicator.setText2(formatResourceName(event)); @@ -100,6 +109,7 @@ public class TransferListenerAdapter implements TransferListener { try { myIndicator.setText2("Checksum failed: " + formatResourceName(event)); myIndicator.setIndeterminate(true); + myIndicator.completeTask(DEPENDENCIES_RESOLVE_PREFIX + formatResourceName(event), "Checksum failed"); } catch (RemoteException e) { throw new RuntimeRemoteException(e); @@ -111,6 +121,7 @@ public class TransferListenerAdapter implements TransferListener { try { myIndicator.setText2("Finished (" + StringUtilRt.formatFileSize(event.getTransferredBytes()) + ") " + formatResourceName(event)); myIndicator.setIndeterminate(true); + myIndicator.completeTask(DEPENDENCIES_RESOLVE_PREFIX + formatResourceName(event), null); Maven3ServerGlobals.getDownloadListener().artifactDownloaded(event.getResource().getFile(), event.getResource().getResourceName()); } @@ -124,6 +135,7 @@ public class TransferListenerAdapter implements TransferListener { try { if (myIndicator.isCanceled()) { myIndicator.setText2("Canceling..."); + myIndicator.completeTask(DEPENDENCIES_RESOLVE_PREFIX + formatResourceName(event), "Cancelled"); return; // Don't throw exception here. } } @@ -134,6 +146,12 @@ public class TransferListenerAdapter implements TransferListener { try { myIndicator.setText2("Failed to download " + formatResourceName(event)); myIndicator.setIndeterminate(true); + String message = "Failed to download"; + if(event.getException()!=null) { + message += ": " + event.getException().getMessage(); + } + myIndicator.completeTask(DEPENDENCIES_RESOLVE_PREFIX + formatResourceName(event), message); + } catch (RemoteException e) { throw new RuntimeRemoteException(e); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/buildtool/MavenSyncConsole.kt b/plugins/maven/src/main/java/org/jetbrains/idea/maven/buildtool/MavenSyncConsole.kt new file mode 100644 index 000000000000..3ce91c5a5b65 --- /dev/null +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/buildtool/MavenSyncConsole.kt @@ -0,0 +1,206 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package org.jetbrains.idea.maven.buildtool + +import com.intellij.build.BuildProgressListener +import com.intellij.build.DefaultBuildDescriptor +import com.intellij.build.SyncViewManager +import com.intellij.build.events.EventResult +import com.intellij.build.events.Failure +import com.intellij.build.events.impl.* +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId +import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskType +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Ref +import com.intellij.openapi.util.text.StringUtil +import org.jetbrains.annotations.TestOnly +import org.jetbrains.idea.maven.utils.MavenUtil + + +class MavenSyncConsole(private val myProject: Project) { + private lateinit var mySyncView: BuildProgressListener + private lateinit var myTaskId: ExternalSystemTaskId + private var finished = false + private var started = false + + private lateinit var myFailuresMap: LinkedHashMap> + private lateinit var myStartedSet: HashSet + private var tasksRunning: Int = 0 + + + @Synchronized + fun startImport() { + if(started){ + return + } + finished = false + started = true + myStartedSet = HashSet() + myFailuresMap = LinkedHashMap() + tasksRunning = 0 + + + myTaskId = ExternalSystemTaskId.create(MavenUtil.SYSTEM_ID, ExternalSystemTaskType.RESOLVE_PROJECT, myProject) + val descriptor = DefaultBuildDescriptor(myTaskId, "Sync", myProject.basePath!!, System.currentTimeMillis()) + val result = Ref() + ApplicationManager.getApplication().invokeAndWait { result.set(ServiceManager.getService(myProject, SyncViewManager::class.java)) } + mySyncView = result.get() + mySyncView.onEvent(StartBuildEventImpl(descriptor, "Sync ${myProject.name}")) + debugLog("maven sync: started importing $myProject") + } + + + @Synchronized + fun addText(text: String) { + addText(text, true) + } + + @Synchronized + fun addText(text: String, stdout: Boolean) { + if(!started) return + if (StringUtil.isEmpty(text) || finished) { + return + } + //println("Maven sync: print $text into $parentId") + mySyncView.onEvent(OutputBuildEventImpl(myTaskId, "$text\n", stdout)) + } + + + @Synchronized + fun finishImport() { + debugLog("Maven sync: finishImport") + finished = true + tryFinish() + } + + @Synchronized + fun startTask(taskName: String) { + debugLog("Maven sync: start $taskName $tasksRunning") + if (finished||!started) { + return + } + tasksRunning += 1 + val title = getTitle(taskName) + val parentId = extractParent(taskName) + myStartedSet.add(taskName.trim('.')) + mySyncView.onEvent(StartEventImpl(title, parentId, System.currentTimeMillis(), title)) + } + + + @Synchronized + fun completeTask(taskName: String) { + debugLog("Maven sync: complete $taskName $tasksRunning") + if(!started || !myStartedSet.contains(taskName.trim('.'))) return + + val title = getTitle(taskName) + val parentId = extractParent(taskName) + val result = eventResult(title) + mySyncView.onEvent(FinishEventImpl(title, parentId, System.currentTimeMillis(), title, result)) + tasksRunning -= 1 + tryFinish() + } + + + @Synchronized + fun completeTask(taskName: String, e: Throwable) { + debugLog("Maven sync: complete $taskName $tasksRunning with $e") + if(!started || !myStartedSet.contains(taskName.trim('.'))) return + val title = getTitle(taskName) + val parentId = extractParent(taskName) + updateErrorStackAbove(e, taskName) + mySyncView.onEvent(FinishEventImpl(title, parentId, System.currentTimeMillis(), title, FailureResultImpl(e))) + tasksRunning -= 1 + tryFinish() + } + + @Synchronized + fun addRootError(e: Throwable) { + if(!started) return + addRootError(e.message?:"", e) + } + @Synchronized + fun addRootError(message: String, e: Throwable) { + addText(message, false) + myFailuresMap.compute("Sync") { _, list -> add(list, FailureImpl(message, e)) } + + } + + private fun updateErrorStackAbove(e: Throwable, taskName: String) { + taskName.splitToSequence("/").forEach { + myFailuresMap.compute(it) { _, list -> add(list, FailureImpl(e.message, e)) } + } + myFailuresMap.compute("Sync") { _, list -> add(list, FailureImpl(e.message, e)) } + } + + private fun add(list: ArrayList?, failure: Failure): ArrayList { + if (list == null) { + return arrayListOf(failure) + } + else { + list.add(failure) + return list + } + } + + private fun tryFinish() { + if (!finished || tasksRunning > 0) { + return + } + val result = eventResult("Sync") + mySyncView + .onEvent(FinishBuildEventImpl(myTaskId, null, System.currentTimeMillis(), "Sync Complete", result)) + started = false + } + + + private fun eventResult(title: String): EventResult { + val failures = myFailuresMap[title] + val result = if (failures != null) FailureResultImpl(failures) else SuccessResultImpl(false) + return result + } + + private fun extractParent(taskName: String): Any { + val parent = taskName.split("--").map { it.trimEnd('.') }.dropLast(1).joinToString("/") + return if (parent.isBlank()) myTaskId else parent + } + + private fun getTitle(taskName: String): String { + return taskName.split("--").last().trimEnd('.') + } + + private fun debugLog(text: String) { + println(text) + } + + @TestOnly + fun isFinished(): Boolean { + return finished + } + + @TestOnly + fun runningProcesses() : Int{ + return tasksRunning + } + + @TestOnly + fun getErrors(): ArrayList? { + return myFailuresMap["Sync"] + } + + @TestOnly + fun getErrors(key: String): ArrayList? { + return myFailuresMap[key] + } + + @TestOnly + fun started(key: String): Boolean{ + return myStartedSet.contains(key.trim('.')) + } + + companion object { + const val PLUGINS_RESOLVE_PREFIX = "Downloading Maven plugins--" + } + + +} diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenExternalParameters.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenExternalParameters.java index 31caba992a39..0600195763c3 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenExternalParameters.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenExternalParameters.java @@ -59,6 +59,7 @@ import org.jetbrains.idea.maven.project.MavenGeneralSettings; import org.jetbrains.idea.maven.project.MavenProject; import org.jetbrains.idea.maven.project.MavenProjectsManager; import org.jetbrains.idea.maven.server.MavenServerUtil; +import org.jetbrains.idea.maven.utils.MavenLog; import org.jetbrains.idea.maven.utils.MavenSettings; import org.jetbrains.idea.maven.utils.MavenUtil; @@ -126,6 +127,9 @@ public class MavenExternalParameters { final String mavenHome = resolveMavenHome(coreSettings, project, runConfiguration); final String mavenVersion = MavenUtil.getMavenVersion(mavenHome); + if(mavenVersion == null) { + throw new ExecutionException("Cannot run maven: Maven home " + mavenHome + " looks incorrect"); + } String sdkConfigLocation = "Settings | Build, Execution, Deployment | Build Tools | Maven | Runner | JRE"; verifyMavenSdkRequirements(jdk, mavenVersion, sdkConfigLocation); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/indices/MavenIndicesManager.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/indices/MavenIndicesManager.java index b8acf438e710..3424ca9a1076 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/indices/MavenIndicesManager.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/indices/MavenIndicesManager.java @@ -167,9 +167,7 @@ public class MavenIndicesManager implements Disposable { MavenIndices indicesObjectCache = getIndicesObject(); try { - MavenIndex localIndex = indicesObjectCache.add(LOCAL_REPOSITORY_ID, localRepository.getPath(), MavenIndex.Kind.LOCAL); - scheduleUpdate(project, Collections.singletonList(localIndex)); - return localIndex; + return indicesObjectCache.add(LOCAL_REPOSITORY_ID, localRepository.getPath(), MavenIndex.Kind.LOCAL); } catch (MavenIndexException e) { MavenLog.LOG.warn(e); @@ -236,7 +234,7 @@ public class MavenIndicesManager implements Disposable { public void run(@NotNull ProgressIndicator indicator) { try { indicator.setIndeterminate(false); - doUpdateIndices(projectOrNull, toSchedule, fullUpdate, new MavenProgressIndicator(indicator)); + doUpdateIndices(projectOrNull, toSchedule, fullUpdate, new MavenProgressIndicator(indicator,null)); } catch (MavenProcessCanceledException ignore) { } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProject.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProject.java index dfb1cfc86b39..ad4e0dd041d5 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProject.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProject.java @@ -694,6 +694,7 @@ public class MavenProject { Collections.singleton(getFile()), getActivatedProfilesIds(), locator); + MavenUtil.notifySyncForUnresolved(project, results); final MavenProjectReaderResult result = results.iterator().next(); MavenProjectChanges changes = set(result, generalSettings, false, result.readingProblems.isEmpty(), false); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManager.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManager.java index 11b393ba0850..9090cdab7b20 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManager.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManager.java @@ -46,6 +46,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; import org.jetbrains.concurrency.AsyncPromise; import org.jetbrains.concurrency.Promise; +import org.jetbrains.idea.maven.buildtool.MavenSyncConsole; import org.jetbrains.idea.maven.importing.MavenFoldersImporter; import org.jetbrains.idea.maven.importing.MavenPomPathModuleService; import org.jetbrains.idea.maven.importing.MavenProjectImporter; @@ -106,6 +107,7 @@ public class MavenProjectsManager extends MavenSimpleProjectComponent private MavenWorkspaceSettings myWorkspaceSettings; + private MavenSyncConsole mySyncConsole; private final MavenMergingUpdateQueue mySaveQueue; private static final int SAVE_DELAY = 1000; @@ -284,6 +286,13 @@ public class MavenProjectsManager extends MavenSimpleProjectComponent } } + public synchronized MavenSyncConsole getSyncConsole() { + if (mySyncConsole == null) { + mySyncConsole = new MavenSyncConsole(myProject); + } + return mySyncConsole; + } + private void initProjectsTree(boolean tryToLoadExisting) { if (tryToLoadExisting) { Path file = getProjectsTreeFile(); @@ -359,7 +368,7 @@ public class MavenProjectsManager extends MavenSimpleProjectComponent new MavenProjectsManagerWatcher(myProject, this, myProjectsTree, getGeneralSettings(), myReadingProcessor, myEmbeddersManager); myImportingQueue = new MavenMergingUpdateQueue(getComponentName() + ": Importing queue", IMPORT_DELAY, !isUnitTestMode(), myProject); - myImportingQueue.setPassThrough(false); + myImportingQueue.setPassThrough(ApplicationManager.getApplication().isUnitTestMode()); myImportingQueue.makeUserAware(myProject); myImportingQueue.makeDumbAware(myProject); @@ -833,9 +842,12 @@ public class MavenProjectsManager extends MavenSimpleProjectComponent * if project is closed) */ public Promise> scheduleImportAndResolve() { + getSyncConsole().startImport(); AsyncPromise> promise = scheduleResolve();// scheduleImport will be called after the scheduleResolve process has finished fireImportAndResolveScheduled(); - return promise; + return promise + .onError(t -> getSyncConsole().addRootError(t)) + .onProcessed(modules -> getSyncConsole().finishImport()); } private AsyncPromise> scheduleResolve() { diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsTree.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsTree.java index 2046e14eb85f..7dcfe45a7bb4 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsTree.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsTree.java @@ -47,6 +47,7 @@ import org.jdom.output.XMLOutputter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; +import org.jetbrains.idea.maven.buildtool.MavenSyncConsole; import org.jetbrains.idea.maven.dom.references.MavenFilteredPropertyPsiReferenceProvider; import org.jetbrains.idea.maven.execution.RunnerBundle; import org.jetbrains.idea.maven.importing.MavenImporter; @@ -66,6 +67,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.regex.Pattern; import java.util.zip.CRC32; + public class MavenProjectsTree { private static final Logger LOG = Logger.getInstance(MavenProjectsTree.class); @@ -535,7 +537,6 @@ public class MavenProjectsTree { return; } updateStack.push(mavenProject); - process.setText(ProjectBundle.message("maven.reading.pom", mavenProject.getPath())); process.setText2(""); @@ -1281,6 +1282,7 @@ public class MavenProjectsTree { if (FileUtil.pathsEqual(mavenProject.getDirectory(), cause.getDirectory())) { showNotificationInvalidConfig(project, mavenProject, cause.getMessage()); mavenProject.setConfigFileError(cause.getMessage()); + MavenProjectsManager.getInstance(myProject).getSyncConsole().addRootError(RunnerBundle.message("maven.invalid.config.file", cause.getMessage()), cause); } } } @@ -1354,6 +1356,7 @@ public class MavenProjectsTree { Collection results = new MavenProjectReader(project).resolveProject( generalSettings, embedder, files, explicitProfiles, myProjectLocator); + MavenUtil.notifySyncForUnresolved(project, results); for (MavenProjectReaderResult result : results) { MavenProject mavenProjectCandidate = null; for (MavenProject mavenProject : mavenProjects) { @@ -1404,6 +1407,11 @@ public class MavenProjectsTree { filesToRefresh.add(pluginDir); // Refresh both *.pom and *.jar files. } } + if(artifacts.isEmpty()) { + process.startTask(MavenSyncConsole.PLUGINS_RESOLVE_PREFIX + each.getMavenId()); + process.completeTask(MavenSyncConsole.PLUGINS_RESOLVE_PREFIX + each.getMavenId(), "Cannot Resolve plugin"); + } + } mavenProject.resetCache(); @@ -1605,7 +1613,10 @@ public class MavenProjectsTree { } public void fireUpdatedIfNecessary() { - if (updatedProjectsWithChanges.isEmpty() && deletedProjects.isEmpty()) return; + if (updatedProjectsWithChanges.isEmpty() && deletedProjects.isEmpty()) { + //MavenProjectsManager.getInstance(myProject).getSyncConsole().finishImport(); + return; + } List mavenProjects = deletedProjects.isEmpty() ? Collections.emptyList() : new ArrayList<>(deletedProjects); @@ -1755,7 +1766,7 @@ public class MavenProjectsTree { } default void projectResolved(@NotNull Pair projectWithChanges, - @Nullable NativeMavenProjectHolder nativeMavenProject) { + @Nullable NativeMavenProjectHolder nativeMavenProject) { } default void pluginsResolved(@NotNull MavenProject project) { @@ -1778,8 +1789,8 @@ public class MavenProjectsTree { @Override public boolean equals(MavenCoordinate o1, MavenCoordinate o2) { return Comparing.equal(o1.getArtifactId(), o2.getArtifactId()) - && Comparing.equal(o1.getVersion(), o2.getVersion()) - && Comparing.equal(o1.getGroupId(), o2.getGroupId()); + && Comparing.equal(o1.getVersion(), o2.getVersion()) + && Comparing.equal(o1.getGroupId(), o2.getGroupId()); } } } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/server/MavenServerManager.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/server/MavenServerManager.java index 78ad8212e692..66352292f802 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/server/MavenServerManager.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/server/MavenServerManager.java @@ -619,6 +619,16 @@ public class MavenServerManager extends RemoteObjectWrapper impleme myProcess.setText2(text); } + @Override + public void startTask(String text) throws RemoteException { + myProcess.startTask(text); + } + + @Override + public void completeTask(String text, String errorMessage) throws RemoteException { + myProcess.completeTask(text, errorMessage); + } + @Override public boolean isCanceled() { return myProcess.isCanceled(); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenProgressIndicator.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenProgressIndicator.java index 9fa525bb9d85..00fcd2d6b9c4 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenProgressIndicator.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenProgressIndicator.java @@ -15,27 +15,39 @@ */ package org.jetbrains.idea.maven.utils; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.progress.EmptyProgressIndicator; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.TaskInfo; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.wm.ex.ProgressIndicatorEx; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.maven.buildtool.MavenSyncConsole; import java.util.ArrayList; import java.util.List; +import java.util.function.Supplier; public class MavenProgressIndicator { private ProgressIndicator myIndicator; private final List> myCancelConditions = new ArrayList<>(); + private final Supplier mySyncSupplier; - public MavenProgressIndicator() { - this(new MyEmptyProgressIndicator()); + public MavenProgressIndicator(Supplier syncSupplier) { + this(new MyEmptyProgressIndicator(), syncSupplier); } - public MavenProgressIndicator(ProgressIndicator i) { + public MavenProgressIndicator(ProgressIndicator i, + Supplier syncSupplier) { + myIndicator = i; + mySyncSupplier = syncSupplier; } public synchronized void setIndicator(ProgressIndicator i) { + //setIndicatorStatus(i); i.setText(myIndicator.getText()); i.setText2(myIndicator.getText2()); if (!i.isIndeterminate()) { @@ -51,10 +63,17 @@ public class MavenProgressIndicator { public synchronized void setText(String text) { myIndicator.setText(text); + if (mySyncSupplier != null) { + mySyncSupplier.get().addText(text); + } + } public synchronized void setText2(String text) { myIndicator.setText2(text); + if (mySyncSupplier != null) { + mySyncSupplier.get().addText(text); + } } public synchronized void setFraction(double fraction) { @@ -102,6 +121,23 @@ public class MavenProgressIndicator { if (isCanceled()) throw new ProcessCanceledException(); } + public void startTask(String text) { + if (mySyncSupplier != null) { + mySyncSupplier.get().startTask(text); + } + } + + public void completeTask(String text, String message) { + if (mySyncSupplier != null) { + if (message == null) { + mySyncSupplier.get().completeTask(text); + } + else { + mySyncSupplier.get().completeTask(text, new RuntimeException(message)); + } + } + } + private static class MyEmptyProgressIndicator extends EmptyProgressIndicator { private String myText; diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java index 4e0a50ab3e79..7b7cb7374c93 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenUtil.java @@ -52,11 +52,14 @@ import icons.MavenIcons; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.maven.buildtool.MavenSyncConsole; import org.jetbrains.idea.maven.dom.MavenDomUtil; +import org.jetbrains.idea.maven.model.MavenArtifact; import org.jetbrains.idea.maven.model.MavenConstants; import org.jetbrains.idea.maven.model.MavenId; import org.jetbrains.idea.maven.model.MavenPlugin; import org.jetbrains.idea.maven.project.MavenProject; +import org.jetbrains.idea.maven.project.MavenProjectReaderResult; import org.jetbrains.idea.maven.project.MavenProjectsManager; import org.jetbrains.idea.maven.server.MavenServerManager; import org.jetbrains.idea.maven.server.MavenServerUtil; @@ -84,6 +87,7 @@ import static com.intellij.openapi.util.io.JarUtil.getJarAttribute; import static com.intellij.openapi.util.io.JarUtil.loadProperties; import static com.intellij.openapi.util.text.StringUtil.*; import static com.intellij.util.xml.NanoXmlBuilder.stop; +import static org.jetbrains.idea.maven.server.MavenServerProgressIndicator.DEPENDENCIES_RESOLVE_PREFIX; public class MavenUtil { @ApiStatus.Experimental @@ -414,7 +418,8 @@ public class MavenUtil { return true; } - public static void run(Project project, String title, final MavenTask task) throws MavenProcessCanceledException { + public static void run(Project project, String title, final MavenTask task) + throws MavenProcessCanceledException { final Exception[] canceledEx = new Exception[1]; final RuntimeException[] runtimeEx = new RuntimeException[1]; final Error[] errorEx = new Error[1]; @@ -423,7 +428,7 @@ public class MavenUtil { @Override public void run(@NotNull ProgressIndicator i) { try { - task.run(new MavenProgressIndicator(i)); + task.run(new MavenProgressIndicator(i, null)); } catch (MavenProcessCanceledException | ProcessCanceledException e) { canceledEx[0] = e; @@ -447,15 +452,20 @@ public class MavenUtil { final String title, final boolean cancellable, final MavenTask task) { - final MavenProgressIndicator indicator = new MavenProgressIndicator(); + MavenProjectsManager manager = MavenProjectsManager.getInstance(project); + + final MavenProgressIndicator indicator = new MavenProgressIndicator(manager::getSyncConsole); Runnable runnable = () -> { if (project.isDisposed()) return; try { + manager.getSyncConsole().startTask(title); task.run(indicator); + manager.getSyncConsole().completeTask(title); } - catch (MavenProcessCanceledException | ProcessCanceledException ignore) { + catch (MavenProcessCanceledException | ProcessCanceledException e) { + manager.getSyncConsole().completeTask(title, e); indicator.cancel(); } }; @@ -637,6 +647,7 @@ public class MavenUtil { } } } + MavenLog.LOG.warn("Cannot resolve maven version for " + mavenHome); return null; } @@ -792,6 +803,29 @@ public class MavenUtil { return res; } + public static void notifySyncForUnresolved(@NotNull Project project, @NotNull Collection results) { + Set unresolvedIds = new HashSet<>(); + for (MavenProjectReaderResult result : results) { + if (result.mavenModel.getDependencies() != null) { + for (MavenArtifact artifact : result.mavenModel.getDependencies()) { + if (!artifact.isResolved()) { + unresolvedIds.add(artifact.getMavenId()); + } + } + } + } + + if (unresolvedIds.isEmpty()) { + return; + } + + MavenSyncConsole syncConsole = MavenProjectsManager.getInstance(project).getSyncConsole(); + for (MavenId id : unresolvedIds) { + syncConsole.startTask(DEPENDENCIES_RESOLVE_PREFIX + id.getKey()); + syncConsole.completeTask(DEPENDENCIES_RESOLVE_PREFIX + id.getKey(), new RuntimeException(id + " not resolved")); + } + } + public interface MavenTaskHandler { void waitFor(); } diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/MavenImportingTestCase.java b/plugins/maven/src/test/java/org/jetbrains/idea/maven/MavenImportingTestCase.java index 4febe8b40216..5ad40739114f 100644 --- a/plugins/maven/src/test/java/org/jetbrains/idea/maven/MavenImportingTestCase.java +++ b/plugins/maven/src/test/java/org/jetbrains/idea/maven/MavenImportingTestCase.java @@ -28,6 +28,7 @@ import com.intellij.util.ui.UIUtil; import org.intellij.lang.annotations.Language; import org.jetbrains.annotations.NotNull; import org.jetbrains.concurrency.AsyncPromise; +import org.jetbrains.idea.maven.buildtool.MavenSyncConsole; import org.jetbrains.idea.maven.execution.*; import org.jetbrains.idea.maven.model.MavenArtifact; import org.jetbrains.idea.maven.model.MavenExplicitProfiles; @@ -396,6 +397,11 @@ public abstract class MavenImportingTestCase extends MavenTestCase { assertFalse("Failed to import Maven project: " + each.getProblems(), each.hasReadingProblems()); } } + + MavenSyncConsole syncConsole = myProjectsManager.getSyncConsole(); + assertEquals(0, syncConsole.runningProcesses()); + assertTrue(syncConsole.isFinished()); + } protected void readProjects(List files, String... profiles) { diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/MavenTestCase.java b/plugins/maven/src/test/java/org/jetbrains/idea/maven/MavenTestCase.java index 737b78d3e642..44474bb1220f 100644 --- a/plugins/maven/src/test/java/org/jetbrains/idea/maven/MavenTestCase.java +++ b/plugins/maven/src/test/java/org/jetbrains/idea/maven/MavenTestCase.java @@ -48,7 +48,7 @@ public abstract class MavenTestCase extends UsefulTestCase { protected static final MavenConsole NULL_MAVEN_CONSOLE = new NullMavenConsole(); // should not be static protected static MavenProgressIndicator EMPTY_MAVEN_PROCESS = - new MavenProgressIndicator(new EmptyProgressIndicator(ModalityState.NON_MODAL)); + new MavenProgressIndicator(new EmptyProgressIndicator(ModalityState.NON_MODAL), null); private File ourTempDir; diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/importing/MavenSyncTest.java b/plugins/maven/src/test/java/org/jetbrains/idea/maven/importing/MavenSyncTest.java new file mode 100644 index 000000000000..2e7cc4fcdac7 --- /dev/null +++ b/plugins/maven/src/test/java/org/jetbrains/idea/maven/importing/MavenSyncTest.java @@ -0,0 +1,49 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package org.jetbrains.idea.maven.importing; + +import org.jetbrains.idea.maven.MavenImportingTestCase; + +public class MavenSyncTest extends MavenImportingTestCase { + public static final String PLUGINS_RESOLVE_PREFIX = "Downloading Maven plugins--"; + + public void testSync() { + importProject("test" + + "project" + + "jar" + + "1"); + + assertNoErrorsInSync(); + } + + public void testUnresolvedDependency() { + importProject("test" + + "project" + + "jar" + + "1" + + "" + + "" + + "unknown" + + "unknown" + + "123" + + "" + + "" + + ""); + + assertEventRegistered("Resolving Maven dependencies"); + assertError("unknown:unknown:123 not resolved"); + + } + + private void assertError(String message) { + assertTrue("Should have error " + message, myProjectsManager.getSyncConsole().getErrors().stream().anyMatch(e -> message.equals(e.getMessage()))); + } + + private void assertEventRegistered(String step) { + assertTrue("Should have step " + step, myProjectsManager.getSyncConsole().started(step)); + } + + private void assertNoErrorsInSync() { + assertNullOrEmpty(myProjectsManager.getSyncConsole().getErrors()); + } +} +