From 32967339d8b2b8b14cb748417be5a4dbb5389cfd Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Fri, 8 Mar 2024 22:01:02 +0100 Subject: [PATCH] Improve subframework launch --- org.argeo.cms/bnd.bnd | 4 + .../argeo/cms/acr/CmsContentNamespace.java | 9 +- .../src/org/argeo/cms/acr/TypesManager.java | 5 + .../argeo/cms/internal/osgi/CmsActivator.java | 14 +- .../src/org/argeo/api/init/InitConstants.java | 2 + .../init/osgi/SubFrameworkActivator.java | 152 ++++++++++++------ 6 files changed, 128 insertions(+), 58 deletions(-) diff --git a/org.argeo.cms/bnd.bnd b/org.argeo.cms/bnd.bnd index 3807af9d1..a183784c1 100644 --- a/org.argeo.cms/bnd.bnd +++ b/org.argeo.cms/bnd.bnd @@ -4,6 +4,10 @@ Import-Package: \ org.osgi.*;version=0.0.0,\ * +Export-Package:\ +org.argeo.cms.acr.schemas,\ +* + Service-Component:\ OSGI-INF/cmsOsgiLogger.xml,\ OSGI-INF/cmsUuidFactory.xml,\ diff --git a/org.argeo.cms/src/org/argeo/cms/acr/CmsContentNamespace.java b/org.argeo.cms/src/org/argeo/cms/acr/CmsContentNamespace.java index 429b759fc..fe334cb5b 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/CmsContentNamespace.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/CmsContentNamespace.java @@ -1,6 +1,7 @@ package org.argeo.cms.acr; import java.net.MalformedURLException; +import java.net.URI; import java.net.URL; import java.util.Objects; @@ -52,7 +53,13 @@ public enum CmsContentNamespace implements ContentNamespace { Objects.requireNonNull(namespace); this.namespace = namespace; if (resourceFileName != null) { - resource = getClass().getResource(RESOURCE_BASE + resourceFileName); + // resource = getClass().getResource(RESOURCE_BASE + resourceFileName); + try { + // FIXME workaround when in nested OSGi frameworks + resource = URI.create("platform:/plugin/org.argeo.cms" + RESOURCE_BASE + resourceFileName).toURL(); + } catch (MalformedURLException e) { + throw new IllegalArgumentException("Cannot convert " + resourceFileName + " to URL"); + } Objects.requireNonNull(resource); } if (publicUrl != null) diff --git a/org.argeo.cms/src/org/argeo/cms/acr/TypesManager.java b/org.argeo.cms/src/org/argeo/cms/acr/TypesManager.java index f0d59e25f..05c7ca638 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/TypesManager.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/TypesManager.java @@ -118,6 +118,11 @@ class TypesManager { List sourcesToUse = new ArrayList<>(); for (URL sourceUrl : sources) { sourcesToUse.add(new StreamSource(sourceUrl.toExternalForm())); +// try { +// sourcesToUse.add(new StreamSource(sourceUrl.openStream())); +// } catch (IOException e) { +// log.error("Cannot open schema source " + sourceUrl); +// } } schema = schemaFactory.newSchema(sourcesToUse.toArray(new Source[sourcesToUse.size()])); // for (StreamSource source : sourcesToUse) { diff --git a/org.argeo.cms/src/org/argeo/cms/internal/osgi/CmsActivator.java b/org.argeo.cms/src/org/argeo/cms/internal/osgi/CmsActivator.java index fe7c80d3b..94b08da4d 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/osgi/CmsActivator.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/osgi/CmsActivator.java @@ -21,19 +21,13 @@ import org.osgi.service.permissionadmin.PermissionInfo; */ public class CmsActivator implements BundleActivator { // private final static CmsLog log = CmsLog.getLog(CmsActivator.class); + private final static String PROP_ARGEO_OSGI_PARENT_UUID = "argeo.osgi.parent.uuid"; // TODO make it configurable private boolean hardened = false; private static BundleContext bundleContext; - void init() { - } - - void destroy() { - new GogoShellKiller().start(); - } - protected void initSecurity() { // code-level permissions String osgiSecurity = bundleContext.getProperty(Constants.FRAMEWORK_SECURITY); @@ -96,15 +90,15 @@ public class CmsActivator implements BundleActivator { cmsState.start(); bundleContext.registerService(new String[] { CmsState.class.getName(), NodeIdSupplier.class.getName() }, cmsState, null); - init(); - } @Override public void stop(BundleContext bc) throws Exception { try { - destroy(); CmsStateImpl cmsState = (CmsStateImpl) getService(CmsState.class); + String parentFrameworkuuid = bc.getProperty(PROP_ARGEO_OSGI_PARENT_UUID); + if (parentFrameworkuuid == null) + new GogoShellKiller().start(); cmsState.stop(); } finally { bundleContext = null; diff --git a/org.argeo.init/src/org/argeo/api/init/InitConstants.java b/org.argeo.init/src/org/argeo/api/init/InitConstants.java index 998a0a4b7..3c558f898 100644 --- a/org.argeo.init/src/org/argeo/api/init/InitConstants.java +++ b/org.argeo.init/src/org/argeo/api/init/InitConstants.java @@ -9,6 +9,8 @@ public interface InitConstants { String PROP_OSGI_CONFIGURATION_AREA = "osgi.configuration.area"; String PROP_OSGI_SHARED_CONFIGURATION_AREA = "osgi.sharedConfiguration.area"; String PROP_ARGEO_OSGI_MAX_START_LEVEL = "argeo.osgi.maxStartLevel"; + /** UUID of the parent framework. Marks a nested runtime. */ + String PROP_ARGEO_OSGI_PARENT_UUID = "argeo.osgi.parent.uuid"; // OSGi standard properties String PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL = "osgi.bundles.defaultStartLevel"; diff --git a/org.argeo.init/src/org/argeo/init/osgi/SubFrameworkActivator.java b/org.argeo.init/src/org/argeo/init/osgi/SubFrameworkActivator.java index 3d6469df8..2aa488050 100644 --- a/org.argeo.init/src/org/argeo/init/osgi/SubFrameworkActivator.java +++ b/org.argeo.init/src/org/argeo/init/osgi/SubFrameworkActivator.java @@ -1,17 +1,22 @@ package org.argeo.init.osgi; +import static java.lang.System.Logger.Level.INFO; + import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.lang.System.Logger; import java.net.URL; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collections; import java.util.Dictionary; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.UUID; import java.util.function.Function; import java.util.stream.Collectors; @@ -21,7 +26,8 @@ import org.osgi.framework.Bundle; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleException; -import org.osgi.framework.BundleReference; +import org.osgi.framework.Constants; +import org.osgi.framework.FrameworkEvent; import org.osgi.framework.connect.ConnectContent; import org.osgi.framework.connect.ConnectFrameworkFactory; import org.osgi.framework.connect.ConnectModule; @@ -30,56 +36,66 @@ import org.osgi.framework.launch.Framework; import org.osgi.framework.wiring.BundleWiring; public class SubFrameworkActivator implements BundleActivator { + private final static Logger logger = System.getLogger(SubFrameworkActivator.class.getName()); + // private final static String EQUINOX_FRAMEWORK_CLASS = "org.eclipse.osgi.launch.Equinox"; private final static String EQUINOX_FRAMEWORK_FACTORY_CLASS = "org.eclipse.osgi.launch.EquinoxFactory"; // private ClassLoader bundleClassLoader; // private ClassLoader subFrameworkClassLoader; - private BundleContext bundleContext; + private BundleContext foreignBundleContext; private ConnectFrameworkFactory frameworkFactory; + private Map subFrameworks = Collections.synchronizedMap(new HashMap<>()); + + private UUID foreignFrameworkUuid; + @Override public void start(BundleContext context) throws Exception { - this.bundleContext = context; + this.foreignBundleContext = context; + foreignFrameworkUuid = UUID.fromString(foreignBundleContext.getProperty(Constants.FRAMEWORK_UUID)); try { - Bundle bundle = context.getBundle(); - ClassLoader bundleClassLoader = bundle.adapt(BundleWiring.class).getClassLoader(); +// Bundle bundle = context.getBundle(); +// ClassLoader bundleClassLoader = bundle.adapt(BundleWiring.class).getClassLoader(); // subFrameworkClassLoader = new URLClassLoader(new URL[0], bundleClassLoader); @SuppressWarnings("unchecked") - Class frameworkFactoryClass = (Class) bundleClassLoader - .loadClass(EQUINOX_FRAMEWORK_FACTORY_CLASS); + Class frameworkFactoryClass = (Class) Framework.class + .getClassLoader().loadClass(EQUINOX_FRAMEWORK_FACTORY_CLASS); frameworkFactory = frameworkFactoryClass.getConstructor().newInstance(); - new Thread() { + boolean test = true; + if (test) + new Thread() { - @Override - public void run() { - for (int i = 0; i < 5; i++) { - Map config = new HashMap<>(); - Path basePase = Paths.get(System.getProperty("user.home"), ".config/argeo/test/", "test" + i); - config.put(InitConstants.PROP_OSGI_CONFIGURATION_AREA, - basePase.resolve(RuntimeManager.STATE).toString()); - config.put(InitConstants.PROP_OSGI_INSTANCE_AREA, - basePase.resolve(RuntimeManager.DATA).toString()); - config.put("argeo.host", "host" + i); - config.put("osgi.console", "host" + i + ":2023"); - startFramework(config); + @Override + public void run() { + for (int i = 0; i < 5; i++) { + Map config = new HashMap<>(); + Path basePase = Paths.get(System.getProperty("user.home"), ".config/argeo/test/", + "test" + i); + config.put(InitConstants.PROP_OSGI_CONFIGURATION_AREA, + basePase.resolve(RuntimeManager.STATE).toString()); + config.put(InitConstants.PROP_OSGI_INSTANCE_AREA, + basePase.resolve(RuntimeManager.DATA).toString()); + config.put("argeo.host", "host" + i); + config.put("osgi.console", "host" + i + ":2023"); + createFramework(config); + } } - } - }.start(); + }.start(); } catch (Exception e) { e.printStackTrace(); throw e; } } - Framework startFramework(Map config) { + Framework createFramework(Map config) { try { - URL bundleConfigUrl = bundleContext.getBundle().getEntry("config.ini"); + URL bundleConfigUrl = foreignBundleContext.getBundle().getEntry("config.ini"); try (InputStream in = bundleConfigUrl.openStream()) { RuntimeManager.loadConfig(in, config); } @@ -89,7 +105,7 @@ public class SubFrameworkActivator implements BundleActivator { // config.put("osgi.parentClassLoader", "app"); // config.put("osgi.contextClassLoaderParent", "app"); - ModuleConnector moduleConnector = new ParentBundleModuleConnector(bundleContext); + ModuleConnector moduleConnector = new ParentBundleModuleConnector(foreignBundleContext); // URL frameworkUrl = URI.create(bundleContext.getProperty("osgi.framework")).toURL(); // URLClassLoader frameworkClassLoader = new URLClassLoader(new URL[] { frameworkUrl, }); @@ -98,11 +114,19 @@ public class SubFrameworkActivator implements BundleActivator { // Framework framework = frameworkClass.getConstructor(Map.class, ModuleConnector.class).newInstance(config, // moduleConnector); + config.put(InitConstants.PROP_ARGEO_OSGI_PARENT_UUID, foreignFrameworkUuid.toString()); Framework framework = frameworkFactory.newFramework(config, moduleConnector); - framework.init(); + framework.init((e) -> { + UUID frameworkUuid = UUID + .fromString(framework.getBundleContext().getProperty(Constants.FRAMEWORK_UUID)); + if (e.getType() == FrameworkEvent.STOPPED) { + subFrameworks.remove(frameworkUuid); + logger.log(INFO, "Removed subframework " + frameworkUuid + " in parent " + foreignFrameworkUuid); + } + }); - for (Bundle b : bundleContext.getBundles()) { + for (Bundle b : foreignBundleContext.getBundles()) { if (b.getBundleId() == 0) continue; String location = b.getLocation(); @@ -110,7 +134,6 @@ public class SubFrameworkActivator implements BundleActivator { || location.contains("/org.argeo.tp.sys/") // || location.contains("/org.argeo.tp.httpd/") // || location.contains("/org.argeo.tp.sshd/") // - || location.contains("/org.argeo.cms/org.argeo.init") // ) { framework.getBundleContext().installBundle(b.getLocation()); } @@ -133,6 +156,10 @@ public class SubFrameworkActivator implements BundleActivator { // if (bc == null) // System.err.println(b.getSymbolicName() + " BC null"); // } + + UUID frameworkUuid = UUID.fromString(framework.getBundleContext().getProperty(Constants.FRAMEWORK_UUID)); + subFrameworks.put(frameworkUuid, framework); + logger.log(INFO, "Created subframework " + frameworkUuid + " in parent " + foreignFrameworkUuid); return framework; } catch (Exception e) { throw new IllegalStateException("Cannot start framework", e); @@ -141,11 +168,21 @@ public class SubFrameworkActivator implements BundleActivator { @Override public void stop(BundleContext context) throws Exception { - bundleContext = null; + for (Iterator it = subFrameworks.values().iterator(); it.hasNext();) { + Framework framework = it.next(); + framework.stop(); + it.remove(); + + } +// for (Framework framework : subFrameworks.values()) { +// framework.stop(); +// } + subFrameworks.clear(); + foreignBundleContext = null; frameworkFactory = null; } - class ParentBundleModuleConnector implements ModuleConnector { + static class ParentBundleModuleConnector implements ModuleConnector { private final BundleContext foreignBundleContext; private BundleContext localBundleContext; @@ -177,7 +214,8 @@ public class SubFrameworkActivator implements BundleActivator { public Optional connect(String location) throws BundleException { Bundle bundle = foreignBundleContext.getBundle(location); if (bundle != null && bundle.getBundleId() != 0) { - System.out.println("Foreign Bundle: " + bundle.getSymbolicName() + " " + location); + // System.out.println("Foreign Bundle: " + bundle.getSymbolicName() + " " + + // location); ConnectModule module = new ConnectModule() { @Override @@ -191,7 +229,7 @@ public class SubFrameworkActivator implements BundleActivator { } } - class ForeignBundleClassLoader extends ClassLoader implements BundleReference { + static class ForeignBundleClassLoader extends ClassLoader {// implements BundleReference { private BundleContext localBundleContext; private Bundle foreignBundle; @@ -202,24 +240,42 @@ public class SubFrameworkActivator implements BundleActivator { this.foreignBundle = foreignBundle; } - @Override - public Bundle getBundle() { +// @Override + protected Bundle getBundle() { return localBundleContext.getBundle(foreignBundle.getLocation()); } + +// @Override +// public URL getResource(String resName) { +// URL res = super.getResource(resName); +// return res; +// } +// +// @Override +// protected URL findResource(String resName) { +// Bundle localBundle = getBundle(); +// if (localBundle != null) { +// URL res = localBundle.getEntry(resName); +// if (res != null) +// return res; +// } +// return null; +// } + } - class ForeignBundleConnectContent implements ConnectContent { - private final Bundle bundle; + static class ForeignBundleConnectContent implements ConnectContent { + private final Bundle foreignBundle; private final ClassLoader classLoader; - public ForeignBundleConnectContent(BundleContext localBundleContext, Bundle bundle) { - this.bundle = bundle; - this.classLoader = new ForeignBundleClassLoader(localBundleContext, bundle); + public ForeignBundleConnectContent(BundleContext localBundleContext, Bundle foreignBundle) { + this.foreignBundle = foreignBundle; + this.classLoader = new ForeignBundleClassLoader(localBundleContext, foreignBundle); } @Override public Optional> getHeaders() { - Dictionary dict = bundle.getHeaders(); + Dictionary dict = foreignBundle.getHeaders(); List keys = Collections.list(dict.keys()); Map dictCopy = keys.stream().collect(Collectors.toMap(Function.identity(), dict::get)); return Optional.of(dictCopy); @@ -227,22 +283,24 @@ public class SubFrameworkActivator implements BundleActivator { @Override public Iterable getEntries() throws IOException { - List lst = Collections.list(bundle.findEntries("", "*", true)).stream().map((u) -> u.getPath()) - .toList(); + List lst = Collections.list(foreignBundle.findEntries("", "*", true)).stream() + .map((u) -> u.getPath()).toList(); return lst; } @Override public Optional getEntry(String path) { - URL u = bundle.getEntry(path); + URL u = foreignBundle.getEntry(path); if (u == null) { - u = bundle.getEntry("bin/" + path); + u = foreignBundle.getEntry("bin/" + path); // System.err.println(u2); } if (u == null) { if ("plugin.xml".equals(path)) return Optional.empty(); - System.err.println(bundle.getSymbolicName() + " " + path + " not found"); + if (path.startsWith("META-INF/versions/")) + return Optional.empty(); + System.err.println(foreignBundle.getSymbolicName() + " " + path + " not found"); return Optional.empty(); } URL url = u; @@ -255,8 +313,7 @@ public class SubFrameworkActivator implements BundleActivator { @Override public long getLastModified() { - // FIXME - return System.currentTimeMillis(); + return foreignBundle.getLastModified(); } @Override @@ -289,6 +346,7 @@ public class SubFrameworkActivator implements BundleActivator { @Override public void close() throws IOException { + } } -- 2.30.2