1

I am quite a newbie with OSGi and struggeling with including bundles into my Maven project.

I created an API bundle and an implementation bundle using the mave-bundle-plugin. In my major project (a Maven project), I tried to get the service of the implemented bundle from a ServiceTracker using the Felix Framework. When I finally try to cast the obtained service into the correct type, I receive a ClassCastException.

API Maven project:

public interface ParserTestService {
    String validForStage();
}

API POM:

<artifactId>ParserTest</artifactId>
<version>0.0.2-SNAPSHOT</version>
<packaging>bundle</packaging>

<build>
    <plugins>
        <plugin> 
            <groupId>org.apache.felix</groupId>
            <artifactId>maven-bundle-plugin</artifactId>
            <extensions>true</extensions>
            <configuration>
                <instructions>
                    <Bundle-SymbolicName>${pom.groupId}.${pom.artifactId}</Bundle-SymbolicName>
                    <Bundle-Vendor>Apache-Felix</Bundle-Vendor>
                    <Bundle-Version>0.0.1</Bundle-Version>
                    <Export-Service>de.ParserTestService</Export-Service>    
                </instructions>
            </configuration>
        </plugin>
    </plugins>
</build>

<dependencies>
    <dependency>
        <groupId>org.apache.felix</groupId>
        <artifactId>org.apache.felix.framework</artifactId>
        <version>2.0.4</version>
    </dependency>
</dependencies>

Impl Maven project:

public class ParserImplService implements ParserTestService {
    public String validForStage() {
        return "S";
    }
}

Impl POM:

<artifactId>ParserTestVersion1</artifactId>
<version>0.0.2-SNAPSHOT</version>
<packaging>bundle</packaging>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.felix</groupId>
            <artifactId>maven-bundle-plugin</artifactId>
            <extensions>true</extensions>
            <configuration>
                <instructions>
                    <Bundle-SymbolicName>${pom.groupId}.${pom.artifactId}</Bundle-SymbolicName>
                    <Bundle-Vendor>Apache-Felix</Bundle-Vendor>
                    <Bundle-Version>0.0.1</Bundle-Version>
                    <Bundle-Activator>de.Activator</Bundle-Activator>
                    <Export-Package>de</Export-Package>
                    <Export-Service>de.ParserImplService</Export-Service>
                    <Import-Package>org.osgi.framework</Import-Package>
                </instructions>
            </configuration>
        </plugin>
    </plugins>
</build>

<dependencies>
    <dependency>
        <groupId>org.apache.felix</groupId>
        <artifactId>org.apache.felix.framework</artifactId>
        <version>2.0.4</version>
        <type>bundle</type>
    </dependency>
    <dependency>
        <groupId>de</groupId>
        <artifactId>ParserTest</artifactId>
        <version>0.0.2-SNAPSHOT</version>
        <type>bundle</type>
    </dependency>
</dependencies>

Maven created two jar-files with the following Manifest files:

Manifest-Version: 1.0
Bnd-LastModified: 1340890655296
Build-Jdk: 1.6.0_24
Built-By: br_s1
Bundle-ManifestVersion: 2
Bundle-Name: Parser Test Interface
Bundle-SymbolicName: de.ParserTest
Bundle-Vendor: Apache-Felix
Bundle-Version: 0.0.1
Created-By: Apache Maven Bundle Plugin
Export-Package: de;version="0.0.1"
Export-Service: de.ParserTestService
Tool: Bnd-1.50.0

Manifest-Version: 1.0
Bnd-LastModified: 1340890661890
Build-Jdk: 1.6.0_24
Built-By: br_s1
Bundle-Activator: de.Activator
Bundle-ManifestVersion: 2
Bundle-Name: Parser Test
Bundle-SymbolicName: de.ParserTestVersion1
Bundle-Vendor: Apache-Felix
Bundle-Version: 0.0.1
Created-By: Apache Maven Bundle Plugin
Export-Package: de;version="0.0.1"
Export-Service: de.ParserImplService
Import-Package: org.osgi.framework;version="[1.5,2)"
Tool: Bnd-1.50.0

In my Major Maven project, I setup and started a Felix framework. In the subfolder "bundles", I copied the jar-files created by the Maven deployment process above. In the install-routine, I tried to install these bundles and start them:

public class HostActivator implements BundleActivator {
...
    public BundleContext getContext()
    {
        return m_context;
    }
...
}

public class HostApplication
{
    private HostActivator m_activator = null;
    private Felix m_felix = null;
    private ServiceTracker m_tracker = null;

    public HostApplication()
    {
        Map<String, Object> configMap = new HashMap<String, Object>();
        configMap.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, "de.test; version=1.0.0");
        m_activator = new HostActivator();
        List<BundleActivator> list = new ArrayList<BundleActivator>();
        list.add(m_activator);
        configMap.put(FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP, list);

        m_felix = new Felix(configMap);
        m_felix.start();

        m_tracker = new ServiceTracker(m_activator.getContext(), ParserTestService.class.getName(), null);
        m_tracker.open();
    }

    public void installBundlesFromDir() {

        File dir = new File("bundles");
        for (File f : dir.listFiles()) {
            try {
                Bundle b = m_felix.getBundleContext().installBundle("file:"+f.getAbsolutePath());
                b.start();

                ServiceReference sr = m_felix.getBundleContext().getServiceReference(ParserTestService.class.getName());

                ParserTestService service = (ParserTestService) m_felix.getBundleContext().getService(sr);

            } catch (BundleException e) {
                System.err.println("could not load bundle "+f.getAbsolutePath());
            }
        }        

    }
}

The corresponding POM:

<artifactId>parserUse</artifactId>
<version>0.0.1-SNAPSHOT</version>

<dependencies>
    <dependency>
        <groupId>org.apache.felix</groupId>
        <artifactId>org.apache.felix.framework</artifactId>
        <version>2.0.4</version>
    </dependency>            
    <dependency>
        <groupId>de</groupId>
        <artifactId>ParserTestVersion1</artifactId>
        <version>0.0.2-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>de</groupId>
        <artifactId>ParserTest</artifactId>
        <version>0.0.2-SNAPSHOT</version>
    </dependency>
</dependencies>

When I try to call the install routine, however, I receive a ClassCastException:

java.lang.ClassCastException: de.ParserImplService cannot be cast to de.ParserTestService

I already checked the class loaders

ParserTestService.class.getClassLoader()

and

(m_felix.getBundleContext().getService(sr)).getClass().getClassLoader()

and they are different from each other.

I always have thought OSGi would take care of the class loading for me.

Does anyone has a clue what I am doing wrong? Any help is appreciated!

Thanks in advance, Sebastian

user1488793
  • 284
  • 2
  • 14

2 Answers2

2

I finally found a way to solve this problem, but only by avoiding Felix and using Equinox instead.

1.) The API exports only the package <Export-Package>de</Export-Package>

2.) The Impl exports nothing. It only defines the activator and its imports

<Bundle-Activator>de.Activator</Bundle-Activator>
<Import-Package>org.osgi.framework;de</Import-Package>

3.) I replaced the whole HostApplication using Equinox:

public class HostApplication {
    private BundleContext bundleContext;

    public HostApplication(String profileName) {
        BundleContext bc = null;
        Properties frameworkProperties = readCustomProfile(profileName);

        frameworkProperties.put("osgi.clean", "true");
        frameworkProperties.put("osgi.console", "true");

        Map<String, String> frameworkPropertiesMap = new HashMap<String, String>();
        for (Object o : frameworkProperties.keySet()) {
            frameworkPropertiesMap.put((String) o,
                    (String) frameworkProperties.getProperty((String) o));
        }

        EclipseStarter.setInitialProperties(frameworkPropertiesMap);
        bc = EclipseStarter.startup(new String[] { "-console", "-dev", "bin" },    null);
    }

   public boolean containsIegm(String stage, byte[] msg) {

        try {
            ServiceReference serviceReference = bundleContext.getServiceReference(ParserTest.class.getName());
            return ((ParserImplService) bundleContext.getService(serviceReference)).containsIegm(msg);

        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return false;
    }

    public void installBundlesFromDir() {

        File dir = new File("bundles");
        int i = 0;
        for (File f : dir.listFiles()) {
            try {
                Bundle b = bundleContext.installBundle("file:"+ f.getAbsolutePath());
                b.start();
            } catch (BundleException e) {
                System.err.println("could not load bundle "    + f.getAbsolutePath());
            }
        }

    }
}

I tried exactly the same with Felix by using the BundleContext of m_felix (see question) and removed the HostActivator completly (I know realize it is not necessary in my app). However, I could not make it work.

Anyways, with Equinox it is equally easy to embedd an OSGi framework in non-OGSi/non-bundle applications.

Thanks everyone for their help! Sebastian

user1488793
  • 284
  • 2
  • 14
  • 3
    To clarify there is absolutely no reason for Felix and Equinox to differ here. I suspect that there is a FAR simpler way to achieve what you were trying to do, if you would only step back from the details and describe your goals in more general terms. – Neil Bartlett Jun 29 '12 at 13:54
1

Both bundles are exporting "de". This means that there will be two name spaces called "de".
The bundle Bundle-SymbolicName: de.ParserTestVersion1 should import "de" and get it from the other bundle

David Zimmerman
  • 335
  • 4
  • 7
  • 1
    In addition, the creation of the `ServiceTracker` uses the wrong `BundleContext`... it looks like the publishing bundle's context was used (though the setup is *very* confusing). As a result, the normal compatibility checks are subverted. Also, if the outer bundle has visibility of the implementation classes inside the inner bundle (such as the HostActivator), then why even bother with a service? Why not just direct instantiation? – Neil Bartlett Jun 28 '12 at 19:37
  • @David: I updated the exports and imports of the bundles and still get the ClassCastException. I suppose this has not been the cause of my problem. – user1488793 Jun 29 '12 at 07:17
  • @Neil: I do not fully understand your answer. Since I am new with OSGi and start to think I took a wrong path, maybe I should clarify what I want to do: My host application should be able to do two things. 1.) It should be able to install and start services from a directory 2.) It should be able to invoke these services. – user1488793 Jun 29 '12 at 07:20
  • @Neil: ... What other option do I have for a BundleContext. In the constructor of my app, I have only the HostActivator as context. – user1488793 Jun 29 '12 at 07:27
  • My general problem is that all tutorials on this stuff are using the console to start a bundle and all applications that use the provided services are also started in the osgi console. So I wondered whether it is possible to have a normal application where you start your osgi framework, load bundles from a folder, and use the services at the same time?? – user1488793 Jun 29 '12 at 07:29
  • @user1488793 You are embedding OSGi so the bundle context you need to use is the one of Felix itself, i.e. the system bundle. However you haven't used the standard API for embedding so I can't tell you how to get that context. Did you read my embedding tutorial? http://njbartlett.name/2011/03/07/embedding-osgi.html – Neil Bartlett Jun 29 '12 at 08:19