Skip to content
This repository has been archived by the owner on Nov 15, 2022. It is now read-only.

BundleContext.getBundle(String) not consistent with BundleContext.getBundles() #83

Open
pdolezal opened this issue Dec 23, 2019 · 3 comments

Comments

@pdolezal
Copy link

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.

@tmarkwardt
Copy link
Contributor

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

@pdolezal
Copy link
Author

pdolezal commented Jan 7, 2020

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.

@tmarkwardt
Copy link
Contributor

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.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants