OSGi Service Hook to Log All Service Invocations Using Dynamic Proxy
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.
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) {
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,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]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.
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
source code is under svn : svn checkout http://osgiservicelogger.googlecode.com/svn/trunk/ osgiservicelogger-read-only
(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