Wednesday, July 15, 2009

Publishing Scala, Groovy and Java OSGi Bundles with Spring DM

In Part 1 of this series, we used Maven and PAX to set up and run an OSGi project with three hello world bundles; one for Java, one for Groovy and one for Scala. This time around, I'd like to show you how to incorporate Spring Dynamic Modules so we can build a Spring application that is deployed in an OSGi framework. Spring DM will help maintain a clean separation of our bundles in addition to abstracting away the plumbing normally required for developing OSGi applications.

There have been minor releases for several of the components highlighted in this tutorial including Pax Runner and Scala since the last post. In the interest of consistency, I'm not going to upgrade anything at this point. The only new component we're adding is version 1.2.0 of Spring DM.

If you'd prefer to look at the code as you go, you can grab it from bitbucket. Be sure to update your working copy to revision 1 to see the code as it would look at the completion of this second article in the series.

        hg clone http://bitbucket.org/brimurph/helloworld

        cd helloworld

        hg update -r 1
    

Spring Dynamic Modules for OSGi

Using Spring DM implies that we're using Spring framework JARs as OSGi bundles as well as these OSGi Spring bundles: spring-osgi-extender, spring-osgi-core, spring-osgi-io and spring-osgi-annotation.

The first thing we need to do is update our maven configuration to include the SpringSource Enterprise Bundle Repository.

        mvn pax:add-repository -DrepositoryId=com.springsource.repository.bundles.release -DrepositoryURL=http://repository.springsource.com/maven/bundles/release
        
        mvn pax:add-repository -DrepositoryId=com.springsource.repository.bundles.external -DrepositoryURL=http://repository.springsource.com/maven/bundles/external
    

When the Spring DM Extender bundle is started, it examines all of the resolved OSGi bundles to determine if they contain a Spring context. META-INF/spring is the default location the Spring DM extender will scan looking for these application context definition files so let's create that directory for each bundle:

        mkdir -p hello-java/src/main/resources/META-INF/spring;
        mkdir -p hello-groovy/src/main/resources/META-INF/spring;
        mkdir -p hello-scala/src/main/resources/META-INF/spring;
    

Within each bundle, we'll create two spring definition files. We want to separate our basic bean definitions from those that need to be defined within an OSGi namespace. This convention is especially useful when writing tests as it allows you to draw a distinction between tests that must take place within an OSGi framework and those that may take place outside of OSGi. bundle-context.xml will be our standard Spring configuration and bundle-context-osgi.xml will contain Spring DM specific configuration.

Publishing Hello Java with Spring DM

In the vanilla spring context configuation, ./hello-java/src/main/resources/META-INF/spring/bundle-context.xml, we define our POJO implementation.

        <beans xmlns="http://www.springframework.org/schema/beans"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xmlns:p="http://www.springframework.org/schema/p"
                  xsi:schemaLocation="http://www.springframework.org/schema/beans
                  http://www.springframework.org/schema/beans/spring-beans.xsd">
            
            <bean id="helloJavaService" class="com.domain.osgi.java.internal.HelloJavaServiceImpl">
            </bean>
            
        </beans>
    

And in the file ./hello-java/src/main/resources/META-INF/spring/bundle-context-osgi.xml we will define our helloJavaService service for consumption by other bundles. This service definition refers to the implementation above.

        <beans:beans xmlns="http://www.springframework.org/schema/osgi"
                        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                        xmlns:beans="http://www.springframework.org/schema/beans"
                        xsi:schemaLocation="http://www.springframework.org/schema/beans
                                            http://www.springframework.org/schema/beans/spring-beans.xsd
                                            http://www.springframework.org/schema/osgi
                                            http://www.springframework.org/schema/osgi/spring-osgi.xsd">
            
            <service ref="helloJavaService" interface="com.domain.osgi.java.HelloJavaService">
            </service>
            
        </beans:beans>
    

That's all there is to it. helloJavaService will be published by Spring DM without any need to use the OSGi API directly.

Publishing Hello Groovy with Spring DM

The configuration for Groovy is identical to what we've done for Java. We simply reference the Groovy code that parallels our Java example.

./hello-groovy/src/main/resources/META-INF/spring/bundle-context.xml

        <beans xmlns="http://www.springframework.org/schema/beans"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xmlns:p="http://www.springframework.org/schema/p"
                  xsi:schemaLocation="http://www.springframework.org/schema/beans
                  http://www.springframework.org/schema/beans/spring-beans.xsd">
            
            <bean id="helloGroovyService" class="com.domain.osgi.groovy.internal.HelloGroovyServiceImpl">
            </bean>
            
        </beans>
    

./hello-groovy/src/main/resources/META-INF/spring/bundle-context-osgi.xml

        <beans:beans xmlns="http://www.springframework.org/schema/osgi"
                        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                        xmlns:beans="http://www.springframework.org/schema/beans"
                        xsi:schemaLocation="http://www.springframework.org/schema/beans
                                            http://www.springframework.org/schema/beans/spring-beans.xsd
                                            http://www.springframework.org/schema/osgi
                                            http://www.springframework.org/schema/osgi/spring-osgi.xsd">
            
            <service ref="helloGroovyService" interface="com.domain.osgi.groovy.HelloGroovyService">
            </service>
            
        </beans:beans>
    

Publishing Hello Scala with Spring DM

Our scala configuration follows suit. It's exactly the same as both our Java and Groovy configurations. This is all nice and simple to get going.

./hello-scala/src/main/resources/META-INF/spring/bundle-context.xml

        <beans xmlns="http://www.springframework.org/schema/beans"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xmlns:p="http://www.springframework.org/schema/p"
                  xsi:schemaLocation="http://www.springframework.org/schema/beans
                  http://www.springframework.org/schema/beans/spring-beans.xsd">
            
            <bean id="helloScalaService" class="com.domain.osgi.scala.internal.HelloScalaServiceImpl">
            </bean>
            
        </beans>
    

./hello-scala/src/main/resources/META-INF/spring/bundle-context-osgi.xml

        <beans:beans xmlns="http://www.springframework.org/schema/osgi"
                        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                        xmlns:beans="http://www.springframework.org/schema/beans"
                        xsi:schemaLocation="http://www.springframework.org/schema/beans
                                            http://www.springframework.org/schema/beans/spring-beans.xsd
                                            http://www.springframework.org/schema/osgi
                                            http://www.springframework.org/schema/osgi/spring-osgi.xsd">
            
            <service ref="helloScalaService" interface="com.domain.osgi.scala.HelloScalaService">
            </service>
            
        </beans:beans>
    

Putting It All Together

At this point, the basics are in place. Once again, PAX is really going to shine as we'll install and provision our bundles. Aside from adding the appropriate Spring repositories in the beginning of this post, we haven't done anything to satisfy the Spring DM dependencies. Thanks to PAX Runner's notion of profiles we can provision all of the bundles required for Spring DM with a simple command line switch:

Update: Newer versions of PAX Runner don't install the compendium bundle by default so we need to explicitly import it:

        pax-import-bundle -g org.osgi -a org.osgi.compendium -v 4.2.0
    

Then we can safely start everything up with pax:provision:

        mvn install pax:provision -Dprofiles=spring.dm
    

Voilà! As the logging indicates, Maven was able to fetch all the bundles we require and our three services have been published by Spring DM.

        ...
        
        [SpringOsgiExtenderThread-5] INFO org.springframework.osgi.service.exporter.support.OsgiServiceFactoryBean - Publishing service under classes [{com.domain.osgi.scala.HelloScalaService}]
        
        ...
        
        [SpringOsgiExtenderThread-4] INFO org.springframework.osgi.service.exporter.support.OsgiServiceFactoryBean - Publishing service under classes [{com.domain.osgi.groovy.HelloGroovyService}]
        
        ...
        
        [SpringOsgiExtenderThread-6] INFO org.springframework.osgi.service.exporter.support.OsgiServiceFactoryBean - Publishing service under classes [{com.domain.osgi.java.HelloJavaService}]
        
        ...
    

Conclusion

We've seen how we can incorporate Spring DM to completely abstract away the OSGi API for defining and publishing OSGi services. Spring DM is wiring our bundles together using compiled bytecode so we needn't do anything fancy whether we're using use Java, Groovy, Scala or any other language than may be run on the JVM. Up next, we'll walk through the Spring DM configuration for consuming these services.

Check out Part 3 here

2 comments:

  1. Hi Brian,

    I am trying to get script uncompiled and trying to run them inside the spring powered bundle and it does not work.

    It throws ClassNotFound Exception all over the place.

    I have pax-runner for equinox and spring.dm profiles.

    ReplyDelete
  2. That's strange. All you need to have installed are the JDK and Maven 2. Everything else will be fetched automatically.

    Did you put everything together by hand? Or did you pull the code down from bitbucket?

    I just deleted my whole mvn repository and pulled down a fresh clone from hg - everything looks good on my end.

    The steps should be:

    hg clone http://bitbucket.org/brimurph/helloworld

    cd helloworld

    hg update -r 1

    mvn install pax:provision -Dprofiles=spring.dm

    ReplyDelete