diff --git a/org.eclipse.m2e.core.ui/src/org/eclipse/m2e/core/ui/internal/archetype/ArchetypeGenerator.java b/org.eclipse.m2e.core.ui/src/org/eclipse/m2e/core/ui/internal/archetype/ArchetypeGenerator.java
index 452be9878..c16710d24 100644
--- a/org.eclipse.m2e.core.ui/src/org/eclipse/m2e/core/ui/internal/archetype/ArchetypeGenerator.java
+++ b/org.eclipse.m2e.core.ui/src/org/eclipse/m2e/core/ui/internal/archetype/ArchetypeGenerator.java
@@ -34,6 +34,7 @@
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.osgi.util.NLS;
@@ -64,9 +65,22 @@ public class ArchetypeGenerator {
IMavenLauncher mavenLauncher;
/**
- * Creates project structure using Archetype and then imports created project(s)
- *
+ * Creates project structure using Archetype and then imports created project(s). May block during execution. Pumps
+ * the UI event loop if called from the UI thread. Cancellation from progress monitor is honored and signaled in the
+ * return value. Equivalent to
+ * {@link #createArchetypeProjects(IPath, IArchetype, String, String, String, String, Map, boolean, IProgressMonitor)}
+ * with interactive = false
+ *
+ * @param location where to place them. may be null for the workspace root
+ * @param archetype archetype
+ * @param groupId groupid
+ * @param artifactId artifactid
+ * @param version version
+ * @param javaPackage java package
+ * @param properties initial properties (some properties will be set/overriden by this call)
+ * @param monitor monitor for progress and cancellation, may be null
* @return a list of created projects.
+ * @throws CoreException to signal an error, or a cancellation (if the contained status is CANCEL)
* @since 1.8
*/
public Collection createArchetypeProjects(IPath location, IArchetype archetype, String groupId,
@@ -76,6 +90,23 @@ public Collection createArchetypeProjects(IPath location, IArc
monitor);
}
+ /**
+ * Creates project structure using Archetype and then imports created project(s). May block during execution. Pumps
+ * the UI event loop if called from the UI thread. Cancellation from progress monitor is honored and signaled in the
+ * return value.
+ *
+ * @param location where to place them. may be null for the workspace root
+ * @param archetype archetype
+ * @param groupId groupid
+ * @param artifactId artifactid
+ * @param version version
+ * @param javaPackage java package
+ * @param properties initial properties (some properties will be set/overriden by this call)
+ * @param interactive see {@link IMavenLauncher#runMaven(File, String, Map, boolean)}
+ * @param monitor monitor for progress and cancellation, may be null
+ * @return a list of created projects.
+ * @throws CoreException to signal an error, or a cancellation (if the contained status is CANCEL)
+ */
public Collection createArchetypeProjects(IPath location, IArchetype archetype, String groupId,
String artifactId, String version, String javaPackage, Map properties, boolean interactive,
IProgressMonitor monitor) throws CoreException {
@@ -105,21 +136,23 @@ public Collection createArchetypeProjects(IPath location, IArc
userProperties.put("outputDirectory", basedir.getAbsolutePath());
String projectFolder = location.append(artifactId).toFile().getAbsolutePath();
+ CompletableFuture> mavenRun = null;
File[] workingDir = new File[1];
try (var workingDirCleaner = createEmptyWorkingDirectory(workingDir)) {
String goals = "-U " + ArchetypePlugin.ARCHETYPE_PREFIX + ":generate";
- CompletableFuture> maven = mavenLauncher.runMaven(workingDir[0], goals, userProperties, interactive);
+ mavenRun = mavenLauncher.runMaven(workingDir[0], goals, userProperties, interactive);
subMonitor.worked(1);
Display current = Display.getCurrent();
- while(!maven.isDone()) {
+ while(!mavenRun.isDone()) {
if(current != null) {
while(!current.isDisposed() && current.readAndDispatch()) {
//loop to process events
}
}
Thread.onSpinWait();
+ subMonitor.checkCanceled(); // check for cancellation when UI thread is idle only (if on UI thread)
}
- maven.get(); //wait for maven build to complete...
+ mavenRun.get(); //wait for maven build to complete...
subMonitor.worked(1);
LocalProjectScanner scanner = new LocalProjectScanner(List.of(projectFolder), true, mavenModelManager);
try {
@@ -128,7 +161,12 @@ public Collection createArchetypeProjects(IPath location, IArc
return List.of();
}
return projectConfigurationManager.collectProjects(scanner.getProjects());
- } catch(InterruptedException | CancellationException ex) {
+ } catch(InterruptedException | CancellationException | OperationCanceledException ex) {
+ // ensure cancellations that might not have originated from the Future itself, request it to cancel
+ if(mavenRun != null) {
+ mavenRun.cancel(true);
+ }
+ // in all cases, for API compatibility, do not throw OCE, and instead throw a CoreException with CANCEL status
throw new CoreException(Status.CANCEL_STATUS);
} catch(ExecutionException | IOException ex) {
if(ex.getCause() instanceof CoreException coreException) {
diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/AbstractProjectScanner.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/AbstractProjectScanner.java
index 7fc6c2a08..82eafbc0e 100644
--- a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/AbstractProjectScanner.java
+++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/project/AbstractProjectScanner.java
@@ -17,6 +17,7 @@
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
/**
@@ -54,5 +55,12 @@ protected void addError(Throwable exception) {
public abstract String getDescription();
+ /**
+ * Execute. Monitor cancellation will propagate as a thrown {@link OperationCanceledException}, InterruptedException
+ * is not an expected exception and remains for compatibility reasons and to handle unexpected thread interruptions.
+ *
+ * @param monitor a progress monitor for progress and cancellation, may be null
+ * @throws InterruptedException an unexpected thread interruption occurred
+ */
public abstract void run(IProgressMonitor monitor) throws InterruptedException;
}
diff --git a/org.eclipse.m2e.scm/src/org/eclipse/m2e/scm/MavenProjectPomScanner.java b/org.eclipse.m2e.scm/src/org/eclipse/m2e/scm/MavenProjectPomScanner.java
index bd048696a..8b1c524ff 100644
--- a/org.eclipse.m2e.scm/src/org/eclipse/m2e/scm/MavenProjectPomScanner.java
+++ b/org.eclipse.m2e.scm/src/org/eclipse/m2e/scm/MavenProjectPomScanner.java
@@ -26,6 +26,7 @@
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.osgi.util.NLS;
@@ -78,10 +79,12 @@ public String getDescription() {
return "" + dependencies.size() + " projects"; //$NON-NLS-1$
}
+ @SuppressWarnings("unused") // InterruptedException XXX unfortunately remains for API compatibility; not intentionally thrown or expected
public void run(IProgressMonitor monitor) throws InterruptedException {
+ monitor = IProgressMonitor.nullSafe(monitor);
for(Dependency d : dependencies) {
- if(monitor.isCanceled()) {
- throw new InterruptedException();
+ if(monitor.isCanceled()) { // intentionally not an InterruptedException, to follow progress monitor expectations
+ throw new OperationCanceledException();
}
try {