In this post I will try to describe ease of use lightweight dependency injection framework Google Guice in OSGi environment.
There are two possibilities to use Guice in OSGi environment. We can use Google Guice without any extensions just take guice.jar and its dependency aopalliance.jar from project website http://code.google.com/p/google-guice/downloads/list.
Second choice is to use Peaberry project which is an extension library for Google-Guice that supports dependency injection of dynamic services.
First I will describe usage of Guice without any extensions.
Google Juice dependency is mainly provided by using annotations, xml is not used for any configuration.
The only dependencies for using Google Guice without any extensions in OSGi bundles:
1 ACTIVE com.google.inject_2.0.0 3 ACTIVE com.springsource.org.aopalliance_1.0.0
In compare to spring it is big difference only 2 extra bundles to have automatically managed dependency injection in our bundle!
In order to use Guice first we have to define a module which will describe our bindings.
Our module must extend AbstractModule class and implement configure method.
In my example I’m using binding of interfaces to concrete implementation classes and interceptor which will intercept all methods with Intercept annotation. Binding to instance could be used as well.
package guice_test.module;
import com.google.inject.AbstractModule;
import com.google.inject.matcher.Matchers;
import com.labs.interceptor.Intercept;
import com.labs.interceptor.ParserInterceptor;
import com.labs.parser.IParser;
import com.labs.parser.Parser;
import com.labs.provider.IProvider;
import com.labs.provider.Provider;
public class TestModule extends AbstractModule {
@Override
protected void configure() {
bind(IParser.class).to(Parser.class);
bind(IProvider.class).to(Provider.class);
bindInterceptor(Matchers.any(), Matchers.annotatedWith(Intercept.class),
new ParserInterceptor());
}
}
In the example we have Parser which is used by Provider and should be injected using its constructor.To use automatic injection we have to annotate method with com.google.inject.Inject annotation. Parser method will be intercepted by ParserInterceptor. With one line in the module we can define and use advantages of AOP language.
package com.labs.provider;
import com.google.inject.Inject;
import com.labs.parser.IParser;
public class Provider implements IProvider {
private IParser parser;
@Inject
public Provider(IParser parser){
this.parser = parser;
}
public String process(String name){
return this.parser.parse(name);
}
}
package com.labs.parser;
import com.labs.interceptor.Intercept;
public class Parser implements IParser {
@Intercept
public String parse(String input){
return "output" + input;
}
}
package com.labs.interceptor;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class ParserInterceptor implements MethodInterceptor{
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("Invocation intercepted : " + invocation.getMethod().getName());
return invocation.proceed();
}
}
The last thing is our Activator which will be used to start our code managed by Guice. In the start method Injector is created and from injector we can get an instance of provider which will have injected Parser by Guice framework.
import guice_test.module.TestModule;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.labs.provider.Provider;
public class Activator implements BundleActivator {
public void start(BundleContext context) throws Exception {
Injector injector = Guice.createInjector(new TestModule());
Provider provider = injector.getInstance(Provider.class);
String res = provider.process("hello");
System.out.println("Result =" + res);
}
public void stop(BundleContext context) throws Exception {
}
}
Mainfest of our guice_test bundle looks:
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Guice_test Bundle-SymbolicName: guice_test Bundle-Version: 1.0.0.qualifier Bundle-Activator: guice_test.Activator Import-Package: com.google.inject;version="1.2.0", com.google.inject.binder;version="1.2.0", com.google.inject.matcher;version="1.2.0", org.aopalliance.intercept;version="1.0.0", org.osgi.framework;version="1.3.0" Bundle-RequiredExecutionEnvironment: J2SE-1.5
In the first example we didn’t register or use any service. We can still do this using OSGI API like context.registerService(…). Output of starting the bundle should be as follow:
osgi> Invocation intercepted : parse Result =outputhello
In the second example we will concentrate on usage of Peaberry project to expose IProvider as a OSGI service.
Peaberry allows us to inject dynamic service to our code or to expose our component as a service.
We will change the previous example to use the Peaberry extension.
First we need to add to the manifest three packages:
org.ops4j.peaberry;version="1.0.0", org.ops4j.peaberry.builders;version="1.0.0", org.ops4j.peaberry.util;version="1.0.0"
When we start our container and list bundles with command ss we should have five following bundles:
id State Bundle 0 ACTIVE org.eclipse.osgi_3.5.0.v20090520 1 ACTIVE com.google.inject_2.0.0 3 ACTIVE com.springsource.org.aopalliance_1.0.0 5 ACTIVE guice_test_1.0.0.qualifier 6 ACTIVE org.ops4j.peaberry_1.0.0
We want to expose IProvider implementation as a service. To do that we need to bind IProvider interface to service instance represent by Provider class.
We can use following API to export the service:
bind(TypeLiterals.export(A.class)). toProvider(Peaberry.service(new A()).export());
At the same time we also import single instance of IProvider service using API:
bind(A.class).toProvider(Peaberry.service(A.class).single());
Here is a code snippet of TestModule..
@Override
protected void configure() {
bind(IParser.class).to(Parser.class);
//exporting service
bind(TypeLiterals.export(IProvider.class)).
toProvider(Peaberry.service(new Provider()).export());
bindInterceptor(Matchers.any(),
Matchers.annotatedWith(Intercept.class), new ParserInterceptor());
//importing service
bind(IProvider.class).
toProvider(Peaberry.service(IProvider.class).single());
bind(IServiceUser.class).to(ServiceUser.class);
}
Provider instance will be registered as a service and new class ServiceUser will
use Provider service.
public class ServiceUser implements IServiceUser {
private IProvider provider;
@Inject
public ServiceUser(IProvider provider){
this.provider = provider;
}
public String processRequest(String name){
return provider.process(name);
}
}
Then we need to add org.ops4j.peaberry.Peaberry.osgiModule to createInjector method.
We are also injecting IProvider service using a handle for export of a service org.ops4j.peaberry.Export. Using a handle we can unregister the service when the bundle is stopped. The Peaberry is providing handle for import as well which can be used to unget the service.
Here is how Activator looks now.
package guice_test;
import guice_test.module.TestModule;
import org.ops4j.peaberry.Export;
import org.ops4j.peaberry.Peaberry;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.labs.provider.IProvider;
import com.labs.user.IServiceUser;
public class Activator implements BundleActivator {
//injecting a handle to IProvider service can be used to unregister
// service when bundle is stopped
@Inject
private Export<IProvider> providerProxy;
public void start(BundleContext context) throws Exception {
Injector injector = Guice.createInjector(Peaberry.osgiModule(context),
new TestModule());
injector.injectMembers(this);
IServiceUser user = injector.getInstance(IServiceUser.class);
String res = user.processRequest("hello");
System.out.println("Result =" + res);
}
public void stop(BundleContext context) throws Exception {
this.providerProxy.unput();
}
}
When the bundle is started ServiceUser will use Provider service and execute process method. Output of the execution should be:
osgi> Invocation intercepted : parse Result =outputhello
API for import multiple services:
Import org.ops4j.peaberry.Peaberry.service; Import org.ops4j.peaberry.util.TypeLiterals.iterable; @Inject Iterable<A> services; bind(iterable(A.class)).toProvider(service(A.class).multiple());
API for service binding:
service(A.class).filter(/* apply a filter */)... service(A.class).in(/* query a specific registry */)... service(A.class).out(/* watch for service changes - act like ServiceTracker*/)... service(A.class).decoratedWith(/* apply decoration */)...
Exporting service with service properties:
service(AImpl).attributes(names("serviceName=A")).export();
Instead of using dynamic proxy you can use direct binding
service(A.class).single().direct();
Conclusion:
The Peaberry project looks quite good I don’t see any big tricks (hacks) done to support OSGi capabilities on Guice. It’s very lightweight only couple bundles which you have to include in your runtime. No xml to configure dependency and very nice API to handle dynamic nature of OSGi services.
Both examples were tested on Eclipse 3.5.
This is very interesting. Nice post……
Comment by Suresh Selvapandian — May 12, 2010 @ 1:25 pm |
Very good post
thx I helped me a lot to understand the basics of Guice & Peaberry
Comment by Paul — November 18, 2010 @ 12:51 pm |