concierge
concierge copied to clipboard
BundleContext.getBundle(String) not consistent with BundleContext.getBundles()
When I tried Concierge with own launcher I found out that BundleContext.getBundle(String)
does not work correctly when the framework uses an existing storage.
Basically the scenario follows this pseudocode:
Framework framework = factory.newFramework(properties); // Here the path to the storage is set
framework.init();
BundleContext systemBundle = framework.getBundleContext();
Bundle bundle = systemBundle.getBundle(location);
if (bundle == null) {
try (InputStream is = open(location)) {
bundle = systemBundle.installBundle(location, is);
}
}
The bundle
variable is always null
, while it should return the installed bundle when the code is run second time with the same storage. I tried replacing getBundle
with something like:
Bundle bundle = Stream.of(systemBundle.getBundles())
.filter(b -> location.equals(b.getLocation())
.findAny()
.orElse(null);
And this always works. So the framework apparently knows which bundles were installed, it just does not update its structures for getBundle
to work properly.
The described behavior of getBundle
in this use case results in duplicated installation of the bundle – and what is even more interesting: the newly installed bundle has the same location as the existing bundle. I believe this is wrong as well and the location should be unique. Maybe the framework implementation fails to check the location duplicity for the same reason.
Hi,
i don't reproduce this problem. When the bundle is installed, then i can found this bundle with getBundle( String)
and with getBundles()
and search on array ...
give us a code to reproduce.
Regards Torsten
I extracted some code to reproduce the issue, or actually a part of it. There is probably yet something else in my original code that contributed to the described difference between getBundles
and getBundle
and I'll have to investigate it more thoroughly to isolate it.
However, the code sample below shows a part of the problem, which might be the root cause for the described difference: the framework forgets which bundles were installed in the previous run and therefore allows repeated installations of the same bundle, so that the storage area grows with each run.
package org.example.bug;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.stream.Stream;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.launch.Framework;
import org.osgi.framework.launch.FrameworkFactory;
public final class Main {
public static void main(String... args) throws Exception {
final String location = args[0];
final FrameworkFactory factory = ServiceLoader.load(FrameworkFactory.class).iterator().next();
final Map<String, String> properties = Collections.singletonMap(Constants.FRAMEWORK_STORAGE, "./storage");
final Framework framework = factory.newFramework(properties);
framework.init();
final BundleContext systemBundle = framework.getBundleContext();
if ((systemBundle.getBundle(location) == null)) {
System.out.println("Installing: " + location);
try (InputStream is = Files.newInputStream(Paths.get(location))) {
final Bundle bundle = systemBundle.installBundle(location, is);
System.out.println("Installed: " + bundle);
}
}
Stream.of(systemBundle.getBundles()).forEach(bundle -> System.out.format("%d: %s%n", bundle.getBundleId(), bundle));
framework.stop();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>bug</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<java.version>1.8</java.version>
<java.compiler.source>${java.version}</java.compiler.source>
<java.compiler.target>${java.version}</java.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>osgi.core</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.concierge</groupId>
<artifactId>org.eclipse.concierge</artifactId>
<version>5.1.0</version>
<scope>runtime</scope>
</dependency>
<!--
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.framework</artifactId>
<version>6.0.3</version>
<scope>runtime</scope>
</dependency>
-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.compiler.source}</source>
<target>${java.compiler.target}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Execute several times mvn exec:java -Dexec.mainClass=org.example.bug.Main -Dexec.args=some-bundle.jar
(where some-bundle.jar
points to an actual bundle). IMHO, the more appropriate behavior shows Felix (just swap the framework implementation in the POM as hinted) which does not install the bundle again.
I wonder if it is by design and the storage area should be then cleared for Concierge always to prevent growing it.
Ok ... i think understand the problem. you want an installed bundle to persist across the life cycle of the framework. when concierge started, concierge don't know which bundles existed on last run. And every new start from framework without clean storage added a new storage folder for the install bundle. persistent data don't store on temporary storage, better the bundle define a persistent storage location outside from the storage.
to implement this functionaly please implement a own startup.