| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| |
| = Interceptor |
| |
| 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 |
| |
| [source,xml] |
| ---- |
| <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: |
| |
| [source,java] |
| ---- |
| @Target({TYPE, METHOD}) |
| @Retention(RUNTIME) |
| @InterceptorBinding |
| public @interface Suffix { |
| } |
| ---- |
| |
| And here is an associated interceptor: |
| |
| [source,java] |
| ---- |
| @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`: |
| |
| [source,java] |
| ---- |
| @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: |
| |
| . Ensure you register your service as an OSGi service, |
| . Mark the service with `@EnableInterceptors`, |
| . 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: |
| |
| [source,java] |
| ---- |
| @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: |
| |
| |
| [source,java] |
| ---- |
| @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. |