Interceptor module is inspired from JavaEE/JakartaEE interceptor API but adapted to OSGi services.

It enables to proxy any service and execute code around the service methods.

Dependencies

<dependency>
  <groupId>org.apache.karaf.services</groupId>
  <artifactId>org.apache.karaf.services.interceptor.api</artifactId>
</dependency>

Defining an interceptor

An interceptor is simply an OSGi service marked with @Interceptor and having an interceptor binding which is nothing more than an annotation marked with @InterceptorBinding.

Here is a binding:

@Target({TYPE, METHOD})
@Retention(RUNTIME)
@InterceptorBinding
public @interface Suffix {
}

And here is an associated interceptor:

@Suffix
@Interceptor
@Component(service = SuffixingInterceptor.class)
public class SuffixingInterceptor {
    // ...
}
Tip
the examples are using SCR but there is no requirement to do so, you can do it with context.registerService() as well.

For the interceptor to do something, you must define an @AroundInvoke method which will intercept method calls in intercepted services. It must takes a single parameter of type InvocationContext:

@AroundInvoke
public Object around(final InvocationContext context) throws Exception {
    return context.proceed() + "(suffixed)";
}

Using interceptors

Assuming you have an interceptor library (it is commong for transversal concerns like security, auditing, tracing, metrics, etc…​), you can enable the interceptor usages with these few steps:

  1. Ensure you register your service as an OSGi service,

  2. Mark the service with @EnableInterceptors,

  3. Mark the class or method with the interceptor bindings you want

Tip
if you put a binding on a class is it available for all methods and is called after method level interceptors.

As an example speaks better than 1000 words, here is a service using our previous suffixing interceptor:

@EnableInterceptors
@Component(service = InterceptedService.class)
public class InterceptedService {
    @Suffix
    public String doStuff(final String value) {
        return "'" + value + "'";
    }
}

You can notice that it is equivalent to the following example which just moved the interceptor at class level:

@Suffix
@EnableInterceptors
@Component(service = InterceptedService.class)
public class InterceptedService {
    public String doStuff(final String value) {
        return "'" + value + "'";
    }
}

Proxying implementation

If possible, the proxying will use java.lang.reflect.Proxy but if there is a class to proxy and not only interfaces, asm must be available for the proxy to suceed to be created.