From 74d92572f01967a5849befd59d22ac5f09e021c9 Mon Sep 17 00:00:00 2001 From: Hannes Wellmann Date: Wed, 12 Jul 2023 21:20:36 +0200 Subject: [PATCH] Enhance computation of system-package provided by a ExecutionEnvironment - Only consider EE of running VM in launch validation to match the behavior later at runtime - For the PDEState the EE's system-package is computed with the following distinction: - for a EE that corresponds to a Java-9 or higher release, get the 'best matching' VMInstall and query the system-packages for the EE's java release version (which is possible for modular JVMs) - for a Java-8 or lower release check if there is a VMInstall selected/strictly compatible to that version and query the system-packages from that VM (i.e. its rt.jar and other libraries) - if there is no VMInstall for a for a Java-8 or lower release, combine the hard-coded list of 'java.*' packages with the set of non-java packages provided by the default VMInstall of the workspace. The default VMInstall of the WS is at the same time the JVM selected in the active target definition (which sets its selection as WS default upon loading). - In general the available system-packages are only really known at runtime. Therefore at any other time, e.g. in the workspace one can only guess/assume what packages are available and the implemented algorithm seems to be the best trade-off between probable accuracy, convenience and the possibility to let the user control the result. - Listen to changes of available VMInstalls and ExecutionEnvironment defaults. Re-read them on changes and re-resolve the PDE-State again with a subsequent re-validation of all Plugin projects. Fixes https://github.com/eclipse-pde/eclipse.pde/issues/429 --- .../api/tools/internal/model/ApiBaseline.java | 12 +- .../pde/internal/build/site/PDEState.java | 208 ++++++++++++++++-- .../pde/internal/core/MinimalState.java | 84 ++++++- .../internal/core/TargetPlatformHelper.java | 73 +----- .../launcher/LaunchValidationOperation.java | 23 +- .../tests/ee/ExecutionEnvironmentTests.java | 2 +- .../target/TargetEnvironmentTestCase.java | 133 +++++++++-- 7 files changed, 404 insertions(+), 131 deletions(-) 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 45170bc9d2f..365ce428f74 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 @@ -290,13 +290,13 @@ public static Properties getJavaProfileProperties(String ee) { */ @SuppressWarnings("deprecation") private void initialize(Properties profile, ExecutionEnvironmentDescription description) throws CoreException { - String value = profile.getProperty(Constants.FRAMEWORK_SYSTEMPACKAGES); + // The pre-defined lists of system-packages are incomplete. + // Always overwrite, if we have a more up-to-date one. + String environmentId = description.getProperty(ExecutionEnvironmentDescription.CLASS_LIB_LEVEL); + IExecutionEnvironment ee = JavaRuntime.getExecutionEnvironmentsManager().getEnvironment(environmentId); + String value = TargetPlatformHelper.querySystemPackages(ee, profile); if (value == null) { - // In Java-10 and beyond, we query system-packages list from the JRE - String environmentId = description.getProperty(ExecutionEnvironmentDescription.CLASS_LIB_LEVEL); - IExecutionEnvironmentsManager manager = JavaRuntime.getExecutionEnvironmentsManager(); - IExecutionEnvironment environment = manager.getEnvironment(environmentId); - value = TargetPlatformHelper.querySystemPackages(environment); + value = profile.getProperty(Constants.FRAMEWORK_SYSTEMPACKAGES); } String[] systemPackages = null; if (value != null) { diff --git a/build/org.eclipse.pde.build/src/org/eclipse/pde/internal/build/site/PDEState.java b/build/org.eclipse.pde.build/src/org/eclipse/pde/internal/build/site/PDEState.java index ad4468b34f2..a30bbe82167 100644 --- a/build/org.eclipse.pde.build/src/org/eclipse/pde/internal/build/site/PDEState.java +++ b/build/org.eclipse.pde.build/src/org/eclipse/pde/internal/build/site/PDEState.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2021 IBM Corporation and others. + * Copyright (c) 2004, 2023 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -10,6 +10,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Hannes Wellmann - Enhance computation of system-package provided by a ExecutionEnvironment *******************************************************************************/ package org.eclipse.pde.internal.build.site; @@ -28,10 +29,13 @@ import java.util.Hashtable; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Properties; import java.util.Set; -import java.util.TreeSet; +import java.util.function.Function; import java.util.jar.JarFile; +import java.util.regex.Pattern; +import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -44,6 +48,7 @@ import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.JavaRuntime; +import org.eclipse.jdt.launching.LibraryLocation; import org.eclipse.jdt.launching.environments.IExecutionEnvironment; import org.eclipse.osgi.service.resolver.BundleDescription; import org.eclipse.osgi.service.resolver.ExportPackageDescription; @@ -388,7 +393,6 @@ public void resolveState() { // initialize profileManager and get the JRE profiles String[] javaProfiles = getJavaProfiles(); - String systemPackages = null; String ee = null; for (Config aConfig : configs) { @@ -435,7 +439,13 @@ public void resolveState() { if (profileProps != null) { String profileName = profileProps.getProperty(ProfileManager.PROFILE_NAME); if (AbstractScriptGenerator.getImmutableAntProperty(profileName) != null || (j == 0 && !added)) { - systemPackages = profileProps.getProperty(ProfileManager.SYSTEM_PACKAGES); + // The pre-defined lists of system-packages are incomplete. + // Always overwrite, if we have a more up-to-date one. + IExecutionEnvironment env = JavaRuntime.getExecutionEnvironmentsManager().getEnvironment(javaProfiles[j]); + String systemPackages = querySystemPackages(env, profileProps); + if (systemPackages == null) { + systemPackages = profileProps.getProperty(ProfileManager.SYSTEM_PACKAGES); + } ee = profileProps.getProperty(Constants.FRAMEWORK_EXECUTIONENVIRONMENT); prop = new Hashtable<>(); @@ -459,7 +469,7 @@ public void resolveState() { for (String execEnvID : eeJava10AndBeyond) { prop = new Hashtable<>(); IExecutionEnvironment env = JavaRuntime.getExecutionEnvironmentsManager().getEnvironment(execEnvID); - systemPackages = querySystemPackages(env); + String systemPackages = querySystemPackages(env, null); String currentEE = previousEE + "," + execEnvID; //$NON-NLS-1$ if (systemPackages == null) { previousEE = currentEE; @@ -480,15 +490,174 @@ public void resolveState() { } } - public static String querySystemPackages(IExecutionEnvironment environment) { - // Copy of org.eclipse.pde.internal.core.TargetPlatformHelper.querySystemPackages() - IVMInstall vm = bestVmInstallFor(environment); - if (vm == null || !JavaRuntime.isModularJava(vm)) { + private static final Pattern COMMA = Pattern.compile(","); //$NON-NLS-1$ + + public static String querySystemPackages(IExecutionEnvironment environment, Properties preJava9ProfileProperties) { + if (environment == null) { + return null; + } + String eeId = environment.getId(); + Integer releaseVersion = readJavaReleaseVersion(eeId); + if (releaseVersion == null) { return null; } - String release = environment.getProfileProperties().getProperty(JavaCore.COMPILER_COMPLIANCE); + Collection systemPackages; + if (releaseVersion <= 8) { + IVMInstall eeVM = bestVmInstallFor(environment, vms -> null); // Use selected VM or a perfect match + if (eeVM != null) { + // In case a VM is selected for an EE, query that VM and use its system-packages + systemPackages = querySystemPackages(eeVM, null); + } else { + // No VM selected for the non-modular EE: + // Compose list of available system-packages from the java.* packages in the predefined profiles in o.e.osgi respectively the hard-coded lists of java-packages in this class + // plus the non-java packages of this workspace's default VM. + // The reasoning for this is, that the OSGi standard only requires the java.* packages of an EE to be present, everything else is optional. + // Therefore the Workspaces default VM (which can also be set as target VM in the active target-definition) is the best guess for them. + // See also OSGi 8.0 specification chapter 3.4 Execution Environment + + List javaPackages = PRE_JAVA_9_SYSTEM_PACKAGES.get(eeId); + if (javaPackages == null) { + String profileSystemPackages = preJava9ProfileProperties.getProperty(Constants.FRAMEWORK_SYSTEMPACKAGES, ""); //$NON-NLS-1$ + if (profileSystemPackages.isBlank()) { + return null; + } + javaPackages = COMMA.splitAsStream(profileSystemPackages).filter(p -> p.startsWith("java.")).toList(); //$NON-NLS-1$ + } + IVMInstall targetVM = JavaRuntime.getDefaultVMInstall(); // Set by the Target-Definition if specified there + Collection targetVMSystemPackages = querySystemPackages(targetVM, null); + if (targetVMSystemPackages == null) { + return null; + } + Stream targetVMNonJavaPackages = targetVMSystemPackages.stream().filter(p -> !p.startsWith("java.")); //$NON-NLS-1$ + + systemPackages = Stream.concat(javaPackages.stream(), targetVMNonJavaPackages).sorted().toList(); + } + } else { + IVMInstall vm = bestVmInstallFor(environment, vms -> vms[0]); + if (vm == null) { + return null; + } + systemPackages = querySystemPackages(vm, environment); + } + return String.join(",", systemPackages); //$NON-NLS-1$ + } + + @SuppressWarnings("nls") + private static Integer readJavaReleaseVersion(String eeId) { + if (eeId.startsWith("JavaSE-")) { //$NON-NLS-1$ + try { + return Integer.parseInt(eeId.substring("JavaSE-".length())); //$NON-NLS-1$ + } catch (NumberFormatException e) { // Another EE + } + } + return switch (eeId) { + // There is no EE for Java 1.0 in OSGi + case "JRE-1.1" -> 1; + case "J2SE-1.2" -> 2; + case "J2SE-1.3" -> 3; + case "J2SE-1.4" -> 4; + case "J2SE-1.5" -> 5; + case "JavaSE-1.6" -> 6; + case "JavaSE-1.7" -> 7; + case "JavaSE-1.8" -> 8; + default -> null; + }; + } + + // Old JDKs can for example be obtained from https://www.oracle.com/java/technologies/downloads/archive/ + @SuppressWarnings("nls") + private static final Map> PRE_JAVA_9_SYSTEM_PACKAGES = Map.of(// + "JRE-1.1", List.of("java.applet", // + "java.awt", // + "java.awt.datatransfer", // + "java.awt.event", // + "java.awt.image", // + "java.awt.peer", // + "java.beans", // + "java.io", // + "java.lang", // + "java.lang.reflect", // + "java.math", // + "java.net", // + "java.rmi", // + "java.rmi.dgc", // + "java.rmi.registry", // + "java.rmi.server", // + "java.security", // + "java.security.acl", // + "java.security.interfaces", // + "java.sql", // + "java.text", // + "java.text.resources", // + "java.util", // + "java.util.zip"), + "J2SE-1.2", List.of("java.applet", // + "java.awt", // + "java.awt.color", // + "java.awt.datatransfer", // + "java.awt.dnd", // + "java.awt.dnd.peer", // + "java.awt.event", // + "java.awt.font", // + "java.awt.geom", // + "java.awt.im", // + "java.awt.image", // + "java.awt.image.renderable", // + "java.awt.peer", // + "java.awt.print", // + "java.awt.resources", // + "java.beans", // + "java.beans.beancontext", // + "java.io", // + "java.lang", // + "java.lang.ref", // + "java.lang.reflect", // + "java.math", // + "java.net", // + "java.rmi", // + "java.rmi.activation", // + "java.rmi.dgc", // + "java.rmi.registry", // + "java.rmi.server", // + "java.security", // + "java.security.acl", // + "java.security.cert", // + "java.security.interfaces", // + "java.security.spec", // + "java.sql", // + "java.text", // + "java.text.resources", // + "java.util", // + "java.util.jar", // + "java.util.zip")); + + /** + * Returns t + * @param vm the VM to query + * @param environment the release EE to use or null, if the VM's release should be used + */ + private static Collection querySystemPackages(IVMInstall vm, IExecutionEnvironment environment) { + if (!JavaRuntime.isModularJava(vm)) { + Collection packages = new HashSet<>(); + for (LibraryLocation libLocation : JavaRuntime.getLibraryLocations(vm)) { + IPath path = libLocation.getSystemLibraryPath(); + if (path != null) { + try (ZipFile zip = new ZipFile(path.toFile())) { + zip.stream().filter(e -> !e.isDirectory()).map(ZipEntry::getName).filter(n -> !n.startsWith("META-INF/")).map(n -> { //$NON-NLS-1$ + int i = n.lastIndexOf('/'); + return n.substring(0, i); + }).distinct().map(n -> n.replace('/', '.')).forEach(packages::add); + } catch (IOException e) { + ILog.get().error("Failed to read packages in JVM library for " + vm + ", at " + path, e); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + } + return packages; + } + + String release = environment != null ? environment.getProfileProperties().getProperty(JavaCore.COMPILER_COMPLIANCE) : null; try { - Collection packages = new TreeSet<>(); + Collection packages = new HashSet<>(); String jrtPath = "lib/" + org.eclipse.jdt.internal.compiler.util.JRTUtil.JRT_FS_JAR; //$NON-NLS-1$ String path = new File(vm.getInstallLocation(), jrtPath).toString(); // $NON-NLS-1$ var jrt = org.eclipse.jdt.internal.core.builder.ClasspathLocation.forJrtSystem(path, null, null, release); @@ -503,17 +672,14 @@ public static String querySystemPackages(IExecutionEnvironment environment) { } } } - return String.join(",", packages); //$NON-NLS-1$ + return packages; } catch (CoreException e) { - ILog.of(PDEState.class).log(Status.error("failed to read system packages for " + environment, e)); //$NON-NLS-1$ + ILog.of(PDEState.class).log(Status.error("Failed to read system packages for " + environment, e)); //$NON-NLS-1$ } return null; } - private static IVMInstall bestVmInstallFor(IExecutionEnvironment environment) { - if (environment == null) { - return null; - } + private static IVMInstall bestVmInstallFor(IExecutionEnvironment environment, Function nonStrictDefaultSelector) { IVMInstall defaultVM = environment.getDefaultVM(); if (defaultVM != null) { return defaultVM; @@ -522,12 +688,8 @@ private static IVMInstall bestVmInstallFor(IExecutionEnvironment environment) { if (compatible.length == 0) { return null; } - for (IVMInstall vm : compatible) { - if (environment.isStrictlyCompatible(vm)) { - return vm; - } - } - return compatible[0]; + Optional vm = Arrays.stream(compatible).filter(environment::isStrictlyCompatible).findFirst(); + return vm.isPresent() ? vm.get() : nonStrictDefaultSelector.apply(compatible); } public State getState() { diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/MinimalState.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/MinimalState.java index 296fe280405..01e4a91ca4d 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/MinimalState.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/MinimalState.java @@ -22,15 +22,26 @@ import java.util.List; import java.util.Map; +import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IncrementalProjectBuilder; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.resources.WorkspaceJob; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.launching.IVMInstall; +import org.eclipse.jdt.launching.IVMInstallChangedListener; import org.eclipse.jdt.launching.JavaRuntime; +import org.eclipse.jdt.launching.PropertyChangeEvent; import org.eclipse.jdt.launching.environments.IExecutionEnvironment; import org.eclipse.osgi.service.resolver.BundleDescription; import org.eclipse.osgi.service.resolver.State; @@ -242,6 +253,77 @@ protected boolean initializePlatformProperties() { return false; } + static { + // Listen to changes in the available VMInstalls and + // ExecutionEnvironment defaults + @SuppressWarnings("restriction") + String nodeQualifier = org.eclipse.jdt.internal.launching.LaunchingPlugin.ID_PLUGIN; + IEclipsePreferences launchingNode = InstanceScope.INSTANCE.getNode(nodeQualifier); + launchingNode.addPreferenceChangeListener(e -> { + if (e.getKey().equals("org.eclipse.jdt.launching.PREF_DEFAULT_ENVIRONMENTS_XML")) { //$NON-NLS-1$ + Object oldValue = e.getOldValue() == null ? "" : e.getOldValue(); //$NON-NLS-1$ + Object newValue = e.getNewValue() == null ? "" : e.getNewValue(); //$NON-NLS-1$ + if (!oldValue.equals(newValue)) { + rereadSystemPackagesAndReloadTP(); + } + } + }); + JavaRuntime.addVMInstallChangedListener(new IVMInstallChangedListener() { + @Override + public void vmRemoved(IVMInstall vm) { + rereadSystemPackagesAndReloadTP(); + } + + @Override + public void vmChanged(PropertyChangeEvent event) { + rereadSystemPackagesAndReloadTP(); + } + + @Override + public void vmAdded(IVMInstall vm) { + rereadSystemPackagesAndReloadTP(); + } + + @Override + public void defaultVMInstallChanged(IVMInstall previous, IVMInstall current) { + rereadSystemPackagesAndReloadTP(); + } + }); + } + private static final String PDE_MANIFEST_BUILDER = "org.eclipse.pde.ManifestBuilder"; //$NON-NLS-1$ + + private static void rereadSystemPackagesAndReloadTP() { + MinimalState state = PDECore.getDefault().getModelManager().getState(); + if (!state.fNoProfile) { + final String jobFamily = "pde.internal.ReresolveStateAfterVMorEEchanges"; //$NON-NLS-1$ + Job.getJobManager().cancel(jobFamily); + WorkspaceJob job = new WorkspaceJob("Re-resolve Target state after VM-Install or EE change") { //$NON-NLS-1$ + @Override + public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { + // The list of EEs has changed: re-read all available + // VM-installs/EEs and re-resolve state with new properties + state.fEEListChanged = true; + state.internalResolveState(true); + // Perform PDE-Manifest build, to re-validate all Manifests + for (IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects()) { + try { + project.build(IncrementalProjectBuilder.FULL_BUILD, PDE_MANIFEST_BUILDER, null, null); + } catch (CoreException e) { // ignore + } + } + return Status.OK_STATUS; + } + + @Override + public boolean belongsTo(Object family) { + return jobFamily.equals(family); + } + }; + job.setRule(ResourcesPlugin.getWorkspace().getRoot()); + job.schedule(200); // Small delay to bulk-handle multiple changes + } + } + public void removeBundleDescription(BundleDescription description) { if (description != null) { fState.removeBundle(description); @@ -269,7 +351,7 @@ private void setExecutionEnvironments() { if (!fNoProfile) { fExecutionEnvironments = knownExecutionEnviroments; } - fEEListChanged = true; // alway indicate the list has changed + fEEListChanged = true; // always indicate the list has changed } public void addBundleDescription(BundleDescription toAdd) { diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/TargetPlatformHelper.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/TargetPlatformHelper.java index 949274f5af4..d07fa1076e2 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/TargetPlatformHelper.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/TargetPlatformHelper.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2022 IBM Corporation and others. + * Copyright (c) 2000, 2023 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -22,7 +22,6 @@ import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Dictionary; import java.util.HashMap; import java.util.Hashtable; @@ -45,8 +44,6 @@ import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; -import org.eclipse.jdt.core.JavaCore; -import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jdt.launching.environments.IExecutionEnvironment; import org.eclipse.osgi.service.resolver.BundleDescription; @@ -354,14 +351,11 @@ public static Dictionary getTargetEnvironment(MinimalState state } @SuppressWarnings("unchecked") - public static Dictionary[] getPlatformProperties(String[] profiles, MinimalState state) { - if (profiles == null || profiles.length == 0) { - return new Dictionary[] { getTargetEnvironment(state) }; - } - + public static Dictionary[] getPlatformProperties(String[] profilesArr, MinimalState state) { + List profiles = profilesArr != null ? Arrays.asList(profilesArr) : List.of(); // add java profiles for those EE's that have a .profile file in the // current system bundle - List> result = new ArrayList<>(profiles.length); + List> result = new ArrayList<>(profiles.size()); for (String profile : profiles) { IExecutionEnvironment environment = JavaRuntime.getExecutionEnvironmentsManager().getEnvironment(profile); if (environment != null) { @@ -381,9 +375,12 @@ public static Dictionary[] getPlatformProperties(String[] profil public static void addEnvironmentProperties(Dictionary properties, IExecutionEnvironment environment, Properties profileProps) { - String systemPackages = profileProps.getProperty(Constants.FRAMEWORK_SYSTEMPACKAGES); - if (systemPackages == null) { // java 10 and beyond - systemPackages = querySystemPackages(environment); + // The pre-defined lists of system-packages are incomplete. + // Always overwrite, if we have a more up-to-date one. + String systemPackages = querySystemPackages(environment, profileProps); + if (systemPackages == null) { + // Unable to compute system-packages, probably OSGi specific EE + systemPackages = profileProps.getProperty(Constants.FRAMEWORK_SYSTEMPACKAGES); } if (systemPackages != null) { properties.put(Constants.FRAMEWORK_SYSTEMPACKAGES, systemPackages); @@ -396,54 +393,8 @@ public static void addEnvironmentProperties(Dictionary propertie } } - @SuppressWarnings("restriction") - public static String querySystemPackages(IExecutionEnvironment environment) { - IVMInstall vm = bestVmInstallFor(environment); - if (vm == null || !JavaRuntime.isModularJava(vm)) { - return null; - } - String release = environment.getProfileProperties().getProperty(JavaCore.COMPILER_COMPLIANCE); - try { - Collection packages = new TreeSet<>(); - String jrtPath = "lib/" + org.eclipse.jdt.internal.compiler.util.JRTUtil.JRT_FS_JAR; //$NON-NLS-1$ - String path = new File(vm.getInstallLocation(), jrtPath).toString(); // $NON-NLS-1$ - var jrt = org.eclipse.jdt.internal.core.builder.ClasspathLocation.forJrtSystem(path, null, null, release); - for (String moduleName : jrt.getModuleNames(null)) { - var module = jrt.getModule(moduleName); - if (module == null) { - continue; - } - for (var packageExport : module.exports()) { - if (!packageExport.isQualified()) { - packages.add(new String(packageExport.name())); - } - } - } - return String.join(",", packages); //$NON-NLS-1$ - } catch (CoreException e) { - PDECore.logException(e, "failed to read system packages for " + environment); //$NON-NLS-1$ - } - return null; - } - - private static IVMInstall bestVmInstallFor(IExecutionEnvironment environment) { - if (environment == null) { - return null; - } - IVMInstall defaultVM = environment.getDefaultVM(); - if (defaultVM != null) { - return defaultVM; - } - IVMInstall[] compatible = environment.getCompatibleVMs(); - if (compatible.length == 0) { - return null; - } - for (IVMInstall vm : compatible) { - if (environment.isStrictlyCompatible(vm)) { - return vm; - } - } - return compatible[0]; + public static String querySystemPackages(IExecutionEnvironment environment, Properties profileProperties) { + return org.eclipse.pde.internal.build.site.PDEState.querySystemPackages(environment, profileProperties); } public static String[] getKnownExecutionEnvironments() { diff --git a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/launcher/LaunchValidationOperation.java b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/launcher/LaunchValidationOperation.java index 58d6b1da974..bbf54f3c322 100644 --- a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/launcher/LaunchValidationOperation.java +++ b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/launcher/LaunchValidationOperation.java @@ -17,10 +17,8 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.util.ArrayList; import java.util.Arrays; import java.util.Dictionary; -import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; @@ -60,27 +58,22 @@ public void run(IProgressMonitor monitor) throws CoreException { @SuppressWarnings("unchecked") protected Dictionary[] getPlatformProperties() throws CoreException { IExecutionEnvironment[] envs = getMatchingEnvironments(); - if (envs.length == 0) { - return new Dictionary[] {TargetPlatformHelper.getTargetEnvironment()}; - } - // add java profiles for those EE's that have a .profile file in the current system bundle - List> result = new ArrayList<>(envs.length); - for (IExecutionEnvironment env : envs) { + Dictionary environmentProperties = TargetPlatformHelper.getTargetEnvironment(); + // Only add the highest EE of the supplied VM, to match the runtime's behavior + for (int i = envs.length - 1; i > 0; i--) { + IExecutionEnvironment env = envs[i]; + // add java profiles for those EE's that have a .profile file in the current system bundle Properties profileProps = getJavaProfileProperties(env.getId()); if (profileProps == null) { // Java10 onwards, we take profile via this method profileProps = env.getProfileProperties(); } if (profileProps != null) { - Dictionary props = TargetPlatformHelper.getTargetEnvironment(); - TargetPlatformHelper.addEnvironmentProperties(props, env, profileProps); - result.add(props); + TargetPlatformHelper.addEnvironmentProperties(environmentProperties, env, profileProps); + break; } } - if (!result.isEmpty()) { - return result.toArray(Dictionary[]::new); - } - return new Dictionary[] {TargetPlatformHelper.getTargetEnvironment()}; + return new Dictionary[] {environmentProperties}; } protected IExecutionEnvironment[] getMatchingEnvironments() throws CoreException { diff --git a/ui/org.eclipse.pde.ui.tests/src/org/eclipse/pde/ui/tests/ee/ExecutionEnvironmentTests.java b/ui/org.eclipse.pde.ui.tests/src/org/eclipse/pde/ui/tests/ee/ExecutionEnvironmentTests.java index 99bfba9e04c..594d011c027 100644 --- a/ui/org.eclipse.pde.ui.tests/src/org/eclipse/pde/ui/tests/ee/ExecutionEnvironmentTests.java +++ b/ui/org.eclipse.pde.ui.tests/src/org/eclipse/pde/ui/tests/ee/ExecutionEnvironmentTests.java @@ -324,7 +324,7 @@ public void testMinimumComplianceNoOverwrite() throws Exception { @Test public void testDynamicSystemPackages() throws Exception { IExecutionEnvironment env = JavaRuntime.getExecutionEnvironmentsManager().getEnvironment("JavaSE-11"); - String systemPackages = TargetPlatformHelper.querySystemPackages(env); + String systemPackages = TargetPlatformHelper.querySystemPackages(env, null); assertThat(systemPackages).isNotNull(); assertThat(systemPackages.split(",")).contains("java.lang", "javax.sql", "org.w3c.dom.css"); } diff --git a/ui/org.eclipse.pde.ui.tests/src/org/eclipse/pde/ui/tests/target/TargetEnvironmentTestCase.java b/ui/org.eclipse.pde.ui.tests/src/org/eclipse/pde/ui/tests/target/TargetEnvironmentTestCase.java index 52a57ae9a07..f7624b9a69a 100644 --- a/ui/org.eclipse.pde.ui.tests/src/org/eclipse/pde/ui/tests/target/TargetEnvironmentTestCase.java +++ b/ui/org.eclipse.pde.ui.tests/src/org/eclipse/pde/ui/tests/target/TargetEnvironmentTestCase.java @@ -14,21 +14,67 @@ package org.eclipse.pde.ui.tests.target; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import java.util.Arrays; import java.util.Dictionary; +import java.util.List; +import java.util.Map; import java.util.Properties; - +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IncrementalProjectBuilder; +import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.Platform; +import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jdt.launching.environments.IExecutionEnvironment; import org.eclipse.pde.core.plugin.TargetPlatform; import org.eclipse.pde.internal.core.TargetPlatformHelper; +import org.eclipse.pde.ui.tests.runtime.TestUtils; +import org.eclipse.pde.ui.tests.util.ProjectUtils; +import org.eclipse.pde.ui.tests.util.TargetPlatformUtil; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; import org.junit.Test; +import org.junit.rules.TestRule; import org.osgi.framework.Constants; public class TargetEnvironmentTestCase { + + private static final String JAVA_SE_1_7 = "JavaSE-1.7"; + + @ClassRule + public static final TestRule RESTORE_TARGET_DEFINITION = TargetPlatformUtil.RESTORE_CURRENT_TARGET_DEFINITION_AFTER; + @ClassRule + public static final TestRule CLEAR_WORKSPACE = ProjectUtils.DELETE_ALL_WORKSPACE_PROJECTS_BEFORE_AND_AFTER; + + @BeforeClass + public static void setupTargetPlatform() throws Exception { + TargetPlatformUtil.setRunningPlatformAsTarget(); + } + + private IExecutionEnvironment eeJava_1_7; + private IVMInstall eeJava_1_7DefaultVM; + + @Before + public void saveJava1_7EEDefault() { + eeJava_1_7 = JavaRuntime.getExecutionEnvironmentsManager().getEnvironment(JAVA_SE_1_7); + eeJava_1_7DefaultVM = eeJava_1_7.getDefaultVM(); + } + + @After + public void restoreJava1_7EEDefault() { + eeJava_1_7.setDefaultVM(eeJava_1_7DefaultVM); + } + @Test public void testOS() { assertEquals(Platform.getOS(), TargetPlatform.getOS()); @@ -82,13 +128,12 @@ public void testDictionaryNL() { @Test public void testResolveOptional() { Dictionary dictionary = TargetPlatformHelper.getTargetEnvironment(); - assertTrue("true".equals(dictionary.get("osgi.resolveOptional"))); + assertEquals("true", dictionary.get("osgi.resolveOptional")); } /** * Tests that the OSGi state for the PDE models has the correct properties set, based on known execution environments */ - @SuppressWarnings("deprecation") @Test public void testStateEEProperties() { Dictionary[] platformProps = TargetPlatformHelper.getState().getPlatformProperties(); @@ -100,29 +145,22 @@ public void testStateEEProperties() { Properties profileProps = environment.getProfileProperties(); if (profileProps != null) { // If we have profile properties for an execution environment, ensure that they were added to the state - String systemPackages = profileProps.getProperty(Constants.FRAMEWORK_SYSTEMPACKAGES); - if (systemPackages != null){ - boolean foundSystemPackage = false; - for (Dictionary platformProp : platformProps) { - if (systemPackages.equals(platformProp.get(Constants.FRAMEWORK_SYSTEMPACKAGES))){ - foundSystemPackage = true; - break; - } - } - if (!foundSystemPackage){ + Set profileSystemPackages = getSystemPackages(profileProps) + .filter(p -> p.startsWith("java.")).collect(Collectors.toSet()); + if (!profileSystemPackages.isEmpty()) { + String profileEE = getExecutionenvironment(profileProps); + boolean foundSystemPackage = Arrays.stream(platformProps) + .filter(pp -> profileEE.equals(getExecutionenvironment(pp))).anyMatch(pp -> { + Set packages = getSystemPackages(pp).collect(Collectors.toSet()); + return packages.containsAll(profileSystemPackages); + }); + if (!foundSystemPackage) { fail("The system packages property for EE " + profile + " was not found in the state's propeties"); } } - String ee = profileProps.getProperty(Constants.FRAMEWORK_EXECUTIONENVIRONMENT); - if (ee != null){ - boolean foundEE = false; - for (Dictionary platformProp : platformProps) { - if (ee.equals(platformProp.get(Constants.FRAMEWORK_EXECUTIONENVIRONMENT))){ - foundEE = true; - break; - } - } - if (!foundEE){ + String ee = getExecutionenvironment(profileProps); + if (ee != null) { + if (Stream.of(platformProps).map(pp -> getExecutionenvironment(pp)).noneMatch(ee::equals)) { fail("The framework EE property for EE " + profile + " was not found in the state's propeties"); } } @@ -131,4 +169,51 @@ public void testStateEEProperties() { } } + @SuppressWarnings("deprecation") + private String getExecutionenvironment(Object profile) { + return (String) ((Map) profile).get(Constants.FRAMEWORK_EXECUTIONENVIRONMENT); + } + + private static Stream getSystemPackages(Object profile) { + String platformPackages = (String) ((Map) profile).get(Constants.FRAMEWORK_SYSTEMPACKAGES); + if (platformPackages == null) { + return Stream.empty(); + } + return Arrays.stream(platformPackages.split(",")); + } + + @Test + public void testProjectWithEEImports() throws CoreException { + // A Java-1.7 VM does not provide the java.util.function package + // introduced in 1.8. But if we say that a Java 1.8+ VM the default + // (which the one running this test and thus the workspaces default-VM + // should be), well then the system-packages of that Java 1.8+ VM should + // be used in the PDEState and thus there should not be errors. This + // might not be a good idea in production, but is a good test-case + IProject project = ProjectUtils.createPluginProject("foo.bar", "foo.bar", "1.0.0.qualifier", (d, s) -> { + d.setExecutionEnvironments(new String[] { JAVA_SE_1_7 }); + d.setHeader(Constants.IMPORT_PACKAGE, "java.util.function"); + d.setHeader("Automatic-Module-Name", "foo.bar"); + }); + project.build(IncrementalProjectBuilder.FULL_BUILD, null); + List errorsWithoutEEDefault = findErrorMarkers(project); + assertEquals(errorsWithoutEEDefault.toString(), 1, errorsWithoutEEDefault.size()); + + eeJava_1_7.setDefaultVM(JavaRuntime.getDefaultVMInstall()); + + // await at least VMInstall/EE change delay in + // MinimalState.rereadSystemPackagesAndReloadTP() + TestUtils.waitForJobs("testProjectWithEEImports", 300, 10_000); + + List errorsWithEEDefault = findErrorMarkers(project); + assertEquals(List.of(), errorsWithEEDefault); + } + + private List findErrorMarkers(IProject project) throws CoreException { + IMarker[] markers = project.findMarkers(null, true, IResource.DEPTH_INFINITE); + List errorsWithoutEEDefault = Arrays.stream(markers) + .filter(m -> m.getAttribute(IMarker.SEVERITY, -1) == IMarker.SEVERITY_ERROR).toList(); + return errorsWithoutEEDefault; + } + }