diff --git a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/ApiBaselineManager.java b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/ApiBaselineManager.java
index dee892bfdf..d48bf30468 100644
--- a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/ApiBaselineManager.java
+++ b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/ApiBaselineManager.java
@@ -483,6 +483,8 @@ public IApiComponent[] readBaselineComponents(ApiBaseline baseline, InputStream
}
}
restored = components.toArray(new IApiComponent[components.size()]);
+ // Avoid unstable bundle traversal order to simplify our life
+ Arrays.sort(restored, (o1, o2) -> o1.getName().compareTo(o2.getName()));
}
} catch (IOException | SAXException e) {
throw new CoreException(Status.error("Error restoring API baseline", e)); //$NON-NLS-1$
@@ -637,10 +639,10 @@ public IApiBaseline getWorkspaceBaseline() {
* the next request.
*/
public void disposeWorkspaceBaseline() {
- if (workspacebaseline == null) {
+ final IApiBaseline originalBaseline = workspacebaseline;
+ if (originalBaseline == null) {
return;
}
- final IApiBaseline originalBaseline = workspacebaseline;
IJobFunction runnable = m -> {
IApiBaseline oldBaseline = null;
synchronized (ApiBaselineManager.this) {
diff --git a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/ApiBaseline.java b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/ApiBaseline.java
index cae1e70fad..68b928e425 100644
--- a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/ApiBaseline.java
+++ b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/ApiBaseline.java
@@ -24,6 +24,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
@@ -32,6 +33,7 @@
import java.util.Map;
import java.util.Properties;
import java.util.Set;
+import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -39,6 +41,7 @@
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.OperationCanceledException;
@@ -47,6 +50,7 @@
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.launching.IVMInstall;
+import org.eclipse.jdt.launching.IVMInstall2;
import org.eclipse.jdt.launching.IVMInstallChangedListener;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.launching.PropertyChangeEvent;
@@ -63,6 +67,7 @@
import org.eclipse.osgi.service.resolver.StateObjectFactory;
import org.eclipse.pde.api.tools.internal.AnyValue;
import org.eclipse.pde.api.tools.internal.ApiBaselineManager;
+import org.eclipse.pde.api.tools.internal.ApiBaselineManager.ApiBaselineManagerRule;
import org.eclipse.pde.api.tools.internal.CoreMessages;
import org.eclipse.pde.api.tools.internal.builder.ApiAnalysisBuilder.ApiAnalysisJob;
import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin;
@@ -165,7 +170,7 @@ public class ApiBaseline extends ApiElement implements IApiBaseline, IVMInstallC
* The VM install this baseline is bound to for system libraries or
* null
. Only used in the IDE when OSGi is running.
*/
- private IVMInstall fVMBinding;
+ private volatile IVMInstall fVMBinding;
private volatile boolean disposed;
@@ -423,7 +428,7 @@ protected void resolveSystemLibrary(HashSet ees) {
if (ApiPlugin.isRunningInFramework() && fAutoResolve) {
IStatus error = null;
IExecutionEnvironmentsManager manager = JavaRuntime.getExecutionEnvironmentsManager();
- Map> vmToEEs = new HashMap<>();
+ Map> vmToEEs = new TreeMap<>(new VmVersionComparator());
for (String ee : ees) {
IExecutionEnvironment environment = manager.getEnvironment(ee);
if (environment != null) {
@@ -433,43 +438,22 @@ protected void resolveSystemLibrary(HashSet ees) {
}
}
}
- // select VM that is compatible with most required environments
- // keep list of all VMs
+ // The list is sorted with highest VM version first
List allVMInstalls = new ArrayList<>(vmToEEs.keySet());
- String systemEE = null;
- if (!allVMInstalls.isEmpty()) {
- for (IVMInstall iVMInstall : allVMInstalls) {
- // find the EE this VM is strictly compatible with
- IExecutionEnvironment[] environments = manager.getExecutionEnvironments();
- for (IExecutionEnvironment environment : environments) {
- if (environment.isStrictlyCompatible(iVMInstall)) {
- systemEE = environment.getId();
- break;
- }
- }
- if (systemEE == null) {
- // https://bugs.eclipse.org/bugs/show_bug.cgi?id=383261
- // we don't need to compute anything here, in all cases
- // if
- // we fail to find a compatible EE, fall back to highest
- // known.
- systemEE = "JavaSE-" + JavaCore.latestSupportedJavaVersion(); //$NON-NLS-1$
- }
- // only update if different from current or missing VM
- // binding
- if (!systemEE.equals(getExecutionEnvironment()) || fVMBinding == null) {
- try {
- File file = Util.createEEFile(iVMInstall, systemEE);
- JavaRuntime.addVMInstallChangedListener(this);
- fVMBinding = iVMInstall;
- ExecutionEnvironmentDescription ee = new ExecutionEnvironmentDescription(file);
- initialize(ee);
- file.delete();
- } catch (CoreException | IOException e) {
- error = Status.error(CoreMessages.ApiBaseline_2, e);
- }
- }
+ for (IVMInstall iVMInstall : allVMInstalls) {
+ try {
+ File tmpEeFile = Util.createEEFile(iVMInstall);
+ initialize(new ExecutionEnvironmentDescription(tmpEeFile));
+ tmpEeFile.delete();
+ fVMBinding = iVMInstall;
+ break;
+ } catch (CoreException | IOException e) {
+ error = Status.error(CoreMessages.ApiBaseline_2, e);
}
+ }
+
+ if (fVMBinding != null) {
+ JavaRuntime.addVMInstallChangedListener(this);
} else {
// no VMs match any required EE
error = Status.error(CoreMessages.ApiBaseline_6);
@@ -497,6 +481,70 @@ protected void resolveSystemLibrary(HashSet ees) {
}
}
+ /**
+ * Sorts highest VM version first
+ */
+ static final class VmVersionComparator implements Comparator {
+
+ private static final String UNKNOWN_VERSION = "UNKNOWN"; //$NON-NLS-1$
+ private static final Integer UNKNOWN_VERSION_ORDINAL = Integer.valueOf(-1);
+ private static final Map KNOWN_VERSIONS_MAP;
+ static {
+ List allVersions = JavaCore.getAllVersions();
+ KNOWN_VERSIONS_MAP = new HashMap<>(allVersions.size() + 1);
+ for (int i = 0; i < allVersions.size(); i++) {
+ KNOWN_VERSIONS_MAP.put(allVersions.get(i), Integer.valueOf(i));
+ }
+ KNOWN_VERSIONS_MAP.put(UNKNOWN_VERSION, UNKNOWN_VERSION_ORDINAL);
+ }
+
+ @Override
+ public int compare(IVMInstall o1, IVMInstall o2) {
+ String vmVersion1 = getSimpleVmVersion(o1);
+ String vmVersion2 = getSimpleVmVersion(o2);
+ Integer ordinal1 = getVmOrdinal(vmVersion1);
+ Integer ordinal2 = getVmOrdinal(vmVersion2);
+ // reversed order, so highest version is sorted first
+ return ordinal2.compareTo(ordinal1);
+ }
+
+ @SuppressWarnings("nls")
+ private static String getSimpleVmVersion(IVMInstall vm) {
+ if (!(vm instanceof IVMInstall2 vm2)) {
+ return UNKNOWN_VERSION;
+ }
+ String javaVersion = vm2.getJavaVersion();
+ if (javaVersion == null) {
+ return UNKNOWN_VERSION;
+ }
+ javaVersion = javaVersion.strip();
+ if (javaVersion.length() > 2 && javaVersion.startsWith("1.")) {
+ // 1.8.0 -> 1.8
+ javaVersion = javaVersion.substring(0, 3);
+ } else {
+ int firstDot = javaVersion.indexOf(".");
+ if (firstDot > 0) {
+ // 21.0.1 -> 21
+ javaVersion = javaVersion.substring(0, firstDot);
+ }
+ }
+ return javaVersion;
+ }
+
+ private static Integer getVmOrdinal(String vmVersion) {
+ Integer value = KNOWN_VERSIONS_MAP.get(vmVersion);
+ if (value == null) {
+ try {
+ // assume it is > Java 21 and can be parsed as integer
+ return Integer.valueOf(vmVersion);
+ } catch (Exception e) {
+ return UNKNOWN_VERSION_ORDINAL;
+ }
+ }
+ return value;
+ }
+ }
+
/**
* Returns true if the {@link IApiBaseline} has its information loaded
* (components) false otherwise. This is a handle only method that will not
@@ -956,22 +1004,47 @@ public void vmChanged(PropertyChangeEvent event) {
* Re-binds the VM this baseline is bound to.
*/
private void rebindVM() {
- Job.createSystem("Rebinding JVM", monitor -> { //$NON-NLS-1$
- try {
- // Let all the already running job finish first, to avoid errors
- Job.getJobManager().join(ApiAnalysisJob.class, monitor);
- } catch (OperationCanceledException | InterruptedException e) {
- ApiPlugin.log("Interrupted while rebinding JVM", e); //$NON-NLS-1$
- return;
+ final IVMInstall originalVm = fVMBinding;
+ Job.getJobManager().cancel(ApiAnalysisJob.class);
+ Job job = new Job("Rebinding JVM") { //$NON-NLS-1$
+
+ @Override
+ public IStatus run(IProgressMonitor monitor) {
+ if (monitor.isCanceled() || ApiBaseline.this.isDisposed()) {
+ return Status.CANCEL_STATUS;
+ }
+ try {
+ // Let all the already running job finish first, to avoid errors
+ Job.getJobManager().join(ApiAnalysisJob.class, monitor);
+ } catch (OperationCanceledException | InterruptedException e) {
+ ApiPlugin.log("Interrupted while rebinding JVM", e); //$NON-NLS-1$
+ return Status.CANCEL_STATUS;
+ }
+ if (originalVm != fVMBinding) {
+ return Status.CANCEL_STATUS;
+ }
+ fVMBinding = null;
+ IApiComponent[] components = getApiComponents();
+ HashSet ees = new HashSet<>();
+ for (IApiComponent component2 : components) {
+ try {
+ ees.addAll(Arrays.asList(component2.getExecutionEnvironments()));
+ } catch (CoreException e) {
+ ApiPlugin.log("Error reading execution environment from " + component2, e); //$NON-NLS-1$
+ }
+ }
+ resolveSystemLibrary(ees);
+ return Status.OK_STATUS;
}
- fVMBinding = null;
- IApiComponent[] components = getApiComponents();
- HashSet ees = new HashSet<>();
- for (IApiComponent component2 : components) {
- ees.addAll(Arrays.asList(component2.getExecutionEnvironments()));
+
+ @Override
+ public boolean belongsTo(Object family) {
+ return super.belongsTo(family) || family == ApiBaseline.class;
}
- resolveSystemLibrary(ees);
- }).schedule();
+ };
+ job.setRule(new ApiBaselineManagerRule());
+ job.setSystem(true);
+ job.schedule();
}
@Override
diff --git a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/provisional/comparator/ApiComparator.java b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/provisional/comparator/ApiComparator.java
index 126083aecf..1cdcc8b837 100644
--- a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/provisional/comparator/ApiComparator.java
+++ b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/provisional/comparator/ApiComparator.java
@@ -645,9 +645,12 @@ public void visit(String packageName, IApiTypeRoot typeRoot) {
// anonymous)
return;
}
- int visibility = 0;
+ int visibility;
if (elementDescription != null) {
visibility = elementDescription.getVisibility();
+ } else {
+ // Annotation is missing, not an API?
+ visibility = 0;
}
IApiTypeRoot typeRoot2 = null;
if (isSWT) {
diff --git a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/util/Util.java b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/util/Util.java
index 2b3d3fdbf6..6a11d556ae 100644
--- a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/util/Util.java
+++ b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/util/Util.java
@@ -117,6 +117,8 @@
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.launching.LibraryLocation;
import org.eclipse.jdt.launching.environments.ExecutionEnvironmentDescription;
+import org.eclipse.jdt.launching.environments.IExecutionEnvironment;
+import org.eclipse.jdt.launching.environments.IExecutionEnvironmentsManager;
import org.eclipse.jface.text.IDocument;
import org.eclipse.osgi.util.NLS;
import org.eclipse.pde.api.tools.internal.FilterStore;
@@ -440,9 +442,10 @@ public static boolean copy(File file, File newFile) {
}
/**
- * Creates an EE file for the given JRE and specified EE id
+ * Creates an EE file for the given JRE
*/
- public static File createEEFile(IVMInstall jre, String eeid) throws IOException {
+ public static File createEEFile(IVMInstall jre) throws IOException {
+ String eeid = getStrictCompatibleEE(jre);
String string = Util.generateEEContents(jre, eeid);
File eeFile = createTempFile("eed", ".ee"); //$NON-NLS-1$ //$NON-NLS-2$
try (FileOutputStream outputStream = new FileOutputStream(eeFile)) {
@@ -451,6 +454,21 @@ public static File createEEFile(IVMInstall jre, String eeid) throws IOException
return eeFile;
}
+ private static String getStrictCompatibleEE(IVMInstall iVMInstall) {
+ IExecutionEnvironmentsManager manager = JavaRuntime.getExecutionEnvironmentsManager();
+ IExecutionEnvironment[] environments = manager.getExecutionEnvironments();
+ for (IExecutionEnvironment environment : environments) {
+ if (environment.isStrictlyCompatible(iVMInstall)) {
+ return environment.getId();
+ }
+ }
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=383261
+ // we don't need to compute anything here, in all cases
+ // if we fail to find a compatible EE, fall back to highest
+ // known.
+ return "JavaSE-" + JavaCore.latestSupportedJavaVersion(); //$NON-NLS-1$
+ }
+
/**
* Returns whether the objects are equal, accounting for either one being
* null
.