package org.argeo.cms.acr;
-import static java.lang.System.Logger.Level.ERROR;
-
import java.net.MalformedURLException;
-import java.net.URI;
import java.net.URL;
import java.util.Objects;
Objects.requireNonNull(namespace);
this.namespace = namespace;
if (resourceFileName != null) {
- // 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) {
- resource = null;
- System.getLogger(CmsContentNamespace.class.getName()).log(ERROR,
- "Cannot load " + resourceFileName + ": " + e.getMessage());
- // throw new IllegalArgumentException("Cannot convert " + resourceFileName + "
- // to URL");
- }
+ 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) {
+// resource = null;
+// System.getLogger(CmsContentNamespace.class.getName()).log(ERROR,
+// "Cannot load " + resourceFileName + ": " + e.getMessage());
+// // throw new IllegalArgumentException("Cannot convert " + resourceFileName + "
+// // to URL");
+// }
// Objects.requireNonNull(resource);
}
if (publicUrl != null)
String PROP_ARGEO_OSGI_SOURCES = "argeo.osgi.sources";
String PROP_ARGEO_OSGI_START = "argeo.osgi.start";
+
+ String PROP_OSGI_USE_SYSTEM_PROPERTIES = "osgi.framework.useSystemProperties";
+
String PROP_OSGI_INSTANCE_AREA = "osgi.instance.area";
String PROP_OSGI_CONFIGURATION_AREA = "osgi.configuration.area";
String PROP_OSGI_SHARED_CONFIGURATION_AREA = "osgi.sharedConfiguration.area";
+ String PROP_OSGI_SHARED_CONFIGURATION_AREA_RO = "osgi.sharedConfiguration.area.readOnly";
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";
String PROP_OSGI_STARTLEVEL = "osgi.startLevel";
- String PROP_OSGI_USE_SYSTEM_PROPERTIES = "osgi.framework.useSystemProperties";
+
+ // FOREIGN RUNTIME PROPERTIES
+ /**
+ * UUID of the parent framework. It is set by the parent runtime and marks a
+ * nested runtime.
+ */
+ String PROP_ARGEO_OSGI_PARENT_UUID = "argeo.osgi.parent.uuid";
+ /**
+ * The A2 categories to load from the parent. If not specified, nested runtimes
+ * won't be used.
+ */
+ @Deprecated
+ String PROP_ARGEO_OSGI_PARENT_CATEGORIES = "argeo.osgi.parent.categories";
+ String PROP_ARGEO_OSGI_EXPORT_CATEGORIES = "argeo.osgi.export.categories";
// Symbolic names
String SYMBOLIC_NAME_INIT = "org.argeo.init";
String JVM_ARGS = "jvm.args";
String STATE = "state";
String DATA = "data";
+ String SHARED = "shared";
public void startRuntime(String relPath, Consumer<Map<String, String>> configCallback);
public void closeRuntime(String relPath, boolean async);
default void startRuntime(String relPath, String props) {
+ startRuntime(relPath, (config) -> {
+ loadProperties(config, props);
+ });
+ }
+
+ static void loadProperties(Map<String, String> config, Properties properties) {
+ for (Object key : properties.keySet()) {
+ config.put(key.toString(), properties.getProperty(key.toString()));
+ }
+ }
+
+ static void loadProperties(Map<String, String> config, String props) {
Properties properties = new Properties();
try (Reader reader = new StringReader(props)) {
properties.load(reader);
} catch (IOException e) {
throw new IllegalArgumentException("Cannot load properties", e);
}
- startRuntime(relPath, (config) -> {
- for (Object key : properties.keySet()) {
- config.put(key.toString(), properties.getProperty(key.toString()));
- }
- });
+ loadProperties(config, properties);
+ }
+
+ static void loadProperties(Map<String, String> config, InputStream in) throws IOException {
+ Properties properties = new Properties();
+ properties.load(in);
+ loadProperties(config, properties);
+ }
+
+ static void loadDefaults(Map<String, String> config) {
+ try (InputStream in = RuntimeManager.class.getResourceAsStream("defaults.ini")) {
+ loadProperties(config, in);
+ } catch (IOException e) {
+ throw new IllegalStateException("Could not load runtime defaults", e);
+ }
}
/**
* Load configs recursively starting with the parent directories, until a
* jvm.args file is found.
*/
+ @Deprecated
static void loadConfig(Path dir, Map<String, String> config) {
try {
Path jvmArgsPath = dir.resolve(RuntimeManager.JVM_ARGS);
* starts with a '+' character, itis expected that the last character is a
* separator and it will be prepended to the existing value.
*/
+ @Deprecated
static void loadConfig(InputStream in, Map<String, String> config) throws IOException {
Properties props = new Properties();
props.load(in);
--- /dev/null
+# Disable Equinox Jetty autostart
+org.eclipse.equinox.http.jetty.autostart=false
+
+# System packages
+org.osgi.framework.system.packages.extra=\
+sun.misc,\
+sun.security.util,\
+sun.security.internal.spec,\
+sun.security.provider,\
+com.sun.net.httpserver,\
+com.sun.jndi.ldap,\
+com.sun.jndi.ldap.sasl,\
+com.sun.jndi.dns,\
+com.sun.security.jgss,\
+com.sun.nio.file,\
+com.sun.nio.sctp
private final static Logger logger = System.getLogger(RuntimeManagerMain.class.getName());
private final static String ENV_STATE_DIRECTORY = "STATE_DIRECTORY";
-// private final static String ENV_CONFIGURATION_DIRECTORY = "CONFIGURATION_DIRECTORY";
+ private final static String ENV_CONFIGURATION_DIRECTORY = "CONFIGURATION_DIRECTORY";
// private final static String ENV_CACHE_DIRECTORY = "CACHE_DIRECTORY";
private final static long RUNTIME_SHUTDOWN_TIMEOUT = 60 * 1000;
private Map<String, String> configuration = new HashMap<>();
- RuntimeManagerMain(Path configArea, Path stateArea) {
- RuntimeManager.loadConfig(configArea, configuration);
+ RuntimeManagerMain(Path configArea, Path writableArea) {
+// RuntimeManager.loadConfig(configArea, configuration);
// integration with OSGi runtime; this will be read by the init bundle
- configuration.put(ServiceMain.PROP_ARGEO_INIT_MAIN, "true");
+// configuration.put(ServiceMain.PROP_ARGEO_INIT_MAIN, "true");
+ RuntimeManager.loadDefaults(configuration);
+
configuration.put(InitConstants.PROP_OSGI_SHARED_CONFIGURATION_AREA, configArea.toUri().toString());
+ configuration.put(InitConstants.PROP_OSGI_SHARED_CONFIGURATION_AREA_RO, "true");
+ configuration.put(InitConstants.PROP_OSGI_USE_SYSTEM_PROPERTIES, "false");
configuration.put(InitConstants.PROP_OSGI_CONFIGURATION_AREA,
- stateArea.resolve(RuntimeManager.STATE).toUri().toString());
+ writableArea.resolve(RuntimeManager.STATE).toUri().toString());
// use config area if instance area is not set
if (!configuration.containsKey(InitConstants.PROP_OSGI_INSTANCE_AREA))
configuration.put(InitConstants.PROP_OSGI_INSTANCE_AREA,
- stateArea.resolve(RuntimeManager.DATA).toUri().toString());
+ writableArea.resolve(RuntimeManager.DATA).toUri().toString());
logger.log(Level.TRACE, () -> "Runtime manager configuration: " + configuration);
ThinLoggerFinder.reloadConfiguration();
logger.log(Logger.Level.DEBUG, () -> "Argeo Init starting with PID " + ProcessHandle.current().pid());
Map<String, String> env = System.getenv();
-// for (String envName : new TreeSet<>(env.keySet())) {
-// System.out.format("%s=%s%n", envName, env.get(envName));
-// }
- if (args.length < 1)
- throw new IllegalArgumentException("A relative configuration directory must be specified");
- Path configArea = Paths.get(System.getProperty("user.dir"), args[0]);
+
+// if (args.length < 1)
+// throw new IllegalArgumentException("A relative configuration directory must be specified");
+// Path configArea = Paths.get(System.getProperty("user.dir"), args[0]);
// System.out.println("## Start with PID " + ProcessHandle.current().pid());
// System.out.println("user.dir=" + System.getProperty("user.dir"));
- Path stateArea = Paths.get(env.get(ENV_STATE_DIRECTORY));
-
- RuntimeManagerMain runtimeManager = new RuntimeManagerMain(configArea, stateArea);
+ Path writableArea = Paths.get(env.get(ENV_STATE_DIRECTORY));
+ Path configArea = Paths.get(env.get(ENV_CONFIGURATION_DIRECTORY));
+ RuntimeManagerMain runtimeManager = new RuntimeManagerMain(configArea, writableArea);
runtimeManager.run();
}
--- /dev/null
+package org.argeo.init.osgi;
+
+import java.util.Optional;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.wiring.BundleWiring;
+
+/**
+ * A {@link ClassLoader} based on a {@link Bundle} from another OSGi runtime.
+ */
+class ForeignBundleClassLoader extends ClassLoader {// implements BundleReference {
+ private BundleContext localBundleContext;
+ private Bundle foreignBundle;
+
+ public ForeignBundleClassLoader(BundleContext localBundleContext, Bundle foreignBundle) {
+ super("Foreign bundle " + foreignBundle.toString(), Optional.ofNullable(foreignBundle.adapt(BundleWiring.class))
+ .map((bw) -> bw.getClassLoader()).orElse(null));
+ this.localBundleContext = localBundleContext;
+ this.foreignBundle = foreignBundle;
+ }
+
+// @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;
+// }
+
+}
--- /dev/null
+package org.argeo.init.osgi;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.connect.ConnectContent;
+
+/**
+ * A {@link ConnectContent} based on a {@link Bundle} from another OSGi runtime.
+ */
+class ForeignBundleConnectContent implements ConnectContent {
+ private final Bundle foreignBundle;
+ private final ClassLoader classLoader;
+
+ public ForeignBundleConnectContent(BundleContext localBundleContext, Bundle foreignBundle) {
+ this.foreignBundle = foreignBundle;
+ this.classLoader = new ForeignBundleClassLoader(localBundleContext, foreignBundle);
+ }
+
+ @Override
+ public Optional<Map<String, String>> getHeaders() {
+ Dictionary<String, String> dict = foreignBundle.getHeaders();
+ List<String> keys = Collections.list(dict.keys());
+ Map<String, String> dictCopy = keys.stream().collect(Collectors.toMap(Function.identity(), dict::get));
+ return Optional.of(dictCopy);
+ }
+
+ @Override
+ public Iterable<String> getEntries() throws IOException {
+ List<String> lst = Collections.list(foreignBundle.findEntries("", "*", true)).stream().map((u) -> u.getPath())
+ .toList();
+ return lst;
+ }
+
+ @Override
+ public Optional<ConnectEntry> getEntry(String path) {
+ URL u = foreignBundle.getEntry(path);
+ if (u == null) {
+ u = foreignBundle.getEntry("bin/" + path);
+ // System.err.println(u2);
+ }
+ if (u == null) {
+ if ("plugin.xml".equals(path))
+ return Optional.empty();
+ if (path.startsWith("META-INF/versions/"))
+ return Optional.empty();
+ System.err.println(foreignBundle.getSymbolicName() + " " + path + " not found");
+ return Optional.empty();
+ }
+ URL url = u;
+ ConnectEntry urlConnectEntry = new ConnectEntry() {
+
+ @Override
+ public String getName() {
+ return path;
+ }
+
+ @Override
+ public long getLastModified() {
+ return foreignBundle.getLastModified();
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ return url.openStream();
+ }
+
+ @Override
+ public long getContentLength() {
+ return -1;
+ }
+ };
+ return Optional.of(urlConnectEntry);
+ }
+
+ @Override
+ public Optional<ClassLoader> getClassLoader() {
+ ClassLoader cl;
+ // cl = bundle.adapt(BundleWiring.class).getClassLoader();
+
+ // cl = subFrameworkClassLoader;
+ cl = classLoader;
+ return Optional.of(cl);
+// return Optional.empty();
+ }
+
+ @Override
+ public void open() throws IOException {
+ }
+
+ @Override
+ public void close() throws IOException {
+
+ }
+
+}
--- /dev/null
+package org.argeo.init.osgi;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.connect.ConnectContent;
+import org.osgi.framework.connect.ConnectModule;
+import org.osgi.framework.connect.ModuleConnector;
+
+/**
+ * A {@link ModuleConnector} based on another OSGi runtime.
+ */
+class ForeignModuleConnector implements ModuleConnector {
+ private final BundleContext foreignBundleContext;
+ private final List<String> foreignCategories;
+
+ private BundleContext localBundleContext;
+
+ public ForeignModuleConnector(BundleContext foreignBundleContext, List<String> foreignCategories) {
+ this.foreignBundleContext = foreignBundleContext;
+ this.foreignCategories = foreignCategories;
+ }
+
+ @Override
+ public Optional<BundleActivator> newBundleActivator() {
+ return Optional.of(new BundleActivator() {
+ @Override
+ public void start(BundleContext context) throws Exception {
+ ForeignModuleConnector.this.localBundleContext = context;
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ ForeignModuleConnector.this.localBundleContext = null;
+ }
+
+ });
+ }
+
+ @Override
+ public void initialize(File storage, Map<String, String> configuration) {
+ }
+
+ @Override
+ public Optional<ConnectModule> connect(String location) throws BundleException {
+ // hacks
+ if (location.contains("org.eclipse.rap.rwt.osgi"))
+ return Optional.empty();
+
+ String category = categoryFromLocation(location);
+ if (category == null || !foreignCategories.contains(category))
+ return Optional.empty();
+ Bundle bundle = foreignBundleContext.getBundle(location);
+ if (bundle != null && bundle.getBundleId() != 0) {
+ // System.out.println("Foreign Bundle: " + bundle.getSymbolicName() + " " +
+ // location);
+ ConnectModule module = new ConnectModule() {
+
+ @Override
+ public ConnectContent getContent() throws IOException {
+ return new ForeignBundleConnectContent(localBundleContext, bundle);
+ }
+ };
+ return Optional.of(module);
+ }
+ return Optional.empty();
+ }
+
+ protected String categoryFromLocation(String location) {
+ // deal with Windows (sigh)
+ String regexp = File.separatorChar == '\\' ? "\\\\" : "/";
+ String[] arr = location.split(regexp);
+ if (arr.length < 2)
+ return null;
+ // TODO make it more robust
+ String category = arr[arr.length - 2];
+ return category;
+ }
+}
public void install() {
String osgiInstancePath = getProperty(InitConstants.PROP_OSGI_INSTANCE_AREA);
String osgiConfigurationPath = getProperty(InitConstants.PROP_OSGI_CONFIGURATION_AREA);
- String osgiSharedConfigurationPath = getProperty(InitConstants.PROP_OSGI_CONFIGURATION_AREA);
+ String osgiSharedConfigurationPath = getProperty(InitConstants.PROP_OSGI_SHARED_CONFIGURATION_AREA);
logger.log(DEBUG, () -> "OSGi bootstrap starting" //
+ (osgiInstancePath != null ? " data: " + osgiInstancePath + "" : "") //
+ (osgiConfigurationPath != null ? " state: " + osgiConfigurationPath + "" : "") //
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.lang.System.LoggerFinder;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Hashtable;
+import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.concurrent.Flow;
import java.util.function.Consumer;
import java.util.function.Supplier;
+import org.argeo.api.init.InitConstants;
import org.argeo.api.init.RuntimeContext;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
private final static long STOP_FOR_UPDATE_TIMEOUT = 60 * 1000;
private final static long CLOSE_TIMEOUT = 60 * 1000;
- // private final static String SYMBOLIC_NAME_FELIX_SCR = "org.apache.felix.scr";
-
- private ConnectFrameworkFactory frameworkFactory;
+ private final ConnectFrameworkFactory frameworkFactory;
private Map<String, String> config;
private Framework framework;
-// private OsgiBoot osgiBoot;
+
+ // Nested runtimes
+// private final BundleContext foreignBundleContext;
+ // private final List<String> foreignCategories;
+
+ private final ForeignModuleConnector moduleConnector;
/**
* Constructor to use when the runtime context will create the OSGi
* {@link Framework}.
*/
public OsgiRuntimeContext(ConnectFrameworkFactory frameworkFactory, Map<String, String> config) {
+ this(frameworkFactory, config, null, null);
+ }
+
+ /**
+ * Constructor to use when the runtime context will create the OSGi
+ * {@link Framework} and will potentially have a parent runtime.
+ */
+ OsgiRuntimeContext(ConnectFrameworkFactory frameworkFactory, Map<String, String> config,
+ BundleContext foreignBundleContext, List<String> foreignCategories) {
this.frameworkFactory = frameworkFactory;
this.config = config;
+ if (foreignCategories != null) {
+// String parentCategories = config.get(InitConstants.PROP_ARGEO_OSGI_PARENT_CATEGORIES);
+// if (parentCategories != null) {
+// Objects.requireNonNull(foreignBundleContext, "Foreign bundle context");
+// List<String> foreignCategories = Arrays.asList(parentCategories.trim().split(","));
+// foreignCategories.removeIf((s) -> "".equals(s));
+ this.moduleConnector = new ForeignModuleConnector(foreignBundleContext, foreignCategories);
+ } else {
+ this.moduleConnector = null;
+ }
}
/**
* means.
*/
OsgiRuntimeContext(BundleContext bundleContext) {
+ this.frameworkFactory = null;
+ // TODO try nesting within Eclipse PDE
+ this.moduleConnector = null;
start(bundleContext);
}
// if (opt.isEmpty())
// throw new IllegalStateException("Cannot find OSGi framework");
// framework = opt.get().newFramework(config);
- framework = frameworkFactory.newFramework(config, null);
+ framework = frameworkFactory.newFramework(config, moduleConnector);
}
try {
OsgiBoot osgiBoot = new OsgiBoot(bundleContext);
String frameworkUuuid = bundleContext.getProperty(Constants.FRAMEWORK_UUID);
+// osgiBoot.bootstrap();
+
// separate thread in order to improve logging
Thread osgiBootThread = new Thread("OSGi boot framework " + frameworkUuuid) {
@Override
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.net.URI;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.TreeMap;
import java.util.function.Consumer;
private final static long RUNTIME_SHUTDOWN_TIMEOUT = 60 * 1000;
+// private Path ownConfigArea;
+
private Path baseConfigArea;
private Path baseWritableArea;
- private Map<String, String> configuration = new HashMap<>();
-
- private Map<String, OsgiRuntimeContext> runtimeContexts = new TreeMap<>();
+// private Map<String, String> configuration = new HashMap<>();
private ConnectFrameworkFactory frameworkFactory;
+ private final BundleContext bundleContext;
+
+ private Map<String, OsgiRuntimeContext> runtimeContexts = new TreeMap<>();
+
OsgiRuntimeManager(BundleContext bundleContext) {
+ Objects.requireNonNull(bundleContext);
+ this.bundleContext = bundleContext;
frameworkFactory = OsgiRuntimeContext.getFrameworkFactory(bundleContext);
this.baseConfigArea = Paths
- .get(URI.create(bundleContext.getProperty(InitConstants.PROP_OSGI_SHARED_CONFIGURATION_AREA)))
- .getParent();
+ .get(URI.create(bundleContext.getProperty(InitConstants.PROP_OSGI_SHARED_CONFIGURATION_AREA)));
+// this.baseConfigArea = ownConfigArea.getParent();
this.baseWritableArea = Paths
- .get(URI.create(bundleContext.getProperty(InitConstants.PROP_OSGI_CONFIGURATION_AREA))).getParent()
- .getParent();
+ .get(URI.create(bundleContext.getProperty(InitConstants.PROP_OSGI_CONFIGURATION_AREA))).getParent();
- logger.log(Level.TRACE, () -> "Runtime manager configuration: " + configuration);
+// logger.log(Level.TRACE, () -> "Runtime manager configuration: " + configuration);
// System.out.println("java.library.path=" + System.getProperty("java.library.path"));
}
protected void shutdown() {
- // shutdowm runtimes
+ // shutdown runtimes
Map<String, RuntimeContext> shutdowning = new HashMap<>(runtimeContexts);
for (String id : new HashSet<>(runtimeContexts.keySet())) {
logger.log(Logger.Level.DEBUG, "Shutting down runtime " + id + " ...");
OsgiRuntimeContext loadRuntime(String relPath, Consumer<Map<String, String>> configCallback) {
closeRuntime(relPath, false);
+
+ BundleContext foreignBundleContext = bundleContext;
+
+ Path parentRelPath = Paths.get(relPath).getParent();
+ if (parentRelPath != null && Files.exists(baseConfigArea.resolve(parentRelPath))) {
+ if (!runtimeContexts.containsKey(parentRelPath.toString())) {
+
+ String exportCategories = bundleContext.getProperty(InitConstants.PROP_ARGEO_OSGI_EXPORT_CATEGORIES);
+ List<String> foreignCategories = exportCategories == null ? new ArrayList<>()
+ : Arrays.asList(exportCategories.trim().split(","));
+ Path writableArea = baseWritableArea.resolve(parentRelPath);
+ Path configArea = baseConfigArea.resolve(parentRelPath);
+ Map<String, String> config = new HashMap<>();
+ RuntimeManager.loadDefaults(config);
+ config.put(InitConstants.PROP_OSGI_USE_SYSTEM_PROPERTIES, "false");
+ config.put(InitConstants.PROP_OSGI_CONFIGURATION_AREA, writableArea.resolve(STATE).toUri().toString());
+ config.put(InitConstants.PROP_OSGI_SHARED_CONFIGURATION_AREA, configArea.toUri().toString());
+ config.put(InitConstants.PROP_OSGI_SHARED_CONFIGURATION_AREA_RO, "true");
+ OsgiRuntimeContext runtimeContext = new OsgiRuntimeContext(frameworkFactory, config,
+ foreignBundleContext, foreignCategories);
+ runtimeContexts.put(parentRelPath.toString(), runtimeContext);
+ runtimeContext.run();
+ // FIXME properly stage installation
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ // silent
+ }
+ }
+ OsgiRuntimeContext parentRuntimeContext = runtimeContexts.get(parentRelPath.toString());
+ foreignBundleContext = parentRuntimeContext.getFramework().getBundleContext();
+ }
+
Path writableArea = baseWritableArea.resolve(relPath);
- Path configArea = baseConfigArea.resolve(relPath);
+ Path configArea = baseConfigArea.resolve(relPath).getParent().resolve(SHARED);
Map<String, String> config = new HashMap<>();
- RuntimeManager.loadConfig(configArea, config);
+// RuntimeManager.loadConfig(configArea, config);
+ RuntimeManager.loadDefaults(config);
+ config.put(InitConstants.PROP_OSGI_USE_SYSTEM_PROPERTIES, "false");
config.put(InitConstants.PROP_OSGI_CONFIGURATION_AREA, writableArea.resolve(STATE).toUri().toString());
+ config.put(InitConstants.PROP_OSGI_SHARED_CONFIGURATION_AREA, configArea.toUri().toString());
+ config.put(InitConstants.PROP_OSGI_SHARED_CONFIGURATION_AREA_RO, "true");
if (configCallback != null)
configCallback.accept(config);
config.put(InitConstants.PROP_OSGI_INSTANCE_AREA, writableArea.resolve(DATA).toUri().toString());
// create framework
-// Framework framework = frameworkFactory.newFramework(config, null);
-// try {
-// framework.start();
-// } catch (BundleException e) {
-// throw new IllegalStateException("Cannot initialise framework", e);
-// }
- OsgiRuntimeContext runtimeContext = new OsgiRuntimeContext(frameworkFactory, config);
+ String exportCategories = bundleContext.getProperty(InitConstants.PROP_ARGEO_OSGI_EXPORT_CATEGORIES);
+ List<String> foreignCategories = exportCategories == null ? new ArrayList<>()
+ : Arrays.asList(exportCategories.trim().split(","));
+ OsgiRuntimeContext runtimeContext = new OsgiRuntimeContext(frameworkFactory, config, foreignBundleContext,
+ foreignCategories);
runtimeContexts.put(relPath, runtimeContext);
return runtimeContext;
}
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;
import org.argeo.api.init.InitConstants;
import org.argeo.api.init.RuntimeManager;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleException;
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;
import org.osgi.framework.connect.ModuleConnector;
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());
// config.put("osgi.parentClassLoader", "app");
// config.put("osgi.contextClassLoaderParent", "app");
- ModuleConnector moduleConnector = new ParentBundleModuleConnector(foreignBundleContext);
+ ModuleConnector moduleConnector = new ForeignModuleConnector(foreignBundleContext, null);
// URL frameworkUrl = URI.create(bundleContext.getProperty("osgi.framework")).toURL();
// URLClassLoader frameworkClassLoader = new URLClassLoader(new URL[] { frameworkUrl, });
frameworkFactory = null;
}
- static class ParentBundleModuleConnector implements ModuleConnector {
- private final BundleContext foreignBundleContext;
- private BundleContext localBundleContext;
-
- public ParentBundleModuleConnector(BundleContext foreignBundleContext) {
- this.foreignBundleContext = foreignBundleContext;
- }
-
- @Override
- public Optional<BundleActivator> newBundleActivator() {
- return Optional.of(new BundleActivator() {
- @Override
- public void start(BundleContext context) throws Exception {
- ParentBundleModuleConnector.this.localBundleContext = context;
- }
-
- @Override
- public void stop(BundleContext context) throws Exception {
- ParentBundleModuleConnector.this.localBundleContext = null;
- }
-
- });
- }
-
- @Override
- public void initialize(File storage, Map<String, String> configuration) {
- }
-
- @Override
- public Optional<ConnectModule> connect(String location) throws BundleException {
- Bundle bundle = foreignBundleContext.getBundle(location);
- if (bundle != null && bundle.getBundleId() != 0) {
- // System.out.println("Foreign Bundle: " + bundle.getSymbolicName() + " " +
- // location);
- ConnectModule module = new ConnectModule() {
-
- @Override
- public ConnectContent getContent() throws IOException {
- return new ForeignBundleConnectContent(localBundleContext, bundle);
- }
- };
- return Optional.of(module);
- }
- return Optional.empty();
- }
- }
-
- static class ForeignBundleClassLoader extends ClassLoader {// implements BundleReference {
- private BundleContext localBundleContext;
- private Bundle foreignBundle;
-
- public ForeignBundleClassLoader(BundleContext localBundleContext, Bundle foreignBundle) {
- super("Foreign bundle " + foreignBundle.toString(), Optional
- .ofNullable(foreignBundle.adapt(BundleWiring.class)).map((bw) -> bw.getClassLoader()).orElse(null));
- this.localBundleContext = localBundleContext;
- this.foreignBundle = foreignBundle;
- }
-
-// @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;
-// }
-
- }
-
- static class ForeignBundleConnectContent implements ConnectContent {
- private final Bundle foreignBundle;
- private final ClassLoader classLoader;
-
- public ForeignBundleConnectContent(BundleContext localBundleContext, Bundle foreignBundle) {
- this.foreignBundle = foreignBundle;
- this.classLoader = new ForeignBundleClassLoader(localBundleContext, foreignBundle);
- }
-
- @Override
- public Optional<Map<String, String>> getHeaders() {
- Dictionary<String, String> dict = foreignBundle.getHeaders();
- List<String> keys = Collections.list(dict.keys());
- Map<String, String> dictCopy = keys.stream().collect(Collectors.toMap(Function.identity(), dict::get));
- return Optional.of(dictCopy);
- }
-
- @Override
- public Iterable<String> getEntries() throws IOException {
- List<String> lst = Collections.list(foreignBundle.findEntries("", "*", true)).stream()
- .map((u) -> u.getPath()).toList();
- return lst;
- }
-
- @Override
- public Optional<ConnectEntry> getEntry(String path) {
- URL u = foreignBundle.getEntry(path);
- if (u == null) {
- u = foreignBundle.getEntry("bin/" + path);
- // System.err.println(u2);
- }
- if (u == null) {
- if ("plugin.xml".equals(path))
- return Optional.empty();
- if (path.startsWith("META-INF/versions/"))
- return Optional.empty();
- System.err.println(foreignBundle.getSymbolicName() + " " + path + " not found");
- return Optional.empty();
- }
- URL url = u;
- ConnectEntry urlConnectEntry = new ConnectEntry() {
-
- @Override
- public String getName() {
- return path;
- }
-
- @Override
- public long getLastModified() {
- return foreignBundle.getLastModified();
- }
-
- @Override
- public InputStream getInputStream() throws IOException {
- return url.openStream();
- }
-
- @Override
- public long getContentLength() {
- return -1;
- }
- };
- return Optional.of(urlConnectEntry);
- }
-
- @Override
- public Optional<ClassLoader> getClassLoader() {
- ClassLoader cl;
- // cl = bundle.adapt(BundleWiring.class).getClassLoader();
-
- // cl = subFrameworkClassLoader;
- cl = classLoader;
- return Optional.of(cl);
-// return Optional.empty();
- }
-
- @Override
- public void open() throws IOException {
- }
-
- @Override
- public void close() throws IOException {
-
- }
-
- }
}