Showing posts with label Spring. Show all posts
Showing posts with label Spring. Show all posts

Wednesday, March 7, 2012

Resolve circular dependency in Spring

It is discussed clearly in Managing Circular Dependencies in Spring.

We used to deal with it in init() method. For example, A<->B, inject B into A, and in A's init(), call B.setA(this). This approach has two drawbacks. Not only the interface of B must expose an additional setA method; but also *this* refers to the raw instance of A, rather than a proxied (wrapped) instance of A. That means, if the method of A requires AOP proxy such as transaction interceptor, you will get Hibernate no session bounded exception. Remember, the Spring bean (applicationContext.getBean("xxx")) provides us both dependency injection and AOP proxy.

The article describes two solutions:
1. Inject ApplicationContext to one bean through ApplicationContextAware interface and look up the peer bean
2. Use a BeanPostProcessor to wire up the beans after they are instantiated. This is preferred since it allows the container to handle this, rather than the bean itself.

The implementation in the above post seems to be problematic. I always run into BeanCurrentlyInCreationException. I revised it as follows, basically, the passed in initialized bean should be target bean, and we know both beans are ready at this point. Also, I added support for proxy and factory bean.

<bean id="circularDependencyBeanPostProcessor" class="CircularDependencyBeanPostProcessor">
  <property name="config">
   <map>
    <entry key="templateManager">  
     <props>
      <prop key="policyDomainManager">templateManager</prop>  
     </props>
    </entry>
   </map>
  </property>
 </bean>
public Object postProcessAfterInitialization(Object targetBean, String beanName) {
  if (config == null) {
   return targetBean;
  }
  Map dependentConfig = (Map) config.get(beanName);
  if (dependentConfig != null) {
   try {
    if (targetBean instanceof FactoryBean) {
     targetBean = ((FactoryBean) targetBean).getObject();
    }
    
    for (Object sourceBeanName : dependentConfig.keySet()) {
     Object sourceBean = factory.getBean((String) sourceBeanName);
     
     // Jdk dynamic proxy is interface based, generally we don't want
     // to expose those set methods such as setPolicyDomainMgr()
     // in the interface. In this case, we inject the dependent bean
     // into its target object instead.
     if (AopUtils.isJdkDynamicProxy(sourceBean)) {
      sourceBean = ((Advised) sourceBean).getTargetSource().getTarget();
     }
     String propertyName = (String)dependentConfig.get(sourceBeanName);
    
     BeanInfo beanInfo = Introspector.getBeanInfo(sourceBean.getClass());
     PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
     if (propertyDescriptors != null) {
      for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
       if (propertyName.equals(propertyDescriptor.getName())) {
        Method setter = propertyDescriptor.getWriteMethod();
        setter.invoke(sourceBean, targetBean);
        break;
       }
      }
     }
    }

   } catch (IntrospectionException ie) {
    log.error("IntrospectionException", ie);
   } catch (Exception e) {
    log.error(e);
   }

  }
  return targetBean;
 }

Friday, December 18, 2009

Spring RMI callback

One open issue of Spring remoting via RMI is that it hasn't supported RMI callback as discussed in here.The conventional RMI callback requires the interface implements Remote and the methods throw RemoteException. Hereby it is not very nice.

WlcfgRmiCallbackProxyFactory is implemented to provide such support by customizing Spring remoting code. It provides a remote proxy which intercepts method invocation on callback interface (non-RMI). It uses wlcfgRmiCallbackExporter which wraps a non-RMI service into remote object via RmiInvocationHandler and export it. Note that it doesn't bind to RMI registry.


public class WlcfgRmiCallbackProxyFactory extends RemoteInvocationBasedAccessor
implements MethodInterceptor, Serializable {

private static final long serialVersionUID = 5685339356048366732L;

private Class callbackInterface;
private RmiInvocationHandler invocationHandler;

public WlcfgRmiCallbackProxyFactory(Object callback, Class callbackInterface) {
this.callbackInterface = callbackInterface;

WlcfgRmiCallbackExporter exporter = new WlcfgRmiCallbackExporter();
this.invocationHandler = exporter.getRmiInvocationHandler(callback,
callbackInterface);
}

public Proxy getProxy() {
Proxy proxy = (Proxy) ProxyFactory.getProxy(callbackInterface, this);
return proxy;
}

@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
try {
return invocationHandler.invoke(createRemoteInvocation(invocation));
} catch (RemoteException re) {
throw RmiClientInterceptorUtils.convertRmiAccessException(
invocation.getMethod(), re, RmiClientInterceptorUtils.isConnectFailure(re), ClassUtils.getShortName(callbackInterface));
} catch (InvocationTargetException ex) {
Throwable targetEx = ex.getTargetException();
RemoteInvocationUtils.fillInClientStackTraceIfPossible(targetEx);
throw targetEx;
}
}
}

public class WlcfgRmiCallbackExporter extends RmiBasedExporter {

private static Log log = LogFactory.getLog(WlcfgRmiCallbackExporter.class);

public RmiInvocationHandler getRmiInvocationHandler(Object callback, Class callbackInterface) {
this.setService(callback);
this.setServiceInterface(callbackInterface);
this.setRegisterTraceInterceptor(false);

// wrap non-RMI service into a remote object
Remote exportedObject = getObjectToExport();

try {
UnicastRemoteObject.exportObject(exportedObject, 0);
} catch(RemoteException e) {
log.error("Failed to export " + getServiceInterface().getName(), e);
}

log.info("Export object " + getServiceInterface().getName());
return (RmiInvocationHandler)exportedObject;
}
}


There are two types of proxy: JDK dynamic proxies or CGLIB. Spring can use both for creating proxies at runtime. One is based on interface, the other for concrete class as discussed in here. With the invocation of ProxyFactory.getProxy(), a Proxy class is generated at runtime which implements all of the supplied interfaces. It is fancy that the returned proxy is an instance of the interface and can be casted as:

WlcfgRmiCallbackProxyFactory proxyFactory = new WlcfgRmiCallbackProxyFactory(this, IHostSessionListener.class);
hostService.registerForNofitications((IHostSessionListener)proxyFactory.getProxy());

Two common usages of proxies are demonstrated above:
- Line 18, add interceptor so that a normal method call becomes remote call.
- Line 47, limit method access to target source object - only methods defined in serviceInterface are exposed. Otherwise, with reflection method call, all methods would be exposed.

Thursday, September 20, 2007

Map representation in Spring & Javascript


<bean id="action2Dashboard" class="java.util.HashMap" singleton="true">
   <constructor-arg>
     <map>
       <entry key="/admin/license/installLicense.action">
       <value>system:administration_setupsupport.3</value>
       </entry>
       <entry key="/admin/agent/listAgents.action">
       <value>system:administration_agents.5</value>
     </entry>
     </map>
   </constructor-arg>
</bean>


self.dashboards = {
  "/admin/license/installLicense.action" : "system:administration_setupsupport.3",
  "/admin/agent/listAgents.action" : "system:administration_agents.5"
}

var dashboard = dashboards["/admin/licnese/installLicense.action"];