OSGi Service Hook to Log All Service Invocations Using Dynamic Proxy
Join the DZone community and get the full member experience.
Join For FreeIn 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

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) {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".
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;
}
}
}
}
- The FindHook interface has only one method to implement
@Override public void find(BundleContext bc, String name, String filter,This method is called during the service find operation, in this method callback we hide all services that aren't proxied.
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();
}
}
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
Opinions expressed by DZone contributors are their own.
Comments