Package org.xmlBlaster.engine.cluster

Source Code of org.xmlBlaster.engine.cluster.NodeMasterInfo

/*------------------------------------------------------------------------------
Name:      NodeMasterInfo.java
Project:   xmlBlaster.org
Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
Comment:   Mapping from domain informations to master id
Author:    xmlBlaster@marcelruff.info
------------------------------------------------------------------------------*/
package org.xmlBlaster.engine.cluster;

import java.util.logging.Logger;
import java.util.logging.Level;
import org.xmlBlaster.util.XmlBlasterException;
import org.xmlBlaster.util.XmlBuffer;
import org.xmlBlaster.util.key.QueryKeyData;
import org.xmlBlaster.engine.ServerScope;
import org.xmlBlaster.util.qos.AccessFilterQos;
import org.xmlBlaster.util.cluster.NodeId;
import org.xmlBlaster.util.cluster.RouteInfo;
import org.xmlBlaster.util.context.ContextNode;
import org.xmlBlaster.util.def.Constants;

import org.xml.sax.Attributes;

import java.util.ArrayList;

/**
* Here we have the rules to find out who is the master of a message.
* <p />
* The rules are configurable with such a message:
* <pre>
* &lt;!-- Messages of type "__sys__cluster.node.master[heron]": -->
*
* &lt;master stratum='0' refid='bilbo' type='DomainToMaster' version='1.0'>
*    &lt;![CDATA[
*       &lt;key domain='rugby'/>
*    ]]>
* &lt;/master>
* </pre>
* Here the plugin 'DomainToMaster' with version '1.0' is chosen, this is mapped
* with the xmlBlaster.properties entry
* <pre>
* MapMsgToMasterPlugin[DomainToMaster][1.0]=org.xmlBlaster.engine.cluster.simpledomain.DomainToMaster
* </pre>
* to the real java implementation.
*/
public final class NodeMasterInfo implements Comparable, NodeMasterInfoMBean
{
   /** Unique name for logging */
   private String ME = "NodeMasterInfo";
   private final ServerScope serverScope;
   private static Logger log = Logger.getLogger(NodeMasterInfo.class.getName());
   private final ClusterNode clusterNode;

   private static int counter=0;
   private final int count;

   private int stratum = 0;
   public static final boolean DEFAULT_acceptDefault = true;
   private boolean acceptDefault = DEFAULT_acceptDefault;
   public static final boolean DEFAULT_acceptOtherDefault = false;
   private boolean acceptOtherDefault = DEFAULT_acceptOtherDefault;
   private String refId = null;
   private String type = null;
   /** The version of the plugin */
   public static final String DEFAULT_version = "1.0";
   private String version;

   private boolean dirtyRead = RouteInfo.DEFAULT_dirtyRead;

   /** for SAX parsing */
   private int inMaster = 0;

   private transient AccessFilterQos tmpFilter = null;
   protected ArrayList filterList = null;                   // To collect the <filter> when sax parsing
   protected transient AccessFilterQos[] filterArr = null; // To cache the filters in an array
   protected final transient AccessFilterQos[] filterArr0 = new AccessFilterQos[0];

   private transient QueryKeyData tmpKey = null;
   protected ArrayList keyList = null;                      // To collect the <key> when sax parsing
   private QueryKeyData[] keyArr;
   private final transient QueryKeyData[] keyArr0 = new QueryKeyData[0];
   private transient boolean inKey = false;
  
   private ContextNode contextNode;
   /** My JMX registration */
   private Object mbeanHandle;


   /**
    * Create a NodeMasterInfo belonging to the given cluster node.
    * <p />
    * One instance of this is created for each &lt;master> tag
    */
   public NodeMasterInfo(ServerScope glob, ClusterNode clusterNode) throws XmlBlasterException {
      this.serverScope = glob;

      this.clusterNode = clusterNode;
      this.ME = this.ME + "-" + this.serverScope.getId();

      synchronized (NodeMasterInfo.class) {
         count = counter++;
      }
      version = glob.getProperty().get("cluster.domainMapper.version", DEFAULT_version);
     
      this.contextNode = new ContextNode(ContextNode.CLUSTERMASTER_MARKER_TAG,
            ""+getCount(), clusterNode.getContextNode());
      this.mbeanHandle = this.serverScope.registerMBean(this.contextNode, this);
   }

   /** Unique number (in this JVM) */
   public int getCount() {
      return count;
   }

   /**
    * Access my manager
    * @return The clusterNode which takes care of me
    */
   public ClusterNode getClusterNode() {
      return clusterNode;
   }

   /**
    * Convenience method, delegates to clusterNode.getNodeId().
    * @return My node id object
    */
   public NodeId getNodeId() {
      return clusterNode.getNodeId();
   }

   /**
    * Convenience method, delegates to clusterNode.getNodeId().getId().
    * @return My node id String
    */
   public String getId() {
      return getNodeId().getId();
   }

   /**
    * Get the key based rules
    * @return never null
    */
   public QueryKeyData[] getKeyMappings() {
      if (this.keyArr != null || this.keyList == null || this.keyList.size() < 1) {
         return (this.keyArr == null) ? keyArr0 : this.keyArr;
      }

      synchronized (this.keyList) {
         this.keyArr = new QueryKeyData[this.keyList.size()];
         this.keyList.toArray(this.keyArr);
         return this.keyArr;
      }
   }

   /**
    * Access the cluster master filters
    * @return never null but length == 0 if none is specified.
    */
   public final AccessFilterQos[] getAccessFilterArr() {
      if (this.filterArr != null || this.filterList == null || this.filterList.size() < 1) {
         return (this.filterArr == null) ? filterArr0 : this.filterArr;
      }

      synchronized (this.filterList) {
         this.filterArr = new AccessFilterQos[filterList.size()];
         this.filterList.toArray(filterArr);
         return this.filterArr;
      }
   }

   /**
    * The distance of the node to the master.
    * @param 0 is the master, 1 is the direct slave, 2 is the slave of the slave ...
    */
   public void setStratum(int stratum) {
      if (stratum < 0) throw new IllegalArgumentException("NodeMasterInfo: stratum can't be small zero");
      this.stratum = stratum;
   }

   /**
    * The distance of the node to the master.
    * @return 0 is the master, 1 is the direct slave, 2 is the slave of the slave ...
    */
   public int getStratum() {
      return this.stratum;
   }

   public void setRefId(String refId) {
      this.refId = refId;
   }

   public String getRefId() {
      return this.refId;
   }

   /**
    * The plugin type.
    */
   public void setType(String type) {
      this.type = type;
   }

   /**
    * The plugin type.
    * @return Defaults to "DomainToMaster"
    */
   public String getType() {
      return this.type;
   }

   /**
    * The plugin version.
    */
   public void setVersion(String version) {
      this.version = version;
   }

   /**
    * The plugin version.
    * @return Defaults to "1.0"
    */
   public String getVersion() {
      return this.version;
   }

   /**
    * Are we master for default domains?
    */
   public void setAcceptDefault(boolean acceptDefault) {
      this.acceptDefault = acceptDefault;
   }

   /**
    * Are we master for messages with the default domain?
    */
   public boolean isAcceptDefault() {
      return this.acceptDefault;
   }

   /**
    * Are we master for default domains of other nodes?
    */
   public void setAcceptOtherDefault(boolean acceptOtherDefault) {
      this.acceptOtherDefault = acceptOtherDefault;
   }

   /**
    * Are we master for messages with default domain from other nodes?
    */
   public boolean isAcceptOtherDefault() {
      return this.acceptOtherDefault;
   }

   /**
    * @return true if cluster slaves cache forwarded publish messages
    * @see <a href="http://www.xmlBlaster.org/xmlBlaster/doc/requirements/cluster.dirtyRead.html">The cluster.dirtyRead requirement</a>
    */
   public boolean isDirtyRead() {
      return this.dirtyRead;
   }

   /**
    * @param dirtyRead true if cluster slaves cache forwarded publish messages
    * @see <a href="http://www.xmlBlaster.org/xmlBlaster/doc/requirements/cluster.dirtyRead.html">The cluster.dirtyRead requirement</a>
    */
   public void setDirtyRead(boolean dirtyRead) {
      this.dirtyRead = dirtyRead;
   }

   /**
    * Called for SAX master start tag
    * @return true if ok, false on error
    */
   public final boolean startElement(String uri, String localName, String name, StringBuffer character, Attributes attrs) {
      //log.info(ME, "startElement: name=" + name + " character='" + character.toString() + "'");
      if (name.equalsIgnoreCase("master")) {
         inMaster++;
         if (inMaster > 1) return false; // ignore nested master tags
         if (attrs != null) {
            String tmp = attrs.getValue("stratum");
            if (tmp != null) { try { setStratum(Integer.parseInt(tmp.trim())); } catch(NumberFormatException e) { log.severe("Invalid <master stratum='" + tmp + "'"); }; }
            tmp = attrs.getValue("refid");
            if (tmp != null) setRefId(tmp.trim());
            tmp = attrs.getValue("type");
            if (tmp != null) setType(tmp.trim());
            tmp = attrs.getValue("version");
            if (tmp != null) setVersion(tmp.trim());
            tmp = attrs.getValue("acceptDefault");
            if (tmp != null) { try { setAcceptDefault(Boolean.valueOf(tmp.trim()).booleanValue()); } catch(NumberFormatException e) { log.severe("Invalid <master acceptDefault='" + tmp + "'"); }; }
            tmp = attrs.getValue("acceptOtherDefault");
            if (tmp != null) { try { setAcceptOtherDefault(Boolean.valueOf(tmp.trim()).booleanValue()); } catch(NumberFormatException e) { log.severe("Invalid <master acceptOtherDefault='" + tmp + "'"); }; }
            tmp = attrs.getValue("dirtyRead");
            if (tmp != null) { try { setDirtyRead(Boolean.valueOf(tmp.trim()).booleanValue()); } catch(NumberFormatException e) { log.severe("Invalid <master dirtyRead='" + tmp + "'"); }; }
         }
         character.setLength(0);
         if (getType() == null) {
            log.warning("Missing 'master' attribute 'type', ignoring the master request");
            setType(null);
            return false;
         }
         return true;
      }

      if (inMaster == 1 && name.equalsIgnoreCase("key")) {
         inKey = true;
      }

      if (inMaster == 1 && name.equalsIgnoreCase("filter")) {
         tmpFilter = new AccessFilterQos(serverScope);
         boolean ok = tmpFilter.startElement(uri, localName, name, character, attrs);
         if (ok) {
            if (this.filterList == null) {
               this.filterList = new ArrayList();
               this.filterArr = null;
            }
            filterList.add(tmpFilter);
         }
         else
            tmpFilter = null;
         return ok;
      }

      if (inKey) {
         // Collect everything to pass it later to XmlKey for DOM parsing:
         character.append("<").append(name);
         if (attrs != null) {
            int len = attrs.getLength();
            for (int ii=0; ii<len; ii++) {
                character.append(" ").append(attrs.getQName(ii)).append("='").append(attrs.getValue(ii)).append("'");
            }
         }
         character.append(">");
         return true;
      }

      return false;
   }

   /**
    * Handle SAX parsed end element
    */
   public final void endElement(String uri, String localName, String name, StringBuffer character) {
      //log.info(ME, "endElement: name=" + name + " character='" + character.toString() + "'");
      if (name.equalsIgnoreCase("master")) {
         inMaster--;
         if (inMaster > 0) return; // ignore nested master tags
         character.setLength(0);
      }

      if (inKey)
         character.append("</"+name+">");

      if (inMaster == 1 && name.equalsIgnoreCase("key")) {
         inKey = false;
         if (log.isLoggable(Level.FINE)) log.fine("Parsing filter xmlKey=" + character.toString());
         try {
            tmpKey = serverScope.getQueryKeyFactory().readObject(character.toString()); // Parse it
            if (keyList == null) keyList = new ArrayList();
            keyList.add(tmpKey);
         }
         catch (XmlBlasterException e) {
            log.warning("Parsing <master>" + character.toString() + " failed, ignoring this rule: " + e.toString());
         }
         character.setLength(0);
         return;
      }

      if (inMaster == 1 && name.equalsIgnoreCase("filter")) {
         if (tmpFilter != null)
            tmpFilter.endElement(uri, localName, name, character);
         return;
      }
   }
  
   /**
    * The XML markup for JMX.
    */
   public String getConfiguration() {
      return toXml("", true);
   }
  
   /**
    * Reconfigure node routing via JMX (transient only).
    * Is not activated and not intensively tested
    */
   public String setConfiguration(String xml) {
      return clusterNode.replace(this, xml);
   }
  
   /**
    * For JMX only.
    * @return
    */
   public String destroy() {
      String xml = toXml();
      this.shutdown();
      log.warning("Configuration of '" + getId() + "' is destroyed. Please also change your configuration file to survive xmlBlaster restart: " + xml);
      return "Configuration of '" + getId() + "' is destroyed.\nPlease also change your configuration file to survive xmlBlaster restart";
   }
  
   public void shutdown() {
      Object mbean = this.mbeanHandle;
      ServerScope sc = this.serverScope;
      if (sc != null && mbean != null) {
         this.mbeanHandle = null;
         sc.unregisterMBean(mbean);
      }
      clusterNode.removeNodeMasterInfo(this);
   }

   /**
    * Dump state of this object into a XML ASCII string.
    */
   public final String toXml() {
      return toXml((String)null, false);
   }


   /**
    * Dump state of this object into a XML ASCII string.
    * <br>
    * Only none default values are dumped for performance reasons
    * @param extraOffset indenting of tags for nice output
    * @return The xml representation
    */
   public final String toXml(String extraOffset, boolean forceAllAttributes) {
      XmlBuffer sb = new XmlBuffer(300);
      if (extraOffset == null) extraOffset = "";
      String offset = Constants.OFFSET + extraOffset;

      sb.append(offset).append("<master");
      if (forceAllAttributes || getStratum() > 0)
         sb.append(" stratum='").append(getStratum()).append("'");
      if (getRefId() != null)
         sb.append(" refId='").append(getRefId()).append("'");
      sb.append(" type='").appendAttributeEscaped(getType()).append("'");
      if (forceAllAttributes || !DEFAULT_version.equals(getVersion()))
          sb.append(" version='").append(getVersion()).append("'");
      if (forceAllAttributes || DEFAULT_acceptDefault != isAcceptDefault())
          sb.append(" acceptDefault='").append(isAcceptDefault()).append("'");
      if (forceAllAttributes || DEFAULT_acceptOtherDefault != isAcceptOtherDefault())
          sb.append(" acceptOtherDefault='").append(isAcceptOtherDefault()).append("'");
      if (forceAllAttributes || RouteInfo.DEFAULT_dirtyRead != isDirtyRead())
          sb.append(" dirtyRead='").append(isDirtyRead()).append("'");
      sb.append(">");

      QueryKeyData[] keyArr = getKeyMappings();
      for (int ii=0; ii<keyArr.length; ii++)
         sb.append(keyArr[ii].toXml(extraOffset+Constants.INDENT));

      AccessFilterQos[] filterArr = getAccessFilterArr();
      for (int ii=0; ii<filterArr.length; ii++)
         sb.append(filterArr[ii].toXml(extraOffset+Constants.INDENT));

      sb.append(offset).append("</master>");

      return sb.toString();
   }

   /**
    * Enforced by interface Comparable, does sorting
    * of NodeMasterInfo instances in a treeSet with stratum
    */
   public int compareTo(Object obj)  {
      NodeMasterInfo a = (NodeMasterInfo)obj;
     
      try {
         if (getClusterNode().getConnectionState() != a.getClusterNode().getConnectionState())
            return getClusterNode().getConnectionState() - a.getClusterNode().getConnectionState();
      }
      catch (XmlBlasterException e) {
         log.severe("Unexpected exception in compareTo(), no sorting for connection state possible: " + e.toString());
      }

      if (getStratum() != a.getStratum())
         return getStratum() - a.getStratum();
  
      return getCount() - a.getCount();
   }
}
TOP

Related Classes of org.xmlBlaster.engine.cluster.NodeMasterInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.