package org.jboss.ha.framework.server;
import java.net.InetAddress;
import java.rmi.dgc.VMID;
import java.rmi.server.UID;
import java.util.Vector;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import org.jboss.ha.framework.interfaces.HAPartition;
import org.jboss.naming.NamingServiceMBean;
import org.jboss.system.ServiceMBean;
import org.jboss.system.ServiceMBeanSupport;
import org.jboss.system.server.ServerConfigUtil;
import org.jgroups.Channel;
import org.jgroups.Version;
import org.jgroups.debug.Debugger;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class ClusterPartition
extends ServiceMBeanSupport
implements ClusterPartitionMBean
{
protected String partitionName = ServerConfigUtil.getDefaultPartitionName();
protected String jgProps =
"UDP(mcast_addr=228.1.2.3;mcast_port=45566;ip_ttl=64;" +
"mcast_send_buf_size=150000;mcast_recv_buf_size=80000):" +
"PING(timeout=2000;num_initial_members=3):" +
"MERGE2(min_interval=5000;max_interval=10000):" +
"FD:" +
"VERIFY_SUSPECT(timeout=1500):" +
"pbcast.NAKACK(gc_lag=50;retransmit_timeout=300,600,1200,2400,4800;max_xmit_size=8192):" +
"UNICAST(timeout=600,1200,2400):" +
"pbcast.STABLE(desired_avg_gossip=20000):" +
"FRAG(down_thread=false;up_thread=false;frag_size=8192):" +
"pbcast.GMS(join_timeout=5000;join_retry_timeout=2000;" +
"shun=false;print_local_addr=true):" +
"pbcast.STATE_TRANSFER";
protected HAPartitionImpl partition;
protected boolean deadlock_detection = false;
protected org.jgroups.JChannel channel;
protected Debugger debugger=null;
protected boolean use_debugger=false;
protected String nodeName = null;
protected InetAddress nodeAddress = null;
protected long state_transfer_timeout=60000;
protected long method_call_timeout=60000;
public String getPartitionName()
{
return partitionName;
}
public void setPartitionName(String newName)
{
partitionName = newName;
}
public String getPartitionProperties()
{
return jgProps;
}
public void setPartitionProperties(String newProps)
{
jgProps = newProps;
}
public void setPartitionConfig(Element config)
{
StringBuffer buffer = new StringBuffer();
NodeList stack = config.getChildNodes();
int length = stack.getLength();
for(int s = 0; s < length; s ++)
{
Node node = stack.item(s);
if( node.getNodeType() != Node.ELEMENT_NODE )
continue;
Element tag = (Element) node;
String protocol = tag.getTagName();
buffer.append(protocol);
NamedNodeMap attrs = tag.getAttributes();
int attrLength = attrs.getLength();
if( attrLength > 0 )
buffer.append('(');
for(int a = 0; a < attrLength; a ++)
{
Attr attr = (Attr) attrs.item(a);
String name = attr.getName();
String value = attr.getValue();
buffer.append(name);
buffer.append('=');
buffer.append(value);
if( a < attrLength-1 )
buffer.append(';');
}
if( attrLength > 0 )
buffer.append(')');
buffer.append(':');
}
buffer.setLength(buffer.length()-1);
this.jgProps = buffer.toString();
log.debug("Setting JGProps from xml to: "+jgProps);
}
public String getNodeName()
{
return this.nodeName;
}
public void setNodeName(String node) throws Exception
{
if (this.getState() == ServiceMBean.CREATED ||
this.getState() == ServiceMBean.STARTED ||
this.getState() == ServiceMBean.STARTING)
{
throw new Exception ("Node name cannot be changed once the partition has been started");
}
else
{
this.nodeName = node;
}
}
public InetAddress getNodeAddress()
{
return nodeAddress;
}
public void setNodeAddress(InetAddress address)
{
this.nodeAddress = address;
}
public String getJGroupsVersion()
{
return Version.version + "( " + Version.cvs + ")";
}
public long getStateTransferTimeout()
{
return state_transfer_timeout;
}
public void setStateTransferTimeout(long timeout)
{
this.state_transfer_timeout=timeout;
}
public long getMethodCallTimeout() {
return method_call_timeout;
}
public void setMethodCallTimeout(long timeout) {
this.method_call_timeout=timeout;
}
public boolean getDeadlockDetection()
{
return deadlock_detection;
}
public void setDeadlockDetection(boolean doit)
{
deadlock_detection = doit;
}
protected ObjectName getObjectName(MBeanServer server, ObjectName name)
throws MalformedObjectNameException
{
return name == null ? OBJECT_NAME : name;
}
public HAPartition getHAPartition ()
{
return this.partition;
}
public Vector getCurrentView()
{
return partition.getCurrentView();
}
public String getName()
{
return partitionName;
}
protected void createService()
throws Exception
{
log.debug("Creating JGroups JChannel");
this.channel = new org.jgroups.JChannel(jgProps);
if(use_debugger && debugger == null)
{
debugger=new Debugger(channel);
debugger.start();
}
channel.setOpt(Channel.GET_STATE_EVENTS, new Boolean(true));
channel.setOpt(Channel.AUTO_RECONNECT, new Boolean(true));
channel.setOpt(Channel.AUTO_GETSTATE, new Boolean(true));
log.debug("Creating HAPartition");
partition = new HAPartitionImpl(partitionName, channel, deadlock_detection, getServer());
partition.setStateTransferTimeout(this.state_transfer_timeout);
partition.setMethodCallTimeout(this.method_call_timeout);
log.debug("Initing HAPartition: " + partition);
partition.init();
log.debug("HAPartition initialized");
}
protected void startService()
throws Exception
{
if (this.nodeName == null || "".equals(this.nodeName))
this.nodeName = generateUniqueNodeName ();
java.util.HashMap staticNodeName = new java.util.HashMap();
staticNodeName.put("additional_data", this.nodeName.getBytes());
this.channel.down(new org.jgroups.Event(org.jgroups.Event.CONFIG, staticNodeName));
this.channel.getProtocolStack().flushEvents();
log.debug("Starting ClusterPartition: " + partitionName);
channel.connect(partitionName);
log.debug("Starting channel");
partition.startPartition();
log.debug("Started ClusterPartition: " + partitionName);
}
protected void stopService() throws Exception
{
stopChannelDebugger();
log.debug("Stopping ClusterPartition: " + partitionName);
partition.closePartition();
log.debug("Stopped ClusterPartition: " + partitionName);
}
protected String generateUniqueNodeName () throws Exception
{
String hostIP = null;
InetAddress address = ServerConfigUtil.fixRemoteAddress(nodeAddress);
if (address == null)
{
log.debug ("unable to create a GUID for this cluster, check network configuration is correctly setup (getLocalHost has returned an exception)");
log.debug ("using a full GUID strategy");
return new VMID().toString();
}
else
{
hostIP = address.getHostAddress();
}
try
{
AttributeList al = this.server.getAttributes(NamingServiceMBean.OBJECT_NAME,
new String[] {"State", "Port"});
int status = ((Integer)((Attribute)al.get(0)).getValue()).intValue();
if (status == ServiceMBean.STARTED)
{
int port = ((Integer)((Attribute)al.get(1)).getValue()).intValue();
return hostIP + ":" + port;
}
else
{
log.debug("JNDI has been found but the service wasn't started so we cannot " +
"be entirely sure we are the only one that wants to use this PORT " +
"as a GUID on this host.");
}
}
catch (InstanceNotFoundException e)
{
log.debug ("JNDI not running here, cannot use this strategy to find a node GUID for the cluster");
}
catch (ReflectionException e)
{
log.debug ("JNDI querying has returned an exception, cannot use this strategy to find a node GUID for the cluster");
}
String uid = new UID().toString();
return hostIP + ":" + uid;
}
public String showHistory ()
{
StringBuffer buff = new StringBuffer();
Vector data = new Vector (this.partition.history);
for (java.util.Iterator row = data.iterator(); row.hasNext();)
{
String info = (String) row.next();
buff.append(info).append("\n");
}
return buff.toString();
}
public String showHistoryAsXML ()
{
StringBuffer buff = new StringBuffer();
buff.append("<events>\n");
Vector data = new Vector (this.partition.history);
for (java.util.Iterator row = data.iterator(); row.hasNext();)
{
buff.append(" <event>\n ");
String info = (String) row.next();
buff.append(info);
buff.append("\n </event>\n");
}
buff.append("</events>\n");
return buff.toString();
}
public void startChannelDebugger()
{
startChannelDebugger(false);
}
public void startChannelDebugger(boolean accumulative)
{
if(debugger == null)
{
debugger=new Debugger(this.channel, accumulative);
debugger.start();
}
}
public void stopChannelDebugger()
{
if(debugger != null)
{
debugger=null;
}
}
}