Spring OSGI service reference interfaces must be explicitly imported by consuming bundle?

3.5k views Asked by At

I'm getting acquainted with Spring OSGI and Blueprint, but am having "classpath" difficulties (like many newbies).

I have two OSGI bundles - one that defines various beans (using Blueprint, not that it should matter) and exports them as services; and another bundle that references the service beans (using Spring OSGI) and plugs them into some Apache Camel routes.

The service-provider bundle's Blueprint looks something like this:

<service id="camelTsvDataFormat" 
    interface="org.apache.camel.spi.DataFormat"> 
    <bean class="org.apache.camel.component.flatpack.FlatpackDataFormat"/> 
</service> 

The service-consumer bundle's Spring context looks something like this:

<osgi:reference id="tsvDataFormat" 
    interface="org.apache.camel.spi.DataFormat" /> 

<camel:camelContext> 
    <route> 
        <from uri="vm:in"> 
        <setBody> 
            <constant>SELECT * FROM myTable</constant> 
        </setBody> 
        <to uri="jdbc:myDataSource" /> 
        <marshal ref="tsvDataFormat" /> 
        <to uri="file:/path/to/my/files/?fileName=out.tsv" /> 
    </route> 
</camel:camelContext> 

… But upon deployment, Spring "Cannot find class [org.apache.camel.spi.DataFormat]". I can add the interface to the Import-Package section of my Bnd instructions, but it seems redundant to have to manually list the class twice in separate locations.

An alternate choice is to extend the interface within my own project so Bnd will automatically pick it up, but this is approximately as much trouble.

I guess I'm expecting Spring to lookup services by interface name without having to actually resolve the interface class. Is this naïve? Or is there a way to have Bnd automatically import interfaces in my appContext's service references? If Bnd can do this (e.g. using plugins), is there a standard way to use the Bnd plugins with the Apache Felix bundle plugin for Maven?

2

There are 2 answers

6
Neil Bartlett On BEST ANSWER

As Holly suggests, bnd would normally find this package referenced from any bytecode inside your bundle that invokes it. It should also introspect Spring-DM XML files if they are in the right location. However I don't know if it yet supports Blueprint XML files in the same way, because they are not in the same bundle location. So it might be necessary to upgrade your version of bnd or use a plugin that supports Blueprint.

However, I'm suspicious of this whole thing. If there are no bytecode references to the interface, then it seems you're not even using the service reference? In that case, why not just remove it?

0
RubyTuesdayDONO On

As @Neil Bartlett indicated, Bnd should introspect Spring and Blueprint files in standard location within the bundle (META-INF/spring and OSGI-INF/blueprint). I had manually overridden these as META-INF/spring/*.xml and OSGI-INF/blueprint/*.xml in my POM. I thought this was fine since the Spring and Blueprint extenders in my OSGI platform accepted the headers and bootstraped the respective containers. Bnd, however, appears to expect a simpler header without globs (see SpringXMLType.java). I don't mean to assign fault since it's an amazing tool, but this one caught me off-guard.

Anyway, since my Spring and Blueprint markups are already in standard location, I just removed the redundant Bnd instructions from my POM, and all the Spring-DM service reference interfaces were automagically picked up and Import-Package'd into my bundle:

<plugin>
    <groupId>org.apache.felix</groupId>
    <artifactId>maven-bundle-plugin</artifactId>
    <version>2.3.6</version>
    <extensions>true</extensions>
    <configuration>
        <instructions>
            <Bundle-Version>${project.version}.${buildNumber}</Bundle-Version>
            <Bundle-Activator>com.example.BundleActivator</Bundle-Activator>
            <!-- 
                <Spring-Context>META-INF/spring/*.xml</Spring-Context> 
                <Bundle-Blueprint>OSGI-INF/blueprint*.xml</Bundle-Blueprint> 
            -->
        </instructions>
    </configuration>
</plugin>