Ouertani Slim was born in Tunisia in 1981. Now he is a software engineer since 2004 and he is Java 6 and Spring certified with 100% and 94% respectively. He is very interested in Java technology, Scala and open source projects. He believes that programming is one of the hardest jobs and most beautiful in the world. Slim has posted 32 posts at DZone. You can read more from them at their website. View Full User Profile

OSGi Service Hook to Log All Service Invocations Using Dynamic Proxy

12.28.2009
| 11180 views |
  • submit to reddit

In this post, I will present an elegant solution to log all services invocations in an OSGi environment. After developing and using some bundles we need to know for different reasons which services are used by such a bundle, when invocation are occurred and what parameters are passed to services. So, should I log all service invocations? It's impossible. First, you need to change all the source code of your bundles. Second, other bundles are proprietary and you haven't their source code .

But thanks to service hooks, introduced in 4.2 Osgi release, we can do it in easy way.

Service Hooks Service hooks are the second face of OSGi :) . As we know how to get and listen to services,  hooks provide a way to know and manage whose bundles are waiting or tacking services. The goal of this solution is to present  FindHook and  EventHook services use case to log all service invocations.

Solution Description logger proxy flow

When service is registered, we create another copy (delegation) using dynamic proxy and register it using source bundle class loader and add the proxied property. When some bundle needs to use this service we hide the first one and effectively that bundle will get the proxied service.
Solution - The EventHook interface has only one method to implement :
 @Override    public void event(ServiceEvent event, Collection contexts) {
final ServiceReference serviceReference = event.getServiceReference();
System.out.println("" + serviceReference.getBundle().getSymbolicName());

if (serviceReference.getProperty(PROXY) == null && serviceReference.getBundle().getBundleContext() != bc) {
Bundle bundle = serviceReference.getBundle();

switch (event.getType()) {
case REGISTERED: {

String[] propertyKeys = serviceReference.getPropertyKeys();
Properties properties = buildProps(propertyKeys, event);
String[] interfaces = (String[]) serviceReference.getProperty(
"objectClass");

Class[] toClass = toClass(interfaces, bundle);
proxyService(bundle,
toClass,
properties,
this.getClass().getClassLoader(), new LoggerProxy(
bc, serviceReference));



break;
}
case UNREGISTERING: {
//TODO
break;
}
case MODIFIED:
case MODIFIED_ENDMATCH: {
//TODO
break;
}
}
}
}
Before event delivery this method will be called. We check if the service is not already proxied. If it is a new service, we create another one using its declared interfaces, all its properties and another property "proxied".

 

- The FindHook interface has only one method to implement
 @Override    public void find(BundleContext bc, String name, String filter,
boolean allServices, Collection references) {
try {
if (this.bc.equals(bc) || bc.getBundle().getBundleId() == 0) {
return;
}

System.out.println(
" bundle : [" + bc.getBundle().getSymbolicName() + "] try to get reference of " + name);
Iterator iterator = references.iterator();

while (iterator.hasNext()) {
ServiceReference sr = (ServiceReference) iterator.next();

System.out.println(
"from bundle" + sr.getBundle().getSymbolicName());

if (sr.getProperty("proxied") == null) {
iterator.remove();
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
This method is called during the service find operation, in this method callback we hide all services that aren't proxied.

The LoggerProxy is a sample proxy to log all invocation and forward call to effective refreence.

public class LoggerProxy implements InvocationHandler, Serializable {
private ServiceReference serviceReference;
private BundleContext bundleContext;

public LoggerProxy(BundleContext bundleContext,
ServiceReference serviceReference) {
this.serviceReference = serviceReference;
this.bundleContext = bundleContext;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-->Methode : [" + method.getName() + "] ");
System.out.println("-->Parameters : ");
for (Object object : args) {
System.out.print("->"+object + " : ");
}
System.out.println("");
Object invoke = method.invoke(bundleContext.getService(serviceReference),
args);

System.out.println("-->Return : " + invoke);
return invoke;
}
}

At the end we register our services using an Activator :

public class Activator implements BundleActivator {
@Override
public void start(BundleContext context) throws Exception {
LoggerHooks loggerHooks = new LoggerHooks(context);
context.registerService(new String[]{FindHook.class.getName(), EventHook.class.getName()},
loggerHooks, null);

}

@Override
public void stop(BundleContext context) throws Exception {
}
}

Console Output


When bundle 34 starts, it tries to get the TService service from bundle 33

osgi> start 34
bundle : [demo.client_0.1] try to get reference of com.jtunisie.akel.demo.api.TService
from bundledemo.register_0.1
from bundledemo.register_0.1
-->Methode : [echo]
-->Parameters :
->Service Found :
-->Return : Yes !Service Found
Yes !Service Found

Bundle 33 register two services one is proxied.

osgi> b 33demo.register_0.1_0.0.0 [33]
Id=33, Status=ACTIVE Data Root=/home/sst/dev/4.2hooks/configuration/org.eclipse.osgi/bundles/33/data
Registered Services
{com.jtunisie.akel.demo.api.TService}={service.id=44}
{com.jtunisie.akel.demo.api.TService}={service.id=45, proxied=true}
No services in use.
Exported packages
com.jtunisie.akel.demo.service.impl; version="0.0.0"[exported]
Imported packages
com.jtunisie.akel.demo.api; version="0.0.0"
org.osgi.framework; version="1.5.0"
scala; version="0.0.0"
No fragment bundles
Named class space
demo.register_0.1; bundle-version="0.0.0"[provided]
No required bundles
Conclusio Dynamic proxy and service hooks provide a powerful tool to mange complicated OSGi use cases. This post shows one use case using find and event hooks to log all service invocations. In the second post I will try to present Listener Hook and a way to distribute Osgi service transparently.

source code is under svn : svn checkout http://osgiservicelogger.googlecode.com/svn/trunk/ osgiservicelogger-read-only
Published at DZone with permission of its author, Slim Ouertani.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Slim Ouertani replied on Tue, 2009/12/29 - 12:55am

Note : for this demo I am using bundlor version
1.0.0.M6 and not the RC1 because it doesn't generate manifest file inside the pom.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.