4

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?

RubyTuesdayDONO
  • 2,350
  • 2
  • 25
  • 37

2 Answers2

3

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?

Neil Bartlett
  • 23,743
  • 4
  • 44
  • 77
  • The `tsvDataFormat` bean is exported as an OSGI service from another, separate bundle using Blueprint, so the bytecode isn't directly referenced in any of my project classes for this bundle, which imports the reference using Spring-OSGI. I apologize for not clarifying this initially. However, I'm not directly interested in the interface, except to the extent that Spring needs it to plug the bean into my Apache Camel routes. Does OSGI not automagically/dynamically import the interface class(es) of referenced services? – RubyTuesdayDONO Jan 25 '12 at 16:39
  • @RubyTuesdayDONO I'm not talking about the bytecode for the implementation of the service, which as you say is in another bundle and shouldn't be imported. I'm talking about the *interface*, which would be referenced by any classes in your bundle. If you're not referencing the service anywhere then it means you have no code that actually calls the service at all, so I'm still not clear why you even need the reference. – Neil Bartlett Jan 25 '12 at 18:48
  • @RubyTuesdayDONO Regarding your other question: no, OSGi never automatically imports anything (well, there's one exception: the `java.*` packages from the JRE are always visible). You have to import every package that you want to have visibility of, including service interfaces. Normally the import would be added automatically by bnd because it would see the reference to that interface from your code. – Neil Bartlett Jan 25 '12 at 18:51
  • thanks, @Neil. I might have become a little lazy with Spring's IoC / dependency injection. I may not have any java sources that reference the service interfaces, but Spring still needs them. I also glanced at the source code for Maven/Felix Bundle plugin, as well as Bnd's SpringXMLType http://grepcode.com/file/repo1.maven.org/maven2/biz.aQute/bndlib/1.50.0/aQute/lib/spring/SpringXMLType.java. My Spring files are in the standard META-INF/spring/*.xml location, but maybe my Spring-Context instruction to Bnd should only have the directory and not the *.xml glob. I'll post test results soon. – RubyTuesdayDONO Jan 25 '12 at 20:12
  • my overridden Spring-Context header-instruction was the culprit! when I removed the *.xml glob at the end, Bnd automagically introspected my application context markup and imported the interfaces. I should probably remove the explicit instructions since my markups are in standard location, anyway. also, depending on whether globs adhere to Spring's standard, maybe Bnd could be improved to interpret them more liberally. – RubyTuesdayDONO Jan 25 '12 at 22:14
  • @RubyTuesdayDONO Glad to hear you got it fixed. I agree that bnd could be improved here. Can you raise an issue in the bnd issue tracker? https://github.com/bndtools/bnd/issues – Neil Bartlett Jan 25 '12 at 22:20
1

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>
RubyTuesdayDONO
  • 2,350
  • 2
  • 25
  • 37