Distributed MBean on WebSphere
Our client wants to keep their current implementation of configuration properties that can be changed at runtime. When a property is changed at runtime, the user clicks a button to flush the in-memory values for properties and reload new properties out of the database. The problem is that this approach only flushes/reloads the in-memory values for the specific node that the user is accessing, so the user needs to keep exiting and then coming back to flush each node in the cluster.
We have setup a POJO that calls the (static) flush/reload method in the configurations class. That "flusher bean" is registered via a Spring MBeanExporter using a WebSphere naming strategy. From my understanding, WAS 6.1 should handle distributing method calls on this bean across the cluster, since it follows the WAS MBean naming standard. When the flush method of this bean is called, it flushes the local node, but does not distribute across the cluster. I have verified that MBeans are being registered to an MBean server locally (so on a non-clustered environment) through Spring log statements and the wsadmin isRegistered command. Any ideas on why the flush method wouldn't be called on every instance of the flusher bean across the cluster?
Spring configuration:
Code:
<... some stuff here...>
<!-- Spring's MBean exporter -->
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
<property name="beans">
<map>
<entry key="propertiesFlusher" value-ref="propertiesFlusher" />
</map>
</property>
<property name="namingStrategy" ref="webSphereNamingStrategy"/>
</bean>
<!-- WebSphere's admin service (used to conform to WAS MBean naming standards) -->
<bean id="adminService" class="com.ibm.websphere.management.AdminServiceFactory" factory-method="getAdminService"/>
<!-- Naming strategy to use WAS naming standard for MBeans that Spring registers with JMX -->
<bean id="webSphereNamingStrategy" class="com.client.WebSphereNamingStrategy">
<constructor-arg index="0" ref="adminService"/>
</bean>
<bean id="propertiesFlusher" class="com.client.PropertiesFlusher" />
<bean name="/servlet/Flush"
class="com.client.PropertyController">
<constructor-arg index="0" ref="propertiesFlusher" />
</bean>
<...some more stuff...>
The WebSphere Naming Strategy:
Code:
public class WebSphereNamingStrategy implements ObjectNamingStrategy
{
private final String domainName;
private final String cellName;
private final String nodeName;
private final String processName;
public WebSphereNamingStrategy(final AdminService adminService) {
this.domainName = adminService.getDefaultDomain();
this.cellName = adminService.getCellName();
this.nodeName = adminService.getNodeName();
this.processName = adminService.getProcessName();
}
public ObjectName getObjectName(Object object, String name)
throws MalformedObjectNameException
{
StringBuffer objectName = new StringBuffer();
objectName.append(domainName);
objectName.append(":cell=");
objectName.append(cellName);
objectName.append(",name=");
objectName.append(name);
objectName.append(",type=");
objectName.append(ClassUtils.getShortName(object.getClass()));
objectName.append(",node=");
objectName.append(nodeName);
objectName.append(",process=");
objectName.append(processName);
return ObjectNameManager.getInstance(objectName.toString());
}
}
The PropertiesFlusher class (note that IPropertiesFlusher extends Serializable):
Code:
public class PropertiesFlusher implements IPropertiesFlusher {
private static final long serialVersionUID = -2906840750693955629L;
private Date flushDate;
public Date getFlushDate() {
return flushDate;
}
public void setFlushDate(Date flushDate) {
this.flushDate = flushDate;
CustomProperties.flushAll(); // this is a static method
}
/**
* Don't think this is necessary, but was thrown in when things weren't
* working. The thought process was that maybe instead of the setter being
* called on each bean linked to the registered MBean, the bean was just
* getting serialized and sent to each node.
*/
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
CustomProperties.flushAll();
this.flushDate = new Date();
}
}
The controller
Code:
public class PropertyController implements Controller {
private IPropertiesFlusher flusher;
public PropertyController (IPropertiesFlusher _flusher){
this.flusher = _flusher;
}
public ModelAndView handleRequest(HttpServletRequest _req,
HttpServletResponse _res) throws Exception {
PrintWriter writer = _res.getWriter();
try{
this.flusher.setFlushDate(new Date());
writer.print("<p>Successfully flushed local node, please check other node via jmx console to confirm propagation flush</p>");
}catch(Exception ex){
writer.println("<p style=\"color: red; weight: bold\">Could not flush the properties</p>");
}
return null;
}
}