Skip to content

Commit b329c70

Browse files
committed
Perform Maven target dependency update in non-UI thread
Depending on how many artifacts are updated, this might be a long-running operation and should therefore be run without blocking the application.
1 parent 66b65ce commit b329c70

File tree

9 files changed

+99
-36
lines changed

9 files changed

+99
-36
lines changed

org.eclipse.m2e.pde.ui/META-INF/MANIFEST.MF

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
22
Bundle-ManifestVersion: 2
33
Bundle-Name: M2E PDE Integration UI
44
Bundle-SymbolicName: org.eclipse.m2e.pde.ui;singleton:=true
5-
Bundle-Version: 2.1.1.qualifier
5+
Bundle-Version: 2.1.2.qualifier
66
Export-Package: org.eclipse.m2e.pde.ui.target.editor;x-friends:="org.eclipse.m2e.swtbot.tests"
77
Automatic-Module-Name: org.eclipse.m2e.pde.ui
88
Bundle-RequiredExecutionEnvironment: JavaSE-21
@@ -23,3 +23,23 @@ Service-Component: OSGI-INF/org.eclipse.m2e.pde.ui.target.adapter.DependencyNode
2323
OSGI-INF/org.eclipse.m2e.pde.ui.target.adapter.MavenTargetAdapterFactory.xml,
2424
OSGI-INF/org.eclipse.m2e.pde.ui.target.adapter.MavenTargetBundleAdapterFactory.xml,
2525
OSGI-INF/org.eclipse.m2e.pde.ui.target.adapter.MavenTargetDependencyAdapterFactory.xml
26+
Service-Component: OSGI-INF/org.eclipse.m2e.pde.ui.target.adapter.DependencyNodeAdapterFactory.xml,
27+
OSGI-INF/org.eclipse.m2e.pde.ui.target.adapter.MavenTargetAdapterFactory.xml,
28+
OSGI-INF/org.eclipse.m2e.pde.ui.target.adapter.MavenTargetBundleAdapterFactory.xml,
29+
OSGI-INF/org.eclipse.m2e.pde.ui.target.adapter.MavenTargetDependencyAdapterFactory.xml
30+
Bundle-ActivationPolicy: lazy
31+
Service-Component: OSGI-INF/org.eclipse.m2e.pde.ui.target.adapter.DependencyNodeAdapterFactory.xml,
32+
OSGI-INF/org.eclipse.m2e.pde.ui.target.adapter.MavenTargetAdapterFactory.xml,
33+
OSGI-INF/org.eclipse.m2e.pde.ui.target.adapter.MavenTargetBundleAdapterFactory.xml,
34+
OSGI-INF/org.eclipse.m2e.pde.ui.target.adapter.MavenTargetDependencyAdapterFactory.xml
35+
Bundle-ActivationPolicy: lazy
36+
Service-Component: OSGI-INF/org.eclipse.m2e.pde.ui.target.adapter.DependencyNodeAdapterFactory.xml,
37+
OSGI-INF/org.eclipse.m2e.pde.ui.target.adapter.MavenTargetAdapterFactory.xml,
38+
OSGI-INF/org.eclipse.m2e.pde.ui.target.adapter.MavenTargetBundleAdapterFactory.xml,
39+
OSGI-INF/org.eclipse.m2e.pde.ui.target.adapter.MavenTargetDependencyAdapterFactory.xml
40+
Bundle-ActivationPolicy: lazy
41+
Service-Component: OSGI-INF/org.eclipse.m2e.pde.ui.target.adapter.DependencyNodeAdapterFactory.xml,
42+
OSGI-INF/org.eclipse.m2e.pde.ui.target.adapter.MavenTargetAdapterFactory.xml,
43+
OSGI-INF/org.eclipse.m2e.pde.ui.target.adapter.MavenTargetBundleAdapterFactory.xml,
44+
OSGI-INF/org.eclipse.m2e.pde.ui.target.adapter.MavenTargetDependencyAdapterFactory.xml
45+
Bundle-ActivationPolicy: lazy

org.eclipse.m2e.pde.ui/src/org/eclipse/m2e/pde/ui/target/editor/MavenTargetDependencyEditor.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2021, 2022 Christoph Läubrich and others
2+
* Copyright (c) 2021, 2025 Christoph Läubrich and others
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License 2.0 which is available at
@@ -30,10 +30,14 @@ public class MavenTargetDependencyEditor {
3030

3131
public MavenTargetDependencyEditor(Composite parent, MavenTargetLocation targetLocation,
3232
MavenTargetDependency selectedRoot) {
33+
this(parent, new TargetDependencyModel(targetLocation, selectedRoot));
34+
}
35+
36+
/* package */ MavenTargetDependencyEditor(Composite parent, TargetDependencyModel model) {
3337
composite = new Composite(parent, SWT.NONE);
3438
composite.setLayout(new BorderLayout());
3539

36-
model = new TargetDependencyModel(targetLocation, selectedRoot);
40+
this.model = model;
3741

3842
new DependencyTable(composite, model);
3943
}

org.eclipse.m2e.pde.ui/src/org/eclipse/m2e/pde/ui/target/editor/MavenTargetLocationWizard.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2018, 2023 Christoph Läubrich and others
2+
* Copyright (c) 2018, 2025 Christoph Läubrich and others
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License 2.0 which is available at
@@ -41,6 +41,7 @@
4141
import org.eclipse.m2e.pde.target.MissingMetadataMode;
4242
import org.eclipse.m2e.pde.target.TemplateFeatureModel;
4343
import org.eclipse.m2e.pde.target.shared.DependencyDepth;
44+
import org.eclipse.m2e.pde.ui.target.editor.internal.TargetDependencyModel;
4445
import org.eclipse.pde.core.target.ITargetDefinition;
4546
import org.eclipse.pde.core.target.ITargetLocation;
4647
import org.eclipse.pde.ui.target.ITargetLocationWizard;
@@ -86,6 +87,7 @@ public MavenTargetLocationWizard() {
8687
public MavenTargetLocationWizard(MavenTargetLocation targetLocation) {
8788
this.targetLocation = targetLocation;
8889
setWindowTitle(Messages.MavenTargetLocationWizard_0);
90+
setNeedsProgressMonitor(true);
8991
if (targetLocation != null) {
9092
for (MavenTargetRepository mavenTargetRepository : targetLocation.getExtraRepositories()) {
9193
repositoryList.add(mavenTargetRepository.copy());
@@ -105,7 +107,9 @@ public void createControl(Composite parent) {
105107
setControl(composite);
106108
composite.setLayout(new GridLayout(2, false));
107109
createRepositoryLink(composite);
108-
dependencyEditor = new MavenTargetDependencyEditor(composite, targetLocation, selectedRoot);
110+
TargetDependencyModel dependencyModel = new TargetDependencyModel(targetLocation, selectedRoot);
111+
dependencyModel.setContext(getContainer());
112+
dependencyEditor = new MavenTargetDependencyEditor(composite, dependencyModel);
109113
dependencyEditor.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
110114

111115
new Label(composite, SWT.NONE).setText(Messages.MavenTargetLocationWizard_14);

org.eclipse.m2e.pde.ui/src/org/eclipse/m2e/pde/ui/target/editor/Messages.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2020, 2023 Christoph Läubrich and others
2+
* Copyright (c) 2020, 2025 Christoph Läubrich and others
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License 2.0 which is available at
@@ -57,6 +57,7 @@ public class Messages extends NLS {
5757
public static String MavenTargetLocationWizard_23;
5858
public static String MavenTargetLocationLabelProvider_1;
5959
public static String MavenTargetLocationLabelProvider_2;
60+
public static String TargetDependencyModel_1;
6061
static {
6162
// initialize resource bundle
6263
NLS.initializeMessages(BUNDLE_NAME, Messages.class);

org.eclipse.m2e.pde.ui/src/org/eclipse/m2e/pde/ui/target/editor/internal/TargetDependencyModel.java

Lines changed: 57 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2023 Patrick Ziegler and others.
2+
* Copyright (c) 2023, 2025 Patrick Ziegler and others.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v2.0
55
* which accompanies this distribution, and is available at
@@ -12,6 +12,7 @@
1212
*******************************************************************************/
1313
package org.eclipse.m2e.pde.ui.target.editor.internal;
1414

15+
import java.lang.reflect.InvocationTargetException;
1516
import java.util.ArrayList;
1617
import java.util.Collections;
1718
import java.util.List;
@@ -26,10 +27,15 @@
2627
import org.eclipse.core.runtime.CoreException;
2728
import org.eclipse.core.runtime.ILog;
2829
import org.eclipse.core.runtime.Platform;
30+
import org.eclipse.core.runtime.SubMonitor;
31+
import org.eclipse.jface.operation.IRunnableContext;
2932
import org.eclipse.m2e.pde.target.MavenTargetDependency;
3033
import org.eclipse.m2e.pde.target.MavenTargetLocation;
3134
import org.eclipse.m2e.pde.ui.target.editor.ClipboardParser;
35+
import org.eclipse.m2e.pde.ui.target.editor.Messages;
3236
import org.eclipse.swt.widgets.Display;
37+
import org.eclipse.ui.PlatformUI;
38+
import org.eclipse.ui.progress.IProgressService;
3339

3440
/**
3541
* This class represents the data model used by the dependency editor. It keeps
@@ -67,6 +73,10 @@ public class TargetDependencyModel {
6773
* Each dependency must contain a valid group id, artifact id, version and type.
6874
*/
6975
private final IObservableValue<Boolean> hasErrors = new WritableValue<>();
76+
/**
77+
* The context in which all long-running operations are executed in.
78+
*/
79+
private IRunnableContext context;
7080

7181
public TargetDependencyModel(MavenTargetLocation targetLocation, MavenTargetDependency selectedRoot) {
7282
this.history = new OperationHistoryFacade(this);
@@ -215,27 +225,38 @@ public void update() {
215225
List<MavenTargetDependency> newTargetDependencies = new ArrayList<>(oldTargetDependencies);
216226

217227
try {
218-
int updated = 0;
219-
220-
for (MavenTargetDependency dependency : oldCurrentSelection) {
221-
int index = oldTargetDependencies.indexOf(dependency);
222-
223-
MavenTargetDependency newDependency = targetLocation.update(dependency, null);
224-
225-
if (!dependency.matches(newDependency)) {
226-
updated++;
228+
getContext().run(true, true, monitor -> {
229+
int updated = 0;
230+
231+
SubMonitor subMonitor = SubMonitor.convert(monitor, oldCurrentSelection.size());
232+
for (MavenTargetDependency dependency : oldCurrentSelection) {
233+
int index = oldTargetDependencies.indexOf(dependency);
234+
235+
subMonitor.subTask(Messages.bind(Messages.TargetDependencyModel_1, dependency.getKey()));
236+
MavenTargetDependency newDependency;
237+
try {
238+
newDependency = targetLocation.update(dependency, subMonitor.split(1, SubMonitor.SUPPRESS_ALL_LABELS));
239+
} catch (CoreException unwrapped) {
240+
throw new InvocationTargetException(unwrapped);
241+
}
242+
243+
if (!dependency.matches(newDependency)) {
244+
updated++;
245+
}
246+
247+
newTargetDependencies.set(index, newDependency);
248+
newCurrentSelection.add(newDependency);
227249
}
228-
229-
newTargetDependencies.set(index, newDependency);
230-
newCurrentSelection.add(newDependency);
231-
}
232-
233-
// An "empty" update should not show up on the command stack...
234-
if (updated > 0) {
235-
history.modelChange(newTargetDependencies, newCurrentSelection);
236-
}
237-
} catch (CoreException e) {
238-
LOGGER.error(e.getMessage(), e);
250+
251+
// An "empty" update should not show up on the command stack...
252+
if (updated > 0) {
253+
history.getRealm().asyncExec(() -> history.modelChange(newTargetDependencies, newCurrentSelection));
254+
}
255+
});
256+
} catch (InvocationTargetException wrapped) {
257+
LOGGER.error(wrapped.getMessage(), wrapped);
258+
} catch (InterruptedException ignored) {
259+
// operation cancelled by user
239260
}
240261
}
241262

@@ -301,6 +322,21 @@ public void check() {
301322
hasErrors.setValue(allFields.anyMatch(StringUtils::isBlank));
302323
}
303324

325+
/**
326+
* Sets the UI context used for executing long-running operations such as
327+
* updating Maven dependencies. If {@code null} is passed as an argument, the
328+
* {@link IProgressService} is used.
329+
*
330+
* @param context The UI context. May be {@code null}.
331+
*/
332+
public void setContext(IRunnableContext context) {
333+
this.context = context;
334+
}
335+
336+
private IRunnableContext getContext() {
337+
return context != null ? context : PlatformUI.getWorkbench().getProgressService();
338+
}
339+
304340
private static List<MavenTargetDependency> deepClone(List<MavenTargetDependency> dependencies) {
305341
List<MavenTargetDependency> copy = new ArrayList<>();
306342

org.eclipse.m2e.pde.ui/src/org/eclipse/m2e/pde/ui/target/editor/messages.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,4 @@ MavenTargetLocationLabelProvider_1={0}:{1} ({2})
3939
MavenTargetLocationLabelProvider_2={0} Maven Dependencies
4040
NewFeatureWizard_PlugPage_title = Additionally referenced Plug-ins and Fragments
4141
NewFeatureWizard_PlugPage_desc = Select the plug-ins and fragments that are additionally added to the generated feature.
42+
TargetDependencyModel_1=Updating {0}...

org.eclipse.m2e.swtbot.tests/META-INF/MANIFEST.MF

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
22
Bundle-ManifestVersion: 2
33
Bundle-Name: SWTBot Integration Tests
44
Bundle-SymbolicName: org.eclipse.m2e.swtbot.tests
5-
Bundle-Version: 2.1.0.qualifier
5+
Bundle-Version: 2.1.1.qualifier
66
Require-Bundle: org.eclipse.core.runtime;bundle-version="3.31.100",
77
org.eclipse.m2e.pde.target,
88
org.eclipse.m2e.pde.ui,

org.eclipse.m2e.swtbot.tests/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<version>2.1.0-SNAPSHOT</version>
99
</parent>
1010

11+
<version>2.1.1-SNAPSHOT</version>
1112
<artifactId>org.eclipse.m2e.swtbot.tests</artifactId>
1213
<packaging>eclipse-test-plugin</packaging>
1314
<name>M2E - SWTBot Integration tests</name>

org.eclipse.m2e.swtbot.tests/src/org/eclipse/m2e/pde/ui/MavenTargetDependencyEditorTest.java

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2024 Patrick Ziegler and others.
2+
* Copyright (c) 2024, 2025 Patrick Ziegler and others.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License 2.0 which is available at
@@ -12,6 +12,7 @@
1212
*******************************************************************************/
1313
package org.eclipse.m2e.pde.ui;
1414

15+
import static org.eclipse.swtbot.swt.finder.waits.Conditions.widgetIsEnabled;
1516
import static org.junit.Assert.assertEquals;
1617
import static org.junit.Assert.assertFalse;
1718
import static org.junit.Assert.assertNotEquals;
@@ -192,11 +193,6 @@ public void tearDown() throws Exception {
192193
workbench.getDisplay().syncExec(wizardDialog::close);
193194
}
194195
}
195-
196-
private void readAndDispatch() {
197-
Display display = workbench.getDisplay();
198-
display.syncExec(display::readAndDispatch);
199-
}
200196

201197
/**
202198
* Checks whether the initial "enablement" state of all buttons in the Maven
@@ -316,7 +312,7 @@ public void testUpdateMavenArtifactVersion() throws Exception {
316312

317313
table.select(12);
318314
robot.button("Update").click();
319-
readAndDispatch();
315+
robot.waitUntil(widgetIsEnabled(table));
320316

321317
assertEquals(table.cell(12, 1), "kotlin-stdlib-common");
322318
assertNotEquals(table.cell(12, 2), "1.7.22");
@@ -329,7 +325,7 @@ public void testUpdateMavenArtifactVersion() throws Exception {
329325

330326
table.select(13, 15);
331327
robot.button("Update").click();
332-
readAndDispatch();
328+
robot.waitUntil(widgetIsEnabled(table));
333329

334330
assertEquals(table.cell(13, 1), "kotlin-stdlib-jdk7");
335331
assertNotEquals(table.cell(13, 2), "1.7.22");

0 commit comments

Comments
 (0)