Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

OSGi Service Hook to Log All Service Invocations Using Dynamic Proxy

DZone's Guide to

OSGi Service Hook to Log All Service Invocations Using Dynamic Proxy

· Java Zone
Free Resource

Learn how to troubleshoot and diagnose some of the most common performance issues in Java today. Brought to you in partnership with AppDynamics.

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

Understand the needs and benefits around implementing the right monitoring solution for a growing containerized market. Brought to you in partnership with AppDynamics.

Topics:

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}