Skip to content
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

adopt Atomos #36

Merged
merged 48 commits into from
Jan 5, 2023
Merged

adopt Atomos #36

merged 48 commits into from
Jan 5, 2023

Conversation

nedtwigg
Copy link
Collaborator

Following up on the suggestions from #33 (thanks in particular to @tjwatson for the Atomos link).

If you run IdeMainTest with useSolstice=true, you'll get a running Eclipse JDT but with UIEventTopic broken.

If you set useSolstice=false, you'll get:

this output
b jdk.xml.dom
b java.security.jgss
b jdk.jconsole
b jdk.jfr
b jdk.jdi
b java.rmi
b java.management
b jdk.nio.mapmode
b jdk.javadoc
b jdk.jlink
b java.xml
b java.sql
b java.desktop
b jdk.management.jfr
b java.transaction.xa
b jdk.sctp
b java.datatransfer
b java.smartcardio
b java.base
b jdk.jpackage
b jdk.jshell
b jdk.charsets
b java.xml.crypto
b jdk.jdwp.agent
b jdk.jdeps
b jdk.net
b jdk.naming.dns
b jdk.internal.le
b jdk.security.auth
b java.security.sasl
b jdk.management.agent
b jdk.attach
b java.compiler
b jdk.internal.opt
b jdk.compiler
b java.prefs
b java.net.http
b jdk.localedata
b java.scripting
b jdk.zipfs
b java.logging
b jdk.internal.ed
b java.naming
b jdk.httpserver
b java.sql.rowset
b jdk.dynalink
b jdk.jstatd
b java.management.rmi
b jdk.naming.rmi
b jdk.jartool
b jdk.management
b jdk.crypto.ec
b java.instrument
b jdk.crypto.cryptoki
b jdk.internal.jvmstat
b jdk.editpad
b jdk.security.jgss
b jdk.jsobject
b jdk.random
b jdk.accessibility
b jdk.unsupported.desktop
b jdk.unsupported
b org.slf4j.simple
b org.eclipse.jetty.servlet
b org.eclipse.jetty.security
b org.eclipse.jetty.server
b org.eclipse.jetty.http
b org.eclipse.jetty.io
b org.eclipse.jetty.util.ajax
b org.eclipse.jetty.util
b org.slf4j.api
b org.eclipse.ant.ui
b org.eclipse.ant.launching
b org.eclipse.ant.core
b org.eclipse.jdt.junit
b org.eclipse.jdt.apt.ui
b org.eclipse.jdt.debug.ui
b org.eclipse.jdt.ui
b org.eclipse.text.quicksearch
b org.eclipse.search
b org.eclipse.ui.ide.application
b org.eclipse.ui.navigator.resources
b org.eclipse.ltk.ui.refactoring
b org.eclipse.team.ui
b org.eclipse.compare
b org.eclipse.team.core
b org.eclipse.debug.ui.launchview
b org.eclipse.ui.externaltools
b org.eclipse.debug.ui
b org.eclipse.team.genericeditor.diff.extension
b org.eclipse.ui.console
b org.eclipse.ui.genericeditor
b org.eclipse.ui.editors
b org.eclipse.ui.workbench.texteditor
b org.eclipse.compare.core
b org.eclipse.core.externaltools
b org.eclipse.jdt.core.manipulation
b org.eclipse.jdt.junit.core
b org.eclipse.jdt.launching
b org.eclipse.jdt.debug
b org.eclipse.debug.core
b org.eclipse.jdt.apt.pluggable.core
b org.eclipse.jdt.apt.core
b org.eclipse.jdt.compiler.apt
  problem: Invalid operation on a fragment. osgi.identity; osgi.identity="org.eclipse.jdt.compiler.apt"; type="osgi.fragment"; version:Version="1.4.200.v20220802-0458"; tags:List<String>="osgi.connect"; singleton:="true" [id=104]
b org.eclipse.jdt.compiler.tool
  problem: Invalid operation on a fragment. osgi.identity; osgi.identity="org.eclipse.jdt.compiler.tool"; type="osgi.fragment"; version:Version="1.3.200.v20220802-0458"; tags:List<String>="osgi.connect"; singleton:="true" [id=105]
b org.eclipse.jdt.core.formatterapp
b org.eclipse.jdt.core
b org.eclipse.ltk.core.refactoring
b org.eclipse.core.resources
b org.eclipse.e4.ui.progress
b org.eclipse.equinox.security.ui
b org.eclipse.help.ui
b org.eclipse.jsch.ui
b org.eclipse.ui.browser
b org.eclipse.ui.ide
b org.eclipse.jdt
b org.eclipse.platform
b org.eclipse.ui.intro.quicklinks
b org.eclipse.ui.intro.universal
b org.eclipse.ui.intro
b org.eclipse.ui.monitoring
b org.eclipse.ui.navigator
b org.eclipse.ui.net
b org.eclipse.ui.views.properties.tabbed
b org.eclipse.ui.views
b org.eclipse.ui.views.log
b org.eclipse.ui
b org.eclipse.ui.workbench
b org.eclipse.e4.ui.workbench.addons.swt
b org.eclipse.e4.ui.workbench.renderers.swt
b org.eclipse.e4.ui.workbench.swt
b org.eclipse.e4.ui.workbench
b org.eclipse.e4.core.commands
b org.eclipse.help.webapp
b org.eclipse.help.base
b org.eclipse.jdt.doc.isv
b org.eclipse.jdt.doc.user
b org.eclipse.platform.doc.user
b org.eclipse.help
b org.eclipse.core.expressions
b org.eclipse.core.filebuffers
b org.eclipse.core.variables
b org.eclipse.e4.ui.model.workbench
b org.eclipse.e4.emf.xpath
b org.eclipse.ui.themes
b org.eclipse.e4.ui.css.swt.theme
b org.eclipse.ui.cheatsheets
b org.eclipse.ui.forms
b org.eclipse.e4.ui.css.swt
b org.eclipse.e4.ui.css.core
b org.eclipse.e4.ui.dialogs
b org.eclipse.e4.ui.ide
b org.eclipse.e4.ui.workbench3
b org.eclipse.jsch.core
b org.eclipse.core.net
b org.eclipse.equinox.p2.engine
b org.eclipse.equinox.p2.repository
b org.eclipse.equinox.security
b org.eclipse.jface.notifications
b org.eclipse.jface.text
b org.eclipse.text
b org.eclipse.urischeme
b org.eclipse.equinox.p2.artifact.repository
b org.eclipse.equinox.p2.metadata
b org.eclipse.equinox.p2.core
b org.eclipse.core.runtime
b org.eclipse.core.contenttype
b org.eclipse.core.filesystem
b org.eclipse.e4.ui.bindings
b org.eclipse.e4.ui.services
b org.eclipse.e4.core.services
b org.eclipse.core.jobs
b org.eclipse.e4.core.di.extensions.supplier
b org.eclipse.e4.core.contexts
b org.eclipse.e4.core.di
b org.eclipse.equinox.bidi
b org.eclipse.jface.databinding
b org.eclipse.jface
b org.eclipse.core.commands
b org.eclipse.core.databinding
b org.eclipse.core.databinding.beans
b org.eclipse.core.databinding.property
b org.eclipse.core.databinding.observable
b org.eclipse.equinox.app
b org.eclipse.equinox.frameworkadmin
b org.eclipse.equinox.frameworkadmin.equinox
b org.eclipse.equinox.http.registry
b org.eclipse.equinox.jsp.jasper.registry
b org.eclipse.equinox.preferences
b org.eclipse.equinox.p2.metadata.repository
b org.eclipse.equinox.registry
b org.eclipse.equinox.simpleconfigurator.manipulator
b org.eclipse.update.configurator
b org.eclipse.equinox.common
b org.eclipse.equinox.http.servlet
b org.eclipse.equinox.supplement
b org.eclipse.osgi.compatibility.state
  problem: Invalid operation on a fragment. osgi.identity; osgi.identity="org.eclipse.osgi.compatibility.state"; type="osgi.fragment"; version:Version="1.2.700.v20220722-0431"; tags:List<String>="osgi.connect" [id=197]
b org.eclipse.osgi.services
b org.eclipse.osgi
Fri Dec 16 13:40:28 PST 2022 - [main] Product-specified preferences called before plugin is started
b org.apache.felix.scr
  problem: Exception in org.apache.felix.scr.impl.Activator.start() of bundle org.apache.felix.scr.
b org.apache.felix.configadmin
b org.apache.felix.dependencymanager
  problem: Exception in org.apache.felix.dm.impl.Activator.start() of bundle org.apache.felix.dependencymanager.
b org.apache.felix.atomos
b org.objenesis
b org.tukaani.xz
b org.osgi.service.component
b org.eclipse.equinox.concurrent
b org.eclipse.jdt.junit5.runtime
  problem: Could not resolve module: org.eclipse.jdt.junit5.runtime [207]
      Unresolved requirement: Require-Bundle: org.junit; bundle-version="4.12.0"
      Unresolved requirement: Require-Bundle: org.eclipse.jdt.junit.runtime; bundle-version="[3.5.0,4.0.0)"
        -> Bundle-SymbolicName: org.eclipse.jdt.junit.runtime; bundle-version="3.7.0.v20220609-1843"; tags:List<String>="osgi.connect"; singleton:="true"
           org.eclipse.jdt.junit.runtime [244]
b junit-jupiter-params
b junit-jupiter-engine
b junit-jupiter-migrationsupport
  problem: Could not resolve module: junit-jupiter-migrationsupport [210]
      Unresolved requirement: Import-Package: org.junit; version="[4.12.0,5.0.0)"
b junit-jupiter-api
b junit-platform-runner
  problem: Could not resolve module: junit-platform-runner [212]
      Unresolved requirement: Import-Package: org.junit.runner; version="[4.12.0,5.0.0)"
b junit-platform-suite-engine
b junit-platform-suite-commons
b junit-platform-launcher
b junit-vintage-engine
  problem: Could not resolve module: junit-vintage-engine [216]
      Unresolved requirement: Import-Package: junit.runner; version="[4.12.0,5.0.0)"
b junit-platform-engine
b junit-platform-suite-api
b junit-platform-commons
b junit-jupiter
b assertj-core
b com.ibm.icu
b com.sun.jna.platform
b com.sun.jna
b jakarta.servlet-api
b javax.servlet.jsp-api
  problem: Could not resolve module: javax.servlet.jsp-api [226]
      Unresolved requirement: Import-Package: javax.el
b org.apache.commons.commons-fileupload
b org.apache.commons.commons-io
b org.apiguardian.api
b org.eclipse.e4.core.di.annotations
  problem: Could not resolve module: org.eclipse.e4.core.di.annotations [230]
      Unresolved requirement: Import-Package: javax.inject; version="1.0.0"
b org.eclipse.e4.core.di.extensions
  problem: Could not resolve module: org.eclipse.e4.core.di.extensions [231]
      Unresolved requirement: Import-Package: javax.inject; version="1.0.0"
b org.eclipse.e4.ui.di
  problem: Could not resolve module: org.eclipse.e4.ui.di [232]
      Unresolved requirement: Import-Package: javax.inject; version="1.0.0"
b org.eclipse.e4.ui.widgets
b org.eclipse.emf.ecore.change
b org.eclipse.emf.ecore.xmi
b org.eclipse.emf.ecore
  problem: Exception in org.eclipse.emf.ecore.plugin.EcorePlugin$Implementation$Activator.start() of bundle org.eclipse.emf.ecore.
b org.eclipse.emf.common
b org.eclipse.equinox.event
b org.eclipse.equinox.http.jetty
  problem: Could not resolve module: org.eclipse.equinox.http.jetty [239]
      Unresolved requirement: Import-Package: org.eclipse.jetty.server.handler; version="[10.0.2,11.0.0)"
        -> Export-Package: org.eclipse.jetty.server.handler; bundle-symbolic-name="org.eclipse.jetty.server"; bundle-version="10.0.11"; version="10.0.11"; uses:="javax.servlet,javax.servlet.descriptor,javax.servlet.http,org.eclipse.jetty.http,org.eclipse.jetty.http.pathmap,org.eclipse.jetty.io,org.eclipse.jetty.server,org.eclipse.jetty.util,org.eclipse.jetty.util.annotation,org.eclipse.jetty.util.component,org.eclipse.jetty.util.resource,org.slf4j"
           org.eclipse.jetty.server [66]
             Unresolved requirement: Import-Package: org.slf4j; version="[1.7.0,3.0.0)"
b org.eclipse.equinox.launcher
b org.eclipse.equinox.simpleconfigurator
b org.eclipse.jdt.annotation
b org.eclipse.jdt.junit4.runtime
  problem: Could not resolve module: org.eclipse.jdt.junit4.runtime [243]
      Unresolved requirement: Require-Bundle: org.junit; bundle-version="4.7.0"
b org.eclipse.jdt.junit.runtime
  problem: Could not resolve module: org.eclipse.jdt.junit.runtime [244]
      Unresolved requirement: Require-Bundle: org.junit; bundle-version="3.8.2"
b org.eclipse.osgi.util
b org.eclipse.rcp
b org.eclipse.swt.cocoa.macosx.aarch64
  problem: Invalid operation on a fragment. osgi.identity; osgi.identity="org.eclipse.swt.cocoa.macosx.aarch64"; type="osgi.fragment"; version:Version="3.121.0.v20220829-1402"; tags:List<String>="osgi.connect"; singleton:="true" [id=247]
b org.eclipse.swt
b org.opentest4j
b org.osgi.service.cm
b org.osgi.service.device
b org.osgi.service.event
b org.osgi.service.metatype
b org.osgi.service.prefs
b org.osgi.service.provisioning
b org.osgi.service.upnp
b org.osgi.service.useradmin
b org.osgi.service.wireadmin
b org.osgi.util.promise
b org.osgi.util.function
b org.osgi.util.measurement
b org.osgi.util.position
b org.osgi.util.xml
b com.squareup.okio
  problem: Could not resolve module: com.squareup.okio [264]
      Unresolved requirement: Import-Package: kotlin
b osgi.annotation
b org.osgi.namespace.extender
b net.bytebuddy.byte-buddy
b jakarta.annotation-api
b org.apache.commons.jxpath
  problem: Could not resolve module: org.apache.commons.jxpath [269]
      Unresolved requirement: Import-Package: org.apache.commons.beanutils
      Unresolved requirement: Import-Package: javax.servlet.jsp
        -> Export-Package: javax.servlet.jsp; bundle-symbolic-name="javax.servlet.jsp-api"; bundle-version="2.3.3"; version="2.3.3"; uses:="javax.servlet,javax.el,javax.servlet.jsp.el,javax.servlet.jsp.tagext,javax.servlet.http"
           javax.servlet.jsp-api [226]
b org.w3c.css.sac
b org.eclipse.jetty.servlet-api
b org.osgi.namespace.implementation
b bcpg
b bcprov
Exception in thread "main" java.lang.IllegalArgumentException: Expected exactly one application, got []
	at dev.equo.solstice.IdeMainUi.main(IdeMainUi.java:36)
	at dev.equo.solstice.IdeMainTest.main(IdeMainTest.java:71)

but the important part is that Atomos

  • throws lots of errors for unresolved Import-Packages which actually are on the classpath, but not correctly packaged for OSGi
    • part of the point of Solstice is that if the classes are there, it should just work and you shouldn't have to learn about / debug OSGi metadata
  • seems to activate org.eclipse.ui.ide.application successfully
  • but its BundleContext still doesn't have the service we need, so maybe it's actually not starting? We get error here Expected exactly one application, got []
    • var appServices =
      osgiShim.getServiceReferences(
      org.osgi.service.application.ApplicationDescriptor.class,
      "(service.pid=org.eclipse.ui.ide.workbench)");
      if (appServices.size() != 1) {
      throw new IllegalArgumentException("Expected exactly one application, got " + appServices);
      }

I can dig in more later, but just wanted to share my first experience with Atomos.

@tjwatson
Copy link

  • part of the point of Solstice is that if the classes are there, it should just work and you shouldn't have to learn about / debug OSGi metadata

Atomos also has the ability to use a header provider:

https://github.com/apache/felix-atomos/blob/4d50bad3990eeded5add6724563a196b2b02e6b6/atomos/src/main/java/org/apache/felix/atomos/Atomos.java#L343

https://github.com/apache/felix-atomos/blob/4d50bad3990eeded5add6724563a196b2b02e6b6/atomos/src/main/java/org/apache/felix/atomos/Atomos.java#L330

https://github.com/apache/felix-atomos/blob/4d50bad3990eeded5add6724563a196b2b02e6b6/atomos/src/main/java/org/apache/felix/atomos/Atomos.java#L286

Perhaps solstice can be a header provider. It could do one of the following:

  1. Augment all the package, bundle, fragment requirements to be optional so that you get no resolution errors even when there is no provider.
  2. Augment manfests for JARs that are not proper OSGi bundles to discover their packages and provide proper OSGi bundle headers so they do export the packages they have.

@nedtwigg
Copy link
Collaborator Author

nedtwigg commented Jan 1, 2023

Thanks @tjwatson for the HeaderProvider links. As I iterate over the bundles and call .start(), here's the first one to throw an exception:

BUNDLE org.eclipse.jdt.junit5.runtime
org.osgi.framework.BundleException: Could not resolve module: org.eclipse.jdt.junit5.runtime [207]
  Unresolved requirement: Require-Bundle: org.junit; bundle-version="4.12.0"
  Unresolved requirement: Require-Bundle: org.eclipse.jdt.junit.runtime; bundle-version="[3.5.0,4.0.0)"
    -> Bundle-SymbolicName: org.eclipse.jdt.junit.runtime; bundle-version="3.7.0.v20220609-1843"; tags:List<String>="osgi.connect"; singleton:="true"
       org.eclipse.jdt.junit.runtime [244]
         Unresolved requirement: Require-Bundle: org.junit; bundle-version="3.8.2"

I know there's enough bundles to start a workbench without throwing any exceptions, so my goal for now is to make the OSGi headers happy until I can start a workbench. So I add a header provider which:

  • parses every Require-Bundle header in a simple way which removes all the versions and option flags
  • removes org.junit if it is present
    private static Optional<Map<String, String>> headerProvider(
    String location, Map<String, String> existingHeaders) {
    return new ModifiedHeaders(existingHeaders)
    .removeReqBundle("org.junit")
    // .removeImpPkg("org.junit")
    .headers();
    // return Optional.empty();
    }

I apply this to every single bundle, but I get the exact same error. I know that the header provider is kind of working, because I print out the headers

var headers = b.getHeaders();
var keys = headers.keys();
while (keys.hasMoreElements()) {
var key = keys.nextElement();
System.err.println(" HEADER " + key + " = " + headers.get(key));
}

Before-> Require-Bundle = org.eclipse.jdt.junit.runtime;bundle-version="[3.5.0,4.0.0)",junit-jupiter-api;bundle-version="5.4.0",junit-jupiter-engine;bundle-version="5.4.0",junit-jupiter-migrationsupport;bundle-version="5.4.0",junit-jupiter-params;bundle-version="5.4.0",junit-vintage-engine;bundle-version="5.4.0",org.opentest4j;bundle-version="1.1.1",junit-platform-commons;bundle-version="1.4.0",junit-platform-engine;bundle-version="1.4.0",junit-platform-launcher;bundle-version="1.4.0",junit-platform-runner;bundle-version="1.4.0",junit-platform-suite-api;bundle-version="1.4.0",junit-platform-suite-commons;bundle-version="1.8.1",junit-platform-suite-engine;bundle-version="1.8.1",org.apiguardian.api;bundle-version="1.0.0",org.junit;bundle-version="4.12.0"
After -> Require-Bundle = org.eclipse.jdt.junit.runtime,junit-jupiter-api,junit-jupiter-engine,junit-jupiter-migrationsupport,junit-jupiter-params,junit-vintage-engine,org.opentest4j,junit-platform-commons,junit-platform-engine,junit-platform-launcher,junit-platform-runner,junit-platform-suite-api,junit-platform-suite-commons,junit-platform-suite-engine,org.apiguardian.api

So it seems like the header provider is working in that it changes the headers that Atomos provides and removes org.junit, but Atomos isn't actually operating on those headers, it's still operating on the built-in headers.

@tjwatson
Copy link

tjwatson commented Jan 3, 2023

I cloned the repository, but it is unclear to me how to run the IdeMainTest class

@nedtwigg
Copy link
Collaborator Author

nedtwigg commented Jan 3, 2023

Thanks very much for taking a look! I just pushed up a commit so that ./gradlew IdeMainTest will launch it.

@nedtwigg
Copy link
Collaborator Author

nedtwigg commented Jan 3, 2023

Also, you can do

  • ./gradlew eclipse
  • In Eclipse: File -> Import... -> General -> Existing Projects into Workspace, pick the solstice

@tjwatson
Copy link

tjwatson commented Jan 3, 2023

I got:

Exception in thread "main" org.osgi.framework.BundleException: Invalid manifest header Require-Bundle: ""
        at org.eclipse.osgi.util.ManifestElement.parseHeader(ManifestElement.java:355)
        at org.eclipse.osgi.container.builders.OSGiManifestBuilderFactory.validateHeaders(OSGiManifestBuilderFactory.java:137)
        at org.eclipse.osgi.container.builders.OSGiManifestBuilderFactory.createBuilder(OSGiManifestBuilderFactory.java:100)
        at org.eclipse.osgi.storage.Storage.getBuilder(Storage.java:821)
        at org.eclipse.osgi.storage.Storage.getBuilder(Storage.java:803)
        at org.eclipse.osgi.storage.Storage.install(Storage.java:723)
        at org.eclipse.osgi.internal.framework.BundleContextImpl.installBundle(BundleContextImpl.java:182)
        at org.eclipse.osgi.internal.framework.BundleContextImpl.installBundle(BundleContextImpl.java:175)
        at org.apache.felix.atomos.impl.base.AtomosBase.installAtomosContent(AtomosBase.java:514)
        at org.apache.felix.atomos.impl.base.AtomosBase$AtomosLayerBase$AtomosContentBase.install(AtomosBase.java:1385)
        at org.apache.felix.atomos.AtomosContent.install(AtomosContent.java:112)
        at dev.equo.solstice.IdeMainTest.main(IdeMainTest.java:95)

I updated the provider to this:

diff --git a/solstice/src/test/java/dev/equo/solstice/IdeMainTest.java b/solstice/src/test/java/dev/equo/solstice/IdeMainTest.java
index 5880986..a48d3ff 100644
--- a/solstice/src/test/java/dev/equo/solstice/IdeMainTest.java
+++ b/solstice/src/test/java/dev/equo/solstice/IdeMainTest.java
@@ -65,7 +65,11 @@ public class IdeMainTest {
                        }
                        List<String> headerList = Solstice.parseManifestHeaderSimple(headerUnparsed);
                        headerList.removeAll(Arrays.asList(toRemove));
-                       headers.put(header, headerList.stream().collect(Collectors.joining(",")));
+                       if (headerList.isEmpty()) {
+                               headers.remove(header);
+                       } else {
+                               headers.put(header, headerList.stream().collect(Collectors.joining(",")));
+                       }

But this still left me with lots of unresolved bundles. So I decided to simply remove all Import-Package and Require-Bundle headers:

        private static Optional<Map<String, String>> headerProvider(
                        String location, Map<String, String> existingHeaders) {

               Map<String, String> copy = new LinkedHashMap<>(existingHeaders);
               copy.remove(Constants.IMPORT_PACKAGE);
               copy.remove(Constants.REQUIRE_BUNDLE);
               return Optional.of(copy);
        }

But then I still had issues like you describe where the headers don't seem to take effect in the framework. Turns out there is an equinox cache holding onto the previous header values.

For me this cache is located in `~/.gradle/caches/modules-2/files-2.1/org.eclipse.platform/org.eclipse.osgi/3.18.100/ with a hashed directory name. I removed all directories under here. Then the bundles "resolve" fine, but I get an NPE activating one:

org.osgi.framework.BundleException: Exception in org.eclipse.emf.ecore.plugin.EcorePlugin$Implementation$Activator.start() of bundle org.eclipse.emf.ecore.
        at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:839)
        at org.eclipse.osgi.internal.framework.BundleContextImpl.start(BundleContextImpl.java:767)
        at org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0(EquinoxBundle.java:1032)
        at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.startWorker(EquinoxBundle.java:371)
        at org.eclipse.osgi.container.Module.doStart(Module.java:605)
        at org.eclipse.osgi.container.Module.start(Module.java:468)
        at org.eclipse.osgi.internal.framework.EquinoxBundle.start(EquinoxBundle.java:445)
        at org.eclipse.osgi.internal.framework.EquinoxBundle.start(EquinoxBundle.java:464)
        at dev.equo.solstice.IdeMainTest.main(IdeMainTest.java:99)
Caused by: java.lang.NullPointerException
        at org.eclipse.emf.ecore.plugin.RegistryReader.readRegistry(RegistryReader.java:84)
        at org.eclipse.emf.ecore.plugin.EcorePlugin$ExtensionProcessor.internalProcessExtensions(EcorePlugin.java:913)
        at org.eclipse.emf.ecore.plugin.EcorePlugin$ExtensionProcessor.access$0(EcorePlugin.java:866)
        at org.eclipse.emf.ecore.plugin.EcorePlugin$Implementation.start(EcorePlugin.java:676)
        at org.eclipse.emf.common.EMFPlugin$OSGiDelegatingBundleActivator.start(EMFPlugin.java:220)
        at org.eclipse.osgi.internal.framework.BundleContextImpl$2.run(BundleContextImpl.java:818)
        at org.eclipse.osgi.internal.framework.BundleContextImpl$2.run(BundleContextImpl.java:1)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:747)
        at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:810)
        ... 8 more

My guess is that this NPE is ultimately causing some issue for e4 to come up properly.

@nedtwigg
Copy link
Collaborator Author

nedtwigg commented Jan 3, 2023

Awesome, thanks for the ~/.gradle/caches/modules-2/files-2.1/org.eclipse.platform/org.eclipse.osgi/3.18.100/ hint!

Whenever a bundle has an Activator, or references a bundle with an Activator (via Import-Package or Require-Bundle), I think the headers are important because they determine the correct order to activate the bundles.

Now that I know about the cache and how to wipe it, I'll experiment some more. In the long-term I hope there's a way to disable the cache...

@tjwatson
Copy link

tjwatson commented Jan 3, 2023

Awesome, thanks for the ~/.gradle/caches/modules-2/files-2.1/org.eclipse.platform/org.eclipse.osgi/3.18.100/ hint!

Whenever a bundle has an Activator, or references a bundle with an Activator (via Import-Package or Require-Bundle), I think the headers are important because they determine the correct order to activate the bundles.

Then just make all the requirements have resolution:=optional directive from the Import-Package and Require-Bundle headers. You may want to consider also doing that for the Require-Capability header.

Now that I know about the cache and how to wipe it, I'll experiment some more. In the long-term I hope there's a way to disable the cache...

If you want to disable the cache:

			Framework framework = atomos.newFramework(
					Map.of(
							"atomos.content.install", "false",
							Constants.FRAMEWORK_STORAGE_CLEAN, Constants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT)
					);

@tjwatson
Copy link

tjwatson commented Jan 3, 2023

Also if you want to control where the framework cache is located then set the Constants.FRAMEWORK_STORAGE property to a path of your choice. Otherwise one is calculated for you by the framework implementation.

@tjwatson
Copy link

tjwatson commented Jan 3, 2023

Making everything optional may be more simple than trying to ignore certain names:

	private static Optional<Map<String, String>> headerProvider(
			String location, Map<String, String> existingHeaders) {
		return new ModifiedHeaders(existingHeaders)
				.removeAll(Constants.REQUIRE_CAPABILITY)
				.makeOptional(Constants.REQUIRE_BUNDLE)
				.makeOptional(Constants.IMPORT_PACKAGE)
				.headers();
	}
...
		public ModifiedHeaders makeOptional(String header) {
			String headerUnparsed = headers.get(header);
			if (headerUnparsed == null) {
				return this;
			}
			List<String> headerList = Solstice.parseManifestHeaderSimple(headerUnparsed);
			headers.put(header, headerList.stream().map(h -> h + "; resolution:=optional").collect(Collectors.joining(",")));
			return this;
		}

With that I ran into issues because the org.eclipse.equinox.supplemental bundle is getting pulled in from some dependency (I guess). So I filtered that out from install:

			for (AtomosContent content : atomos.getBootLayer().getAtomosContents()) {
				// The resulting bundle will use a bundle location of
				// "atomos:" + atomosContent.getAtomosLocation();
				if (!"org.eclipse.equinox.supplement".equals(content.getSymbolicName())) {
					bundles.add(content.install());
				}
			}

But then I ran into this exception which I think will end up blocking you:

Exception in thread "main" java.lang.UnsupportedOperationException
        at org.eclipse.osgi.internal.connect.ConnectBundleFile$ConnectBundleEntry.getLocalURL(ConnectBundleFile.java:82)
        at org.eclipse.osgi.storage.url.BundleURLConnection.getLocalURL(BundleURLConnection.java:134)
        at org.eclipse.osgi.storage.url.BundleURLConverter.resolve(BundleURLConverter.java:57)
        at org.eclipse.core.runtime.FileLocator.resolve(FileLocator.java:288)
        at org.eclipse.core.internal.runtime.PlatformURLConverter.resolve(PlatformURLConverter.java:53)
        at org.eclipse.core.runtime.FileLocator.resolve(FileLocator.java:288)
        at org.eclipse.e4.ui.css.swt.internal.theme.ThemeEngine.setTheme(ThemeEngine.java:466)
        at org.eclipse.e4.ui.css.swt.internal.theme.ThemeEngine.setTheme(ThemeEngine.java:434)
        at org.eclipse.e4.ui.css.swt.internal.theme.ThemeEngine.setTheme(ThemeEngine.java:426)
        at org.eclipse.e4.ui.css.swt.internal.theme.ThemeEngine.restore(ThemeEngine.java:620)
        at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.setCSSTheme(PartRenderingEngine.java:1417)
        at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.initializeStyling(PartRenderingEngine.java:1324)
        at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1050)
        at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:338)
        at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1046)
        at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:155)
        at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:643)
        at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:338)
        at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:550)
        at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:171)
        at dev.equo.solstice.IdeMainUi.main(IdeMainUi.java:55)
        at dev.equo.solstice.IdeMainTest.main(IdeMainTest.java:146)

When implementing this I was not sure what to do for the local URL. At this level the framework does not know what protocol sits behind the connected resource and I am not sure how we could fix that.

@tjwatson
Copy link

tjwatson commented Jan 3, 2023

You could open a bug against Equinox on this. I suppose the connect implementation in Equinox could ask for the resource with the connected class loader and then return that resource URL from org.eclipse.osgi.internal.connect.ConnectBundleFile.ConnectBundleEntry.getLocalURL()

@nedtwigg
Copy link
Collaborator Author

nedtwigg commented Jan 3, 2023

Amazing! I've made a few tweaks

  • any header with resolution:=optional gets removed
  • I populated the list of ignored packages and bundles for useSolstice = false using useSolstice = true

At this point, using useSolstice = true still gives the same semi-functional workbench we've had all along. And with useSolstice = false we are now able to start every single bundle. We die like this

Exception in thread "main" org.eclipse.e4.core.di.InjectionException: Unable to process "WorkbenchLogger#setFrameworkLog()": no actual value was found for the argument "FrameworkLog".
	at org.eclipse.e4.core.internal.di.InjectorImpl.reportUnresolvedArgument(InjectorImpl.java:478)
	at org.eclipse.e4.core.internal.di.InjectorImpl.resolveRequestorArgs(InjectorImpl.java:469)
	at org.eclipse.e4.core.internal.di.InjectorImpl.internalInject(InjectorImpl.java:129)
	at org.eclipse.e4.core.internal.di.InjectorImpl.internalMake(InjectorImpl.java:403)
	at org.eclipse.e4.core.internal.di.InjectorImpl.make(InjectorImpl.java:330)
	at org.eclipse.e4.core.contexts.ContextInjectionFactory.make(ContextInjectionFactory.java:202)
	at org.eclipse.e4.ui.internal.workbench.swt.E4Application.createDefaultContext(E4Application.java:500)
	at org.eclipse.e4.ui.internal.workbench.swt.E4Application.createE4Workbench(E4Application.java:213)
	at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:572)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:338)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:550)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:171)

The good news is that we're able to get all the way to L55 of this, so the context has some services populated

static int main(BundleContext osgiShim) throws InvalidSyntaxException {
var appServices =
osgiShim.getServiceReferences(
org.osgi.service.application.ApplicationDescriptor.class,
"(service.pid=org.eclipse.ui.ide.workbench)");
if (appServices.size() != 1) {
throw new IllegalArgumentException("Expected exactly one application, got " + appServices);
}
var appDescriptor = osgiShim.getService(appServices.iterator().next());
var appLauncher = new EclipseAppLauncher(osgiShim, false, false, null, null);
osgiShim.registerService(ApplicationLauncher.class, appLauncher, Dictionaries.empty());
Map<String, Object> appProps = new HashMap<>();
appProps.put(IApplicationContext.APPLICATION_ARGS, new String[] {});
try {
var appHandle = appDescriptor.launch(appProps);
} catch (ApplicationException e) {
throw new RuntimeException(e);
}
var display = PlatformUI.createDisplay();
// processor must be created before we start event loop
var processor = new DelayedEventsProcessor(display);
return PlatformUI.createAndRunWorkbench(

It is strange that FrameworkLog is not present. I can step through org.eclipse.osgi.internal.framework.SystemBundleActivator and see what looks like the missing services getting registered. I can also add them manually using the tricks we've been using for useSolstice=true

context.registerService(
org.osgi.service.condition.Condition.class,
new org.osgi.service.condition.Condition() {},
Dictionaries.of("osgi.condition.id", "true"));
context.registerService(FrameworkLog.class, new ShimFrameworkLog(), Dictionaries.empty());
context.registerService(
DebugOptions.class,
new DebugOptions() {

By adding those lines, I can get the problem to switch back and forth between DebugOptions and FrameworkLog, but somehow it is always missing at least one of those, even if I manually add them both.

Any tips or tricks for how Atomos handles logging?

@tjwatson
Copy link

tjwatson commented Jan 3, 2023

Exception in thread "main" org.eclipse.e4.core.di.InjectionException: Unable to process "WorkbenchLogger#setFrameworkLog()": no actual value was found for the argument "FrameworkLog".
	at org.eclipse.e4.core.internal.di.InjectorImpl.reportUnresolvedArgument(InjectorImpl.java:478)
	at org.eclipse.e4.core.internal.di.InjectorImpl.resolveRequestorArgs(InjectorImpl.java:469)
	at org.eclipse.e4.core.internal.di.InjectorImpl.internalInject(InjectorImpl.java:129)
	at org.eclipse.e4.core.internal.di.InjectorImpl.internalMake(InjectorImpl.java:403)
	at org.eclipse.e4.core.internal.di.InjectorImpl.make(InjectorImpl.java:330)
	at org.eclipse.e4.core.contexts.ContextInjectionFactory.make(ContextInjectionFactory.java:202)
	at org.eclipse.e4.ui.internal.workbench.swt.E4Application.createDefaultContext(E4Application.java:500)
	at org.eclipse.e4.ui.internal.workbench.swt.E4Application.createE4Workbench(E4Application.java:213)
	at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:572)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:338)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:550)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:171)

I saw similar issues until I filtered out org.eclipse.equinox.suppliment

@nedtwigg nedtwigg marked this pull request as draft January 4, 2023 06:29
…cially, the Atomos mode has a working `UIEventTopic` which means that tabs can dock and undock.
@nedtwigg nedtwigg changed the title Atomos experiment adopt Atomos Jan 5, 2023
@nedtwigg nedtwigg merged commit d0f9bbb into main Jan 5, 2023
@nedtwigg nedtwigg deleted the atomos-experiment branch January 5, 2023 18:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants