Goal: see if Launchpad JUnit tests can be run out of one of our projects.
My test class:
package *REDACTED*;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.osgi.framework.BundleContext;
import aQute.launchpad.Launchpad;
import aQute.launchpad.LaunchpadBuilder;
import aQute.launchpad.Service;
class LaunchpadInClasspathModeTest {
static final String EQUINOX_FRAMEWORK = "org.eclipse.osgi;version='[3.18,3.19)'";
LaunchpadBuilder builder;
@Service
BundleContext context;
@BeforeEach
void before() {
builder = new LaunchpadBuilder();
}
@Test
void launchpadWorksInClasspathMode() throws Exception {
try (Launchpad launchpad = builder.debug()
.runfw(EQUINOX_FRAMEWORK)
.create()) {
assertTrue(launchpad.check());
}
}
}
Problem 1:
A -buildpath
-included signed bundle -let’s call it platform.bundle1
exports its locally included org.slf4j:slf4j-api:2.0.13
-packages all in version 1.7.36.
When aQute.launchpad.LaunchpadBuilder
initializes its aQute.bnd.remoteworkspace.client.RemoteWorkspaceClientFactory
, which in turn initializes its org.slf4j.Logger
, the ClassLoader can’t find org.slf4j.spi.SLF4JServiceProvider
:
// I lost the first line copy-pasting, so it isn't complete
java.lang.NoClassDefError
at aQute.bnd.remoteworkspace.client.RemoteWorkspaceClientFactory.<clinit>(RemoteWorkspaceClientFactory.java:28)
at aQute.launchpad.LaunchpadBuilder.<clinit>(LaunchpadBuilder.java:51)
at *REDACTED*.LaunchpadInClasspathModeTest.before(LaunchpadInClasspathModeTest.java:22)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
Caused by: java.lang.ClassNotFoundException: org.slf4j.spi.SLF4JServiceProvider
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525)
... 6 more
Once I include org.slf4j.simple
in -testpath
and resolve it via -runrequires
for the -runbundles
, via org.slf4j.simple
’s Require-Bundle: slf4j.api
, I have to add org.slf4j.api
and once I add both in a higher version than 1.7.36, trying to run my test, the following Exception is thrown:
java.lang.ExceptionInInitializerError
at aQute.bnd.remoteworkspace.client.RemoteWorkspaceClientFactory.<clinit>(RemoteWorkspaceClientFactory.java:28)
at aQute.launchpad.LaunchpadBuilder.<clinit>(LaunchpadBuilder.java:51)
at *REDACTED*.LaunchpadInClasspathModeTest.before(LaunchpadInClasspathModeTest.java:22)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
Caused by: java.lang.SecurityException: class "org.slf4j.ILoggerFactory"'s signer information does not match signer information of other classes in the same package
at java.base/java.lang.ClassLoader.checkCerts(ClassLoader.java:1163)
at java.base/java.lang.ClassLoader.preDefineClass(ClassLoader.java:907)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1015)
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150)
at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:862)
at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:760)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525)
at org.slf4j.LoggerFactory.<clinit>(LoggerFactory.java:95)
... 6 more
Now, after removing the signature keys and entries in platform.bundle1
’s Manifest, my JUnit Console Output reads:
SLF4J: Class path contains multiple SLF4J providers.
SLF4J: Found provider [platform.bundle1.adapter.Slf4jProvider@21baa903]
SLF4J: Found provider [org.slf4j.simple.SimpleServiceProvider@607fbe09]
SLF4J: See https://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual provider is of type [platform.bundle1.adapter.Slf4jProvider@21baa903]
Now, for fixing the next two Exceptions after starting my test, I have to include org.osgi.util.tracker
and org.osgi.resource
in my -testpath
, after that, Problem 2 starts.
Problem 2:
A -buildpath
-included signed bundle -let’s call it platform.bundle2
exports org.osgi.service.repository;version="1.0.0"
and I don’t know why, but all other classes typically provided by the system bundle get loaded from org.eclipse.osgi
, yet this one suddenly gets provided by platform.bundle2
and the following Exception is thrown:
java.lang.NoSuchMethodError: 'void org.osgi.service.resolver.ResolveContext.onCancel(java.lang.Runnable)'
at org.apache.felix.resolver.ResolverImpl$ResolveSession.createSession(ResolverImpl.java:99)
at org.apache.felix.resolver.ResolverImpl.resolve(ResolverImpl.java:419)
at org.apache.felix.resolver.ResolverImpl.resolve(ResolverImpl.java:374)
at org.eclipse.osgi.container.ModuleResolver$ResolveProcess.resolveRevisions(ModuleResolver.java:1027)
at org.eclipse.osgi.container.ModuleResolver$ResolveProcess.resolveRevisionsInBatch(ModuleResolver.java:981)
at org.eclipse.osgi.container.ModuleResolver$ResolveProcess.resolve(ModuleResolver.java:898)
at org.eclipse.osgi.container.ModuleResolver.resolveDelta(ModuleResolver.java:176)
at org.eclipse.osgi.container.ModuleContainer.resolveAndApply(ModuleContainer.java:553)
at org.eclipse.osgi.container.ModuleContainer.resolve(ModuleContainer.java:503)
at org.eclipse.osgi.container.ModuleContainer.resolve(ModuleContainer.java:492)
at org.eclipse.osgi.storage.Storage.checkSystemBundle(Storage.java:417)
at org.eclipse.osgi.storage.Storage.createStorage(Storage.java:187)
at org.eclipse.osgi.internal.framework.EquinoxContainer.<init>(EquinoxContainer.java:108)
at org.eclipse.osgi.launch.Equinox.<init>(Equinox.java:53)
at org.eclipse.osgi.launch.EquinoxFactory.newFramework(EquinoxFactory.java:35)
at org.eclipse.osgi.launch.EquinoxFactory.newFramework(EquinoxFactory.java:30)
at aQute.launchpad.LaunchpadBuilder.getFramework(LaunchpadBuilder.java:364)
at aQute.launchpad.LaunchpadBuilder.create(LaunchpadBuilder.java:284)
at aQute.launchpad.LaunchpadBuilder.create(LaunchpadBuilder.java:248)
at *REDACTED*.LaunchpadInClasspathModeTest.launchpadWorksInClasspathMode(LaunchpadInClasspathModeTest.java:29)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)