AOP Proxy in Spring
Traditionally Spring has excellent support for AOP features.
One of the most common approaches for adding aspects to a Spring bean is the org.springframework.aop.framework.ProxyFactoryBean class.
In Spring, the following definitions will inject a logging aspect to an existing service bean:
<beans> <bean id="serviceImpl" class="io.modio.blog.osgi.aop.proxy.ServiceImpl"/> <bean id="traceInterceptor" class="org.springframework.aop.interceptor.SimpleTraceInterceptor"/> <bean id="service" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>io.modio.blog.osgi.aop.proxy.Service</value> </property> <property name="target" ref="serviceImpl"/> <property name="interceptorNames"> <list> <value>traceInterceptor</value> </list> </property> </bean> </beans>
The equivalent definition in Blueprint fails:
<blueprint default-activation="eager" xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <bean id="serviceImpl" class="io.modio.blog.osgi.aop.proxy.ServiceImpl"/> <bean id="traceInterceptor" class="org.springframework.aop.interceptor.SimpleTraceInterceptor"/> <bean id="serviceFactory" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <array> <value>io.modio.blog.osgi.aop.proxy.Service</value> </array> </property> <property name="target" ref="serviceImpl"/> <property name="interceptorNames"> <list> <value>traceInterceptor</value> </list> </property> </bean> <bean id="service" factory-ref="serviceFactory" factory-method="getObject"/> </blueprint>
The error reads:
AOP Proxy in Blueprint
Our attempt to create a Blueprint-compatible version of Spring’s ProxyFactoryBean is based on similar work by the OpenEngSB project.
However, the version of ProxyFactoryBean in this article differs from OpenEngSB’s version in the approach used to load service interfaces. The OpenEngSB version creates a chain of 3 class loaders (TCCL, ProxyFactoryBean’s, System) and tries them in sequence. The article version uses the bundle class loader which is more suitable for an OSGi container.
public class ProxyFactoryBean { private BundleContext context; private List<String> proxyInterfaces; private List<Advice> advices; private Object target; private Class<?>[] retrieveInterfaceClasses() throws ClassNotFoundException { Class<?>[] interfaces; Class<?> clazz; interfaces = new Class<?>[proxyInterfaces.size()]; for (int i = 0; i < proxyInterfaces.size(); i++) { clazz = context.getBundle().loadClass(proxyInterfaces.get(i)); if (clazz == null) { throw new IllegalStateException("Could not find class " + proxyInterfaces.get(i)); } interfaces[i] = clazz; } return interfaces; } public Object getObject() throws ClassNotFoundException { Class<?>[] interfaces; ProxyFactory proxyFactory; ClassLoader cl; interfaces = retrieveInterfaceClasses(); proxyFactory = new ProxyFactory(interfaces); for (Advice advice:advices) { proxyFactory.addAdvice(advice); } proxyFactory.setTarget(target); cl = ((BundleWiring)context.getBundle().adapt(BundleWiring.class)). getClassLoader(); return proxyFactory.getProxy(cl); } public void setContext(BundleContext context) { this.context = context; } public void setProxyInterfaces(List<String> proxyInterfaces) { this.proxyInterfaces = proxyInterfaces; } /** * Convenience method to avoid the verbose <list></list> * syntax in blueprint when there's only one entry in the list * * @param proxyInterface proxy interface */ public void setProxyInterface(String proxyInterface) { proxyInterfaces = new ArrayList<String>(); proxyInterfaces.add(proxyInterface); } public void setAdvices(List<Advice> advices) { this.advices = advices; } /** * Convenience method to avoid the verbose <list></list> * syntax in blueprint when there's only one entry in the list * * @param advice advice */ public void setAdvice(Advice advice) { advices = new ArrayList<Advice>(); advices.add(advice); } public void setTarget(Object target) { this.target = target; } }
The previous blueprint definition can now be written as follows:
<blueprint default-activation="eager" xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <bean id="serviceImpl" class="io.modio.blog.osgi.aop.proxy.ServiceImpl"/> <bean id="traceInterceptor" class="org.springframework.aop.interceptor.SimpleTraceInterceptor"/> <bean id="serviceFactory" class="io.modio.blog.osgi.aop.proxy.ProxyFactoryBean"> <property name="context" ref="blueprintBundleContext"/> <property name="proxyInterface" value="io.modio.blog.osgi.aop.proxy.Service"/> <property name="target" ref="serviceImpl"/> <property name="advice" ref="traceInterceptor"/> </bean> <bean id="service" factory-ref="serviceFactory" factory-method="getObject"/> </blueprint>