-
Notifications
You must be signed in to change notification settings - Fork 121
Limit access to junit platform engines #2071
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| /******************************************************************************* | ||
| * Copyright (c) 2025 Christoph Läubrich and others. | ||
| * This program and the accompanying materials | ||
| * are made available under the terms of the Eclipse Public License 2.0 | ||
| * which accompanies this distribution, and is available at | ||
| * https://www.eclipse.org/legal/epl-2.0/ | ||
| * | ||
| * SPDX-License-Identifier: EPL-2.0 | ||
| * | ||
| * Contributors: | ||
| * Christoph Läubrich - initial API and implementation | ||
| * | ||
| */ | ||
| package org.eclipse.pde.internal.junit.runtime; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import org.eclipse.core.runtime.Platform; | ||
| import org.osgi.framework.Bundle; | ||
| import org.osgi.framework.FrameworkUtil; | ||
| import org.osgi.framework.wiring.BundleCapability; | ||
| import org.osgi.framework.wiring.BundleWiring; | ||
|
|
||
| /** | ||
| * Default implementation used with Java 1.8 | ||
| * TODO provide MR variant using stack walker, currently blocked by JDT bug ... | ||
|
Comment on lines
+25
to
+26
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was about to suggest to use StackWalker. Until I was reminded that this bundle is still at Java-8 and until I read this comment.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, sadly I was hit by this bug now but once this is merged it should work, and would be good if this feature is actually used in platform somewhere, but this is here really not high throughput method I just wanted to make it work first: |
||
| */ | ||
| public class Caller { | ||
| private static final String JUNIT_PLATFORM_LAUNCHER = "org.junit.platform.launcher"; //$NON-NLS-1$ | ||
| private static final Bundle BUNDLE = FrameworkUtil.getBundle(Caller.class); | ||
| private static final Bundle loaderBundle; | ||
|
|
||
| static { | ||
| Bundle junit5RuntimeBundle = Platform.getBundle("org.eclipse.jdt.junit5.runtime"); //$NON-NLS-1$ | ||
| if (junit5RuntimeBundle == null) { | ||
| Bundle junit4RuntimeBundle = Platform.getBundle("org.eclipse.jdt.junit4.runtime"); //$NON-NLS-1$ | ||
| loaderBundle = findJUnit5LauncherByRuntime(junit4RuntimeBundle); | ||
| } else { | ||
| loaderBundle = junit5RuntimeBundle; | ||
| } | ||
| } | ||
|
|
||
| protected static Bundle findJUnit5LauncherByRuntime(Bundle junit4RuntimeBundle) { | ||
| if (junit4RuntimeBundle == null) { | ||
| return BUNDLE; | ||
| } | ||
| for (Bundle bundle : BUNDLE.getBundleContext().getBundles()) { | ||
| BundleWiring bundleWiring = bundle.adapt(BundleWiring.class); | ||
| List<BundleCapability> capabilities = bundleWiring.getCapabilities(JUNIT_PLATFORM_LAUNCHER); | ||
| if (!capabilities.isEmpty() && bundle.getVersion().getMajor() < 6) { | ||
| return bundle; | ||
| } | ||
| } | ||
|
|
||
| return BUNDLE; | ||
| } | ||
|
|
||
| static Bundle getBundle() { | ||
| StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); | ||
| for (StackTraceElement element : stackTraceElements) { | ||
| try { | ||
| String className = element.getClassName(); | ||
| Class<?> clz = loaderBundle.loadClass(className); | ||
| Bundle bundle = FrameworkUtil.getBundle(clz); | ||
| if (bundle == BUNDLE) { | ||
| continue; | ||
| } | ||
| if (bundle != null) { | ||
| return bundle; | ||
| } | ||
| } catch (ClassNotFoundException e) { | ||
| continue; | ||
| } | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| } | ||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| /******************************************************************************* | ||
| * Copyright (c) 2025 Christoph Läubrich and others. | ||
| * This program and the accompanying materials | ||
| * are made available under the terms of the Eclipse Public License 2.0 | ||
| * which accompanies this distribution, and is available at | ||
| * https://www.eclipse.org/legal/epl-2.0/ | ||
| * | ||
| * SPDX-License-Identifier: EPL-2.0 | ||
| * | ||
| * Contributors: | ||
| * Christoph Läubrich - initial API and implementation | ||
| * | ||
| */ | ||
| package org.eclipse.pde.internal.junit.runtime; | ||
|
|
||
| import java.io.IOException; | ||
| import java.net.URL; | ||
| import java.util.ArrayList; | ||
| import java.util.Collection; | ||
| import java.util.Collections; | ||
| import java.util.Enumeration; | ||
| import java.util.Iterator; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.concurrent.ConcurrentHashMap; | ||
|
|
||
| import org.eclipse.core.runtime.FileLocator; | ||
| import org.osgi.framework.Bundle; | ||
|
|
||
| /** | ||
| * The classloader wraps the OSGi provided one but gives access for the JUnit | ||
| * runer to any SPI declared services. | ||
| */ | ||
| class SPIBundleClassLoader extends ClassLoader { | ||
|
|
||
| private static final String META_INF_SERVICES = "META-INF/services/"; //$NON-NLS-1$ | ||
| private List<Bundle> bundles; | ||
| private Map<String, List<SPIMapping>> mappings = new ConcurrentHashMap<>(); | ||
|
|
||
| SPIBundleClassLoader(List<Bundle> bundles) { | ||
| super(null); | ||
| this.bundles = bundles; | ||
| } | ||
|
|
||
| @Override | ||
| protected Class<?> findClass(String name) throws ClassNotFoundException { | ||
| Iterator<SPIMapping> spi = mappings.values().stream().flatMap(Collection::stream).filter(mapping -> mapping.hasService(name)).iterator(); | ||
| if (spi.hasNext()) { | ||
| Bundle caller = Caller.getBundle(); | ||
| while (spi.hasNext()) { | ||
| SPIMapping mapping = spi.next(); | ||
| if (mapping.isCompatible(caller)) { | ||
| return mapping.loadImplementation(name); | ||
| } | ||
| } | ||
| throw new ClassNotFoundException(name); | ||
| } | ||
| for (Bundle bundle : bundles) { | ||
| try { | ||
| Class<?> c = bundle.loadClass(name); | ||
| if (c != null) { | ||
| return c; | ||
| } | ||
| } catch (ClassNotFoundException e) { | ||
| } | ||
| } | ||
| throw new ClassNotFoundException(name); | ||
| } | ||
|
|
||
| @Override | ||
| protected URL findResource(String name) { | ||
| try { | ||
| Enumeration<URL> resources = findResources(name); | ||
| if (resources.hasMoreElements()) { | ||
| return resources.nextElement(); | ||
| } | ||
| } catch (IOException e) { | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| @Override | ||
| protected Enumeration<URL> findResources(String name) throws IOException { | ||
| List<URL> result = new ArrayList<>(); | ||
| if (name.startsWith(META_INF_SERVICES)) { | ||
| String serviceName = name.substring(META_INF_SERVICES.length()); | ||
| List<SPIMapping> spis = mappings.computeIfAbsent(name, spi -> { | ||
| List<SPIMapping> list = new ArrayList<>(); | ||
| for (Bundle other : bundles) { | ||
| URL entry = other.getEntry(name); | ||
| if (entry != null) { | ||
| try { | ||
| list.add(new SPIMapping(other.loadClass(serviceName), other, entry)); | ||
| } catch (ClassNotFoundException e) { | ||
| // should not happen | ||
| } | ||
| } | ||
| } | ||
| return list; | ||
| }); | ||
| Bundle caller = Caller.getBundle(); | ||
| for (SPIMapping mapping : spis) { | ||
| if (mapping.isCompatible(caller)) { | ||
| result.add(mapping.getUrl()); | ||
| } | ||
| } | ||
| return Collections.enumeration(result); | ||
| } | ||
| for (Bundle bundle : bundles) { | ||
| Enumeration<URL> resources = bundle.getResources(name); | ||
| while (resources != null && resources.hasMoreElements()) { | ||
| result.add(FileLocator.resolve(resources.nextElement())); | ||
| } | ||
| } | ||
| return Collections.enumeration(result); | ||
| } | ||
|
|
||
| @Override | ||
| public String toString() { | ||
| return "SPIBundleClassLoader for bundles " + bundles; //$NON-NLS-1$ | ||
| } | ||
|
|
||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have to admit that I didn't knew that dynamically imported packages can have a version(range) too.
In general I wonder if we should better convert this to an optional package-import?
IIRC dynamic imports still create wires at runtime, but only on demand/dynamically. But as a test-runtime usually doesn't change I'm think if an ordinary (optional) import would be fine.
Rereading my comment from #1047 (comment), I think there was no greater meaning in it.
And we already have an optional requirement on
org.eclipse.ui.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The difference between an optional and a dynamic import is that the optional one is resolved at the time the bundle is resolved, and the dynamic one is resolved at the time you try to access that package (+dynamic supports wildcards)