Skip to content

Commit

Permalink
[FIXUP] attempt to handle ClassRealm import problems
Browse files Browse the repository at this point in the history
  • Loading branch information
HannesWell committed Sep 18, 2022
1 parent be20a4e commit 112e80f
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.Set;
Expand All @@ -53,6 +55,7 @@
import org.osgi.framework.Constants;
import org.osgi.framework.connect.ConnectFrameworkFactory;
import org.osgi.framework.launch.Framework;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.service.component.runtime.ServiceComponentRuntime;
import org.osgi.service.component.runtime.dto.ComponentConfigurationDTO;
import org.osgi.service.component.runtime.dto.ComponentDescriptionDTO;
Expand Down Expand Up @@ -136,29 +139,48 @@ synchronized PlexusConnectFramework getFramework(ClassRealm realm) throws Bundle
p.put(Constants.FRAMEWORK_BEGINNING_STARTLEVEL, "6");
p.put("osgi.instance.area", storagePath + File.separator + "instance");

ServiceLoader<ConnectFrameworkFactory> sl = ServiceLoader.load(ConnectFrameworkFactory.class,
getClass().getClassLoader());
ConnectFrameworkFactory factory = sl.findFirst().orElseThrow();
Class<? extends PlexusFrameworkConnectServiceFactory> thisClass = getClass();
System.out.println("thisClass is " + thisClass + " id=" + System.identityHashCode(thisClass));
ClassLoader thisClassLoader = thisClass.getClassLoader();
System.out.println("thisClassLoader is " + thisClassLoader + " id=" + System.identityHashCode(thisClassLoader));

ConnectFrameworkFactory factory = loadServiceFromRealm(ConnectFrameworkFactory.class, realm);
// ConnectFrameworkFactory factory = new EquinoxFactory();
Class<? extends ConnectFrameworkFactory> factoryClass = factory.getClass();

System.out.println("factoryClass is " + factoryClass + " id=" + System.identityHashCode(factoryClass));
ClassLoader factoryClassLoader = factoryClass.getClassLoader();
System.out.println(
"factoryClassLoader" + factoryClassLoader + " id=" + System.identityHashCode(factoryClassLoader));

PlexusModuleConnector connector = new PlexusModuleConnector(factory);
Framework osgiFramework = factory.newFramework(p, connector);

Class<? extends Framework> frameworkClass = osgiFramework.getClass();
System.out.println("frameworkClass is " + frameworkClass + " id=" + System.identityHashCode(frameworkClass));
ClassLoader frameworksClassLoader = frameworkClass.getClassLoader();
System.out.println("frameworkClassLoader is " + frameworksClassLoader + " id="
+ System.identityHashCode(frameworksClassLoader));

ClassLoader frameworkWiringClassloader = osgiFramework.adapt(BundleWiring.class).getClassLoader();
System.out.println("frameworkWiringClassloader is " + frameworkWiringClassloader + " id="
+ System.identityHashCode(frameworkWiringClassloader));

PlexusConnectFramework connectFramework = new PlexusConnectFramework(osgiFramework, log, this, realm, false);
PlexusFrameworkUtilHelper.registerHelper(connectFramework);
osgiFramework.init(connectFramework);
frameworkMap.put(realm, connectFramework);

BundleContext bundleContext = osgiFramework.getBundleContext();
connectFramework.start(bundleContext);

Bundle[] bundles1 = bundleContext.getBundles();
System.out.println("Bundle1");
Arrays.stream(bundles1).map(Bundle::getLocation).forEach(System.out::println);
connectFramework.start(bundleContext);

for (ClassRealm r : realms) {
connector.installRealm(r, bundleContext, connectFramework);
}
System.out.println("Bundle2");
Bundle[] bundles2 = bundleContext.getBundles();
Arrays.stream(bundles2).map(Bundle::getLocation).forEach(System.out::println);

// FIXME: make this better by classloader separation?
// RegistryProviderFactory.releaseDefault();

osgiFramework.start();

Expand All @@ -171,9 +193,80 @@ synchronized PlexusConnectFramework getFramework(ClassRealm realm) throws Bundle
printFrameworkState(osgiFramework, connectFramework);
}
return connectFramework;
}

private static <T> T loadServiceFromRealm(Class<T> service, ClassRealm realm) {
ClassLoader clLoader = realm.getImportRealms().isEmpty() ? realm
: new ClassRealm(realm.getWorld(), "isolated-Framework-loader [" + realm.getId() + "]",
realm.getParent()) {

@Override
public ClassLoader getImportClassLoader(String name) {
return null;
}

@Override
public Collection<ClassRealm> getImportRealms() {
return List.of();
}

@Override
public Class<?> loadClassFromSelf(String name) {
return realm.loadClassFromSelf(name);
}

@Override
public URL loadResourceFromSelf(String name) {
return realm.loadResourceFromSelf(name);
}

@Override
public Enumeration<URL> loadResourcesFromSelf(String name) {
return realm.loadResourcesFromSelf(name);
}
};

ServiceLoader<T> loader = ServiceLoader.load(service, clLoader);
Optional<T> findFirst = loader.findFirst();
T instance = findFirst.orElseThrow();
if (instance.getClass().getClassLoader() == realm) {
return instance;
}

// TODO: the problem is that a Realm first searches the imported realms for
// classes and therefore always uses the maven-tycho-core classloader.
// This has the effect that some classes are shared between the OSGi-framework instances and are therefore not freshly initiallized with each framework and therefore share the state.
// It is not sufficient to load the service explicitly from self because within
// the framework when classloader.loadClass(String) is called it looks for
// cached classes and first loads from imported realms.
// Maybe just create a new URL-Classloader that has the classpath of the current
// realm?
// try {
// Enumeration<URL> resources = realm.getResources("META-INF/services/" + service.getName());
// List<URL> list = Collections.list(resources);
// for (URL url : list) {
// try (InputStream in = url.openStream(); var reader = new BufferedReader(new InputStreamReader(in));) {
// List<String> names = reader.lines().collect(Collectors.toList());
// for (String name : names) {
// @SuppressWarnings("unchecked")
// Class<T> cl = (Class<T>) realm.loadClassFromSelf(name);
// if (cl != null && service.isAssignableFrom(cl)) {
// try {
// return cl.getConstructor().newInstance();
// } catch (ReflectiveOperationException e) { // ignore
// }
// }
// }
// }
// }
// } catch (IOException e) {
// throw new IllegalStateException("Failed to load service: " + service, e);
// }
throw new NoSuchElementException("No service found: " + service);
}

// FIXME: check if connect.bundles has to be cleared?!

private void printRealm(ClassRealm realm, int indent, Set<ClassRealm> printed) {
if (printed.add(realm)) {
ClassRealm parentRealm = realm.getParentRealm();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ public synchronized void installRealm(ClassRealm realm, BundleContext bundleCont
File file = getFile(url);
if (file == null) {
logger.debug("Can't convert url " + url + " to file!");
// TODO: why not support directory based bundles? In a Maven-world they are
// usually not extracted. But what about bundles with BundleShape: dir? They
// usually relay on being extracted? Or does the runtime handle this?
continue;
}
try {
Expand Down
27 changes: 13 additions & 14 deletions tycho-apitools-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
<name>Plugin for performing API analysis tasks</name>
<packaging>maven-plugin</packaging>
<dependencies>
<dependency>
<groupId>org.eclipse.tycho</groupId>
<artifactId>sisu-osgi-connect</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Maven -->
<dependency>
<groupId>org.apache.maven</groupId>
Expand All @@ -23,27 +28,21 @@
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
</dependency>
<!-- Tycho -->
<dependency>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.tycho</groupId>
<artifactId>sisu-osgi-connect</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.tycho.extras</groupId>
<artifactId>tycho-dependency-tools-plugin</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-utils</artifactId>
</dependency>
<!-- PDE -->
<dependency>
<groupId>org.eclipse.pde</groupId>
<artifactId>org.eclipse.pde.api.tools</artifactId>
<version>1.2.1000</version>
<type>eclipse-plugin</type>
<exclusions>
<exclusion>
<groupId>javax.annotation</groupId>
Expand All @@ -52,9 +51,9 @@
</exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.osgi.compatibility.state</artifactId>
<version>1.2.700</version>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.logging.Logger;
import org.eclipse.core.runtime.CoreException;
Expand Down Expand Up @@ -96,6 +95,18 @@ public void execute() throws MojoExecutionException, MojoFailureException {
return;
}
if (supportedPackagingTypes.contains(project.getPackaging())) {

// ClassLoader classLoader = ApiAnalysisMojo.class.getClassLoader();
// if (classLoader instanceof ClassRealm) {
// try {
// Field foreignImports = ClassRealm.class.getDeclaredField("foreignImports");
// foreignImports.trySetAccessible();
// foreignImports.set(classLoader, new TreeSet<>());
// } catch (ReflectiveOperationException e) {
// throw new IllegalStateException(e);
// }
// }

File baselineFile2;
try {
baselineFile2 = getBaselineFile();
Expand Down

0 comments on commit 112e80f

Please sign in to comment.