Package org.xmlBlaster.engine.cluster

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

/*------------------------------------------------------------------------------
Name:      NodeInfo.java
Project:   xmlBlaster.org
Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
Comment:   Holding information about the current node.
Author:    xmlBlaster@marcelruff.info
------------------------------------------------------------------------------*/
package org.xmlBlaster.engine.cluster;

import java.util.Properties;
import java.util.logging.Logger;
import java.util.logging.Level;

import org.xmlBlaster.client.qos.DisconnectQos;
import org.xmlBlaster.util.Global;
import org.xmlBlaster.util.qos.ConnectQosData;
import org.xmlBlaster.util.qos.ConnectQosSaxFactory;
import org.xmlBlaster.util.qos.address.Address;
import org.xmlBlaster.util.qos.address.AddressBase;
import org.xmlBlaster.util.qos.address.CallbackAddress;
import org.xmlBlaster.util.XmlBlasterException;
import org.xmlBlaster.util.def.Constants;
import org.xmlBlaster.util.def.ErrorCode;
import org.xmlBlaster.util.def.MethodName;
import org.xmlBlaster.util.cluster.NodeId;


import org.xml.sax.Attributes;
import org.xml.sax.SAXException;

/**
* This class holds the address informations about an
* xmlBlaster server instance (=cluster node).
* It is created by the NodeParser from xml markup of by
* the ClusterManager (via ClusterNode) for our local node.
* <pre>
* &lt;!-- Messages of type "__sys__cluster.node.master[heron]": -->
* &lt;connect>
*   &lt;qos>
*   ...
*   &lt;/qos>
* &lt;/connect>
*/
public final class NodeConnectQos
{
   private final String ME;
   /**
    * This util global instance is used for I_XmlBlasterAccess, it
    * uses the specific settings from NodeInfo to connect to the remote node
    */
   private final org.xmlBlaster.util.Global remoteGlob;
   private static Logger log = Logger.getLogger(NodeConnectQos.class.getName());

   private NodeId nodeId;
   private long counter = 0L;

   private ConnectQosSaxFactory connectQosSaxFactory;
   private ConnectQosData connectQosData;

   private DisconnectQos disconnectQos;

   private boolean nameService = false;

   private boolean inConnectQos = false// parsing inside <connect><qos> ?

   /** A unique created session id delivered on callback in update() method */
   private String cbSessionId = null;

   /**
    * Holds the ConnectQos of a node.
    * @param remoteGlob The global specific to this node instance.
    */
   public NodeConnectQos(Global remoteGlob, NodeId nodeId) throws XmlBlasterException {
      this.remoteGlob = remoteGlob;

      this.setNodeId(nodeId);
      this.ME = "NodeInfo." + getId();
      this.connectQosData = new ConnectQosData(this.remoteGlob, this.nodeId);
   }

   /**
    * @return The unique name of the managed xmlBlaster instance e.g. "bilbo.mycompany.com"
    */
   public String getId(){
     return nodeId.getId();
   }

   /**
    * @return The unique name of the managed xmlBlaster instance.
    */
   public NodeId getNodeId() {
     return nodeId;
   }

   /**
    * @return The connection configuration for the remote cluster node, never null
    */  
   public ConnectQosData getConnectQosData() {
      /*
      if (this.connectQosData == null) {
         synchronized (this) {
            if (this.connectQosData == null) {
               this.connectQosData = new ConnectQosData(this.remoteGlob, this.nodeId);
            }
         }
      }
      */
      return this.connectQosData;
   }

   /**
    * TODO: !!!! is this needed?
    * @param The unique name of the managed xmlBlaster instance
    */
   public void setNodeId(NodeId nodeId) {
      if (nodeId == null) throw new IllegalArgumentException("NodeInfo.setNodeId(): NodeId argument is null");
      this.nodeId = nodeId;
   }

   /**
    * The expected callback sessionId which used to authenticate the update() call
    */
   String getCbSessionId() {
      return (this.cbSessionId == null) ? "" : this.cbSessionId;
   }

   /**
    * Access the currently used address to access the node
    * @return null if not specified  !!!!!! TODO: Changed to throws IllegalArgumentException
    */
   private Address getAddress() {
      return getConnectQosData().getAddress();
   }

   /**
    * Add another address for this cluster node.
    * <p />
    * The map is sorted with the same sequence as the given XML sequence
    */
   public void addAddress(Address address){
      // All local plugin configurations are added here if this is the local node
      getConnectQosData().addAddress(address);
   }

   /**
    * Does the given address belong to this node?
    */
   public boolean contains(Address other) {
      return getConnectQosData().contains(other);
   }

   /**
    * Add another callback address for this cluster node.
    */
   public void addCbAddress(CallbackAddress cbAddress) {
      getConnectQosData().addCallbackAddress(cbAddress);
   }

   /**
    * Is the node acting as a preferred cluster naming service.
    * <p />
    * NOTE: This mode is currently not supported
    */
   public boolean isNameService() {
      return nameService;
   }

   /**
    * Tag this node as a cluster naming service.
    * <p />
    * NOTE: This mode is currently not supported
    */
   public void setNameService(boolean nameService) {
      this.nameService = nameService;
   }

   /**
    * Force some cluster specific connection settings.
    */
   private void postInitialize() throws XmlBlasterException
   {
      ConnectQosData data = getConnectQosData();
     
      data.setClusterNode(true);
      this.remoteGlob.setBootstrapAddress(getAddress());

      // Shall we allow a configurable user name for cluster slave logins?
      // Required: To use the cluster.node.id as login name
      //           so other cluster nodes accept our subscriptionId, e.g. "__subId:heron-3456646466"
      //SessionName sessionName = new SessionName(this.getRemoteGlob(), this.remoteGlob.getId() + "/1"); // is done in setUserId already
      //data.getSessionQos().setSessionName(sessionName);
      data.setUserId(this.remoteGlob.getId() + "/1"); // the login name, e.g. "heron/1"
      // The password is from the environment -passwd or more specific -passwd[heron]
      // Or from the XML securityQos

      // Create a secret callback session id to be able to authenticate update() calls
      CallbackAddress callback = data.getCurrentCallbackAddress();
      if (callback != null) {
         if (callback.getSecretSessionId().equals(AddressBase.DEFAULT_sessionId))
            callback.setSecretSessionId(createCbSessionId());
         this.cbSessionId = callback.getSecretSessionId();
         callback.setRetries(-1);
      }
      else {
         log.severe("Internal problem: Expected a callback address setup but none was delivered");
      }

      // As we forward many subscribes probably accessing the
      // same message but only want one update.
      // We cache this update and distribute to all our clients:
      // TODO: Please change to use multiSubscribe=false from SubscribeQos
      //              as an unSubscribe() deletes all subscribes() at once
      //              we have not yet implemented the new desired use of multiSubscribe
      data.setDuplicateUpdates(false);

      data.getSessionQos().setSessionTimeout(0L); // session lasts forever
      data.getSessionQos().clearSessions(true);   // We only login once, kill other (older) sessions of myself!
   }
  
   /**
    * Characters.
    * The text between two tags, in the following example 'Hello':
    * <key>Hello</key>
    */
   public void characters(char ch[], int start, int length, StringBuffer delegateCharacters) {
      if (inConnectQos && this.connectQosSaxFactory != null) {
         this.connectQosSaxFactory.characters(ch, start, length, delegateCharacters);
      }
      else {
         log.severe("Unexpected call during xml parse: " + Global.getStackTraceAsString(null));        
      }
   }

   /**
    * 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(MethodName.CONNECT.getMethodName())) { // "connect"
         inConnectQos = true;
         this.connectQosSaxFactory = new ConnectQosSaxFactory(this.remoteGlob);
         this.connectQosSaxFactory.setConnectQosData(getConnectQosData());
         return true;
      }

      if (inConnectQos) {
         this.connectQosSaxFactory.startElement(uri, localName, name, character, attrs);
         return true;
      }

      return false;
   }

   /**
    * Handle SAX parsed end element
    */
   public final void endElement(String uri, String localName, String name, StringBuffer character)
      throws SAXException {

      try {
         if (inConnectQos) { // delegate to connectQosSaxFactory ...
            if (name.equalsIgnoreCase(MethodName.CONNECT.getMethodName())) { // "connect"
               inConnectQos = false;
               character.setLength(0);
               this.connectQosData = this.connectQosSaxFactory.getConnectQosData();
               this.connectQosSaxFactory = null;
               postInitialize();
            }
            else {
               this.connectQosSaxFactory.endElement(uri, localName, name, character);
            }
            return;
         }
      }
      catch(XmlBlasterException e) {
         throw new SAXException("Cluster node configuration parse error", e);
      }
      finally {
         //character.setLength(0);
      }
      return;
   }

   /**
    * Create a more or less unique sessionId.
    * <p />
    * see Authenticate.java createSessionId() for a discussion
    */
   private String createCbSessionId() throws XmlBlasterException {
      try {
         String ip = this.remoteGlob.getLocalIP();
         java.util.Random ran = new java.util.Random();
         StringBuffer buf = new StringBuffer(512);
         buf.append(Constants.SESSIONID_PREFIX).append(ip).append("-").append(this.remoteGlob.getId()).append("-");
         buf.append(System.currentTimeMillis()).append("-").append(ran.nextInt()).append("-").append((counter++));
         String sessionId = buf.toString();
         if (log.isLoggable(Level.FINE)) log.fine("Created callback sessionId='" + sessionId + "'");
         return sessionId;
      }
      catch (Exception e) {
         String text = "Can't generate a unique callback sessionId: " + e.toString();
         log.severe(text);
         throw new XmlBlasterException(this.remoteGlob, ErrorCode.USER_SECURITY_AUTHENTICATION_ACCESSDENIED, ME, text);
      }
   }

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

   /**
    * Dump state of this object into a XML ASCII string.
    * @param extraOffset indenting of tags for nice output
    */
   public final String toXml(String extraOffset, Properties props) {
      StringBuffer sb = new StringBuffer(512);
      if (extraOffset == null) extraOffset = "";
      String offset = Constants.OFFSET + extraOffset;

      sb.append(offset).append("<").append(MethodName.CONNECT.getMethodName()).append(">");
      sb.append(getConnectQosData().toXml(extraOffset + Constants.INDENT));
      sb.append(offset).append("</").append(MethodName.CONNECT.getMethodName()).append(">");
     
      DisconnectQos dis = getDisconnectQos();
      if (dis != null) {
         sb.append(offset).append("<").append(MethodName.DISCONNECT.getMethodName()).append(">");
         sb.append(dis.toXml(extraOffset + Constants.INDENT, props));
         sb.append(offset).append("</").append(MethodName.DISCONNECT.getMethodName()).append(">");
      }

      /*
      sb.append(offset).append("<info>");
      if (this.connectQosData != null) {
         AddressBase[] arr = this.connectQosData.getAddresses();
         for (int i=0; i<arr.length; i++) {
            sb.append(arr[i].toXml(extraOffset + Constants.INDENT));
         }
      }
      if (cbAddressMap != null && cbAddressMap.size() > 0) {
         Iterator it = cbAddressMap.values().iterator();
         while (it.hasNext()) {
            CallbackAddress info = (CallbackAddress)it.next();
            sb.append(info.toXml(extraOffset + Constants.INDENT));
         }
      }

      if (getBackupnodeMap() != null && getBackupnodeMap().size() > 0) {
         Iterator it = getBackupnodeMap().values().iterator();
         sb.append(offset).append("   <backupnode>");
         while (it.hasNext()) {
            NodeId info = (NodeId)it.next();
            sb.append(offset).append("      <clusternode id='").append(info.getId()).append("'/>");
         }
         sb.append(offset).append("   </backupnode>");
      }
      sb.append(offset).append("</info>");
      */

      return sb.toString();
   }

   /**
    * @return Returns the disconnectQos.
    */
   public DisconnectQos getDisconnectQos() {
      return this.disconnectQos;
   }

   /**
    * @param disconnectQos The disconnectQos to set.
    */
   public void setDisconnectQos(DisconnectQos disconnectQos) {
      this.disconnectQos = disconnectQos;
   }

   /**
    * @return Returns the remoteGlob.
    */
   public org.xmlBlaster.util.Global getRemoteGlob() {
      return this.remoteGlob;
   }
}
TOP

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

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.