Skip to content

HowTo Grails OSGi integration in 5 minutes

rotty3000 edited this page Nov 9, 2011 · 7 revisions

With Arkadiko, integrating OSGi into any Spring Framework driven application is a piece of cake!

Here is an example using the Grails trip-planner app developed on the IBM DeveloperWorks blog a couple years back: http://www.ibm.com/developerworks/java/library/j-grails01158/

Setting up Grails:

Setting up the app:

  • extract it
  • cd into the src/trip-planner (let's assume the app is extracted to /opt/src/trip-planner
  • execute grails upgrade (cause the code is a little old)
  • test the app by running grails run-app (open the url given in the browser)

In order to see what's going on we'll enable info logging for Grails. Open up /opt/src/trip-planner/grails-app/conf/Config.groovy and at about line 62 (after the large log4j block) paste the following:

log4j = {
    root {
        info()
    }
}

Now that logging is enable we need to add a couple of jars to the project:

In order to make this a little more interesting, let's install some other bundles, a simple logging adapter. That bundle uses declarative services, so we also have to install a few other bundles to enable support for it. Here is the list:

  • bundle-log-adapter.jar
  • org.eclipse.equinox.ds_1.3.0.v20110502.jar
  • org.eclipse.equinox.util_1.0.300.v20110502.jar
  • org.eclipse.osgi.services_3.3.0.v20110513.jar

Also, we'll download the Felix Gogo shell so that you can interact with the OSGi framework from the command line, once the app is up and running:

The first two jars, arkadiko-0.0.4-111108112238.jar and org.eclipse.osgi_3.7.0.v20110613.jar, we'll place in /opt/src/trip-planner/lib. The remaining jars we'll place into /opt/src/trip-planner/lib/osgi-plugins.

Finally, we're ready to integrate the OSGi framework.

Open up /opt/src/trip-planner/web-app/WEB-INF/applicationContext.xml and immediately after the opening beans tag paste the following:

  <bean 
    id="framework" 
    class="com.liferay.arkadiko.util.AKFrameworkFactory" 
    factory-method="init" 
    destroy-method="stop"
  >
    <constructor-arg>
      <map>
        <entry key="org.osgi.framework.bundle.parent" value="app" />
        <entry key="org.osgi.framework.startlevel.beginning" value="5" />
        <entry key="org.osgi.framework.storage">
          <!-- This is where the OSGi framework with save it's data. -->
          <value>/opt/src/trip-planner/osgi-data</value>
        </entry>
        <entry key="org.osgi.framework.system.packages.extra">
          <!-- List any packages that are needed in the framework from the app's classpath. -->
          <value>org.apache.commons.logging</value>
        </entry>
        <entry key="bundles.force.start">
          <value>true</value>
        </entry>
        <entry key="bundles.to.install">
          <value>
            lib/osgi-plugins/org.eclipse.equinox.util_1.0.300.v20110502.jar,
            lib/osgi-plugins/org.eclipse.osgi.services_3.3.0.v20110513.jar,
            lib/osgi-plugins/org.eclipse.equinox.ds_1.3.0.v20110502.jar,
            lib/osgi-plugins/bundle-log-adapter.jar,
            lib/osgi-plugins/org.apache.felix.gogo.command-0.12.0.jar,
            lib/osgi-plugins/org.apache.felix.gogo.runtime-0.10.0.jar,
            lib/osgi-plugins/org.apache.felix.gogo.shell-0.10.0.jar
          </value>
        </entry>
        <entry key="project.dir">
          <value>/opt/src/trip-planner</value>
        </entry>
      </map>
    </constructor-arg>
  </bean>

  <bean 
    id="com.liferay.arkadiko.AKBeanPostProcessor" 
    class="com.liferay.arkadiko.AKBeanPostProcessor" 
    init-method="afterPropertiesSet"
  >
    <property name="framework" ref="framework" />
    <property name="ignoredBeanNames">
      <list>
        <value>grailsApplication</value>
        <value>grailsResourceLoader</value>
      </list>      
    </property>
    <property name="ignoredClassNames">
        <value>org.codehaus.groovy.grails.commons.spring.GrailsRuntimeConfigurator</value>
    </property>
  </bean>

The first bean is a factory that sets up the framework that is required by the second bean which is a BeanPostProcessor (processes all other beans). Of course there are some limitations to what we can do, and only interfaces can be wired using OSGi because Arkadiko has to be able to create a proxy. So we have to filter out some beans that are not using interfaces, like grailsApplication, grailsResourceLoader, and the class org.codehaus.groovy.grails.commons.spring.GrailsRuntimeConfigurator (these were discovered by trial and error, but only took a few attempts to find which beans couldn't be proxied).

That's it! We've now integrated OSGi into our Grails app by using Arkadiko.

So, how do we know it's working? Earlier we enabled more extensive logging, and added some useful bundles, so the first thing we should notice in the log are statements something like the following:

...
Running Grails application..
2011-11-09 00:19:03,651 [main] INFO  http11.Http11Protocol  - Initializing Coyote HTTP/1.1 on http-8080
2011-11-09 00:19:03,652 [main] INFO  core.StandardService  - Starting service Tomcat
2011-11-09 00:19:03,652 [main] INFO  core.StandardEngine  - Starting Servlet Engine: Apache Tomcat/6.0-snapshot
...
2011-11-09 00:19:04,667 [com.liferay.arkadiko.bundle.log.adapter.LogListenerImpl@19166179] INFO  bundle-log-adapter  - ServiceEvent REGISTERED {org.osgi.service.log.LogListener}=	{component.name=com.liferay.arkadiko.bundle.log.adapter.LogListenerImpl, component.id=0, service.id=34}
2011-11-09 00:19:04,669 [com.liferay.arkadiko.bundle.log.adapter.LogListenerImpl@19166179] INFO  bundle-log-adapter  - BundleEvent STARTED
2011-11-09 00:19:04,676 [com.liferay.arkadiko.bundle.log.adapter.LogListenerImpl@19166179] INFO  gogo.command  - ServiceEvent REGISTERED {org.apache.felix.gogo.command.Basic}={osgi.command.function=[bundlelevel,frameworklevel,headers,help,install,lb,log,refresh,resolve,start,stop,uninstall,update,which], osgi.command.scope=felix, service.id=35}
2011-11-09 00:19:04,678 [com.liferay.arkadiko.bundle.log.adapter.LogListenerImpl@19166179] INFO  gogo.command  - ServiceEvent REGISTERED {org.apache.felix.gogo.command.Inspect}={osgi.command.function=[inspect], osgi.command.scope=felix, service.id=36}
2011-11-09 00:19:04,680 [com.liferay.arkadiko.bundle.log.adapter.LogListenerImpl@19166179] INFO  gogo.command  - ServiceEvent REGISTERED {org.apache.felix.gogo.command.Files}={osgi.command.function=[cd,ls], osgi.command.scope=felix, service.id=37}
2011-11-09 00:19:04,683 [com.liferay.arkadiko.bundle.log.adapter.LogListenerImpl@19166179] INFO  gogo.command  - ServiceEvent REGISTERED {org.apache.felix.gogo.command.OBR}={osgi.command.function=[deploy,info,javadoc,list,repos,source], osgi.command.scope=obr, service.id=38}
...

So we know our bundles are loading. But what did Arkadiko do to our other beans? If we look a little further into the log we should find statements something like:

...
2011-11-09 00:19:04,838 [com.liferay.arkadiko.bundle.log.adapter.LogListenerImpl@19166179] INFO  eclipse.osgi  - **ServiceEvent REGISTERED** {org.springframework.beans.factory.FactoryBean, org.springframework.beans.factory.InitializingBean, org.springframework.context.ApplicationContextAware}={original.bean=true, bean.id=**pluginManager**, service.id=47}
____________________________
Welcome to Apache Felix Gogo

g! 2011-11-09 00:19:04,849 [com.liferay.arkadiko.bundle.log.adapter.LogListenerImpl@19166179] INFO  eclipse.osgi  - **ServiceEvent REGISTERED** {org.codehaus.groovy.grails.plugins.GrailsPluginManager}={original.bean=true, bean.id=**pluginManager**, service.id=48}
2011-11-09 00:19:04,862 [com.liferay.arkadiko.bundle.log.adapter.LogListenerImpl@19166179] INFO  eclipse.osgi  - **ServiceEvent REGISTERED** {javax.servlet.Filter, org.springframework.beans.factory.BeanNameAware, org.springframework.web.context.ServletContextAware, org.springframework.beans.factory.InitializingBean, org.springframework.beans.factory.DisposableBean}={original.bean=true, bean.id=**characterEncodingFilter**, service.id=49}
...

We can see that all of the supported beans were published into the framework. This also means that they can be used, replaced or augmented dynamically from within the OSGi framework.

As you may notice also, there is a statement in the log Welcome to Apache Felix Gogo. This is a hint that the Gogo shell is running, and in fact it's running on stdout which where the logs are being printed. So we should simply be able to type help into the shell where we started the Grails app.

We should get the following output:

...
Server running. Browse to http://localhost:8080/trip-planner
2011-11-09 00:19:08,129 [main] INFO  plugins.DefaultGrailsPluginManager  - Started to scan for plugin changes in every 5000ms.
help
felix:bundlelevel
felix:cd
felix:frameworklevel
felix:headers
felix:help
felix:inspect
felix:install
felix:lb
felix:log
...
g!   

Typing help lb gives us the following:

g! help lb

lb - list all installed bundles
   scope: felix
   flags:
      -l, --location   show location
      -s, --symbolicname   show symbolic name
      -u, --updatelocation   show update location

lb - list installed bundles matching a substring
   scope: felix
   flags:
      -l, --location   show location
      -s, --symbolicname   show symbolic name
      -u, --updatelocation   show update location
   parameters:
      String   subtring matched against name or symbolic name
g! 

And there you have it! OSGi integrated into your Spring/Grails app.

Admittedly this is a pretty basic app with boring beans. But there is no limit to the number of beans that Arkadiko can theoretically bridge into the OSGi framework.

Clone this wiki locally