package org.sakaiproject.entitybroker.util;
import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.component.cover.ComponentManager;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.aop.target.HotSwappableTargetSource;
import org.springframework.beans.factory.InitializingBean;
/**
* WARNING: Requires Spring 1.2.8 or newer libraries in the classpath <br/>
* Allows you to define a Sakai component in a webapp so it can be reloaded,
* this makes development easier since it does not require all of Sakai to be restarted
* and yet allows you to expose beans to the Sakai component manager<br/>
* <b>WARNING:</b> This is currently experimental as your bean will not be able to be found by the component
* manager while things are starting up (since your webapp will not have loaded up yet),
* it is basically only useful for development and some testing right now
* <br/>
* How to use:<br/>
* 1) Create a bean for the service you want to proxy in your webapp application context (example: myLocalBean)<br/>
* 2) Create a bean in your webapp like so:
<xmp><bean class="org.sakaiproject.entitybroker.util.ReloadableComponentProxy">
<property name="proxyInterfaces" value="org.sakaiproject.myproject.MyService" />
<property name="sakaiComponentName" value="org.sakaiproject.myproject.MyService" />
<property name="localSakaiComponentBean" ref="myLocalBeanName" />
</bean></xmp><br/>
* 3) Put the interface for your service into shared (this has to be the same interface you are registering in proxyInterfaces)<br/>
* 4) Use {@link ComponentManager#get(Class)} to load up the proxied bean in the service/thing that is using your service
* at the point where it is used (not in the init or it will fail):
<xmp>if (webappService == null) {
webappService = (MyService) ComponentManager.get(MyService.class);
}</xmp>
* That's it. Good luck.<br/>
*
* @author Steven Githens (sgithens@caret.cam.ac.uk)
* @author Aaron Zeckoski (aaron@caret.cam.ac.uk)
*/
public class ReloadableComponentProxy extends ProxyFactoryBean implements InitializingBean {
private static Log log = LogFactory.getLog(ReloadableComponentProxy.class);
private String sakaiComponentName;
/**
* @param sakaiComponentName (optional) the name to use for this bean in the component manager,
* if unset then this will use the name of the registered proxyInterface
*/
public void setSakaiComponentName(String sakaiComponentName) {
this.sakaiComponentName = sakaiComponentName;
}
private Object localSakaiComponentBean;
/**
* @param localSakaiComponentBean this is the bean which you want to expose via the proxy
*/
public void setLocalSakaiComponentBean(Object localSakaiComponentBean) {
this.localSakaiComponentBean = localSakaiComponentBean;
}
public void afterPropertiesSet() throws Exception {
// run this when the bean is loaded
// auto set the name if it is not set
if (sakaiComponentName == null || "".equals(sakaiComponentName)) {
Class<?>[] interfaces = getProxiedInterfaces();
if (interfaces.length > 0) {
sakaiComponentName = interfaces[0].getName();
log.info("Autogenerating component name from interface: " + sakaiComponentName);
}
}
// get the component from the Sakai CM if it is in there
Object obj = ComponentManager.get(sakaiComponentName);
/*
* If the obj is null, that means this is the first time we have loaded
* the component, so we will add it to the Component Manager. If this
* component is already available from the component manager, then we
* will simply update it's proxy target.
*/
if (obj == null) {
this.setTargetSource(new HotSwappableTargetSource(localSakaiComponentBean));
ComponentManager.loadComponent(sakaiComponentName, this);
}
else {
try {
Method getTargetSource = obj.getClass().getMethod("getTargetSource");
HotSwappableTargetSource hsts = (HotSwappableTargetSource) getTargetSource.invoke(obj);
hsts.swap(localSakaiComponentBean);
} catch (Exception e) {
log.error("Unable to update reloadable SakaiComponent: " + sakaiComponentName, e);
}
}
log.info("Added component proxy from webapp to component manager: " + sakaiComponentName);
}
}