Package org.xmlBlaster.util.dispatch.plugins.prio

Source Code of org.xmlBlaster.util.dispatch.plugins.prio.XmlBlasterNativeClient

/*------------------------------------------------------------------------------
Name:      XmlBlasterNativeClient.java
Project:   xmlBlaster.org
Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
------------------------------------------------------------------------------*/
package org.xmlBlaster.util.dispatch.plugins.prio;

import java.util.logging.Logger;
import java.util.logging.Level;
import org.xmlBlaster.util.Global;
import org.xmlBlaster.util.SessionName;
import org.xmlBlaster.util.XmlBlasterException;
import org.xmlBlaster.client.qos.ConnectQos;
import org.xmlBlaster.client.qos.ConnectReturnQos;
import org.xmlBlaster.util.queuemsg.MsgQueueEntry;
import org.xmlBlaster.util.MsgUnit;
import org.xmlBlaster.util.qos.address.Address;
import org.xmlBlaster.util.qos.address.CallbackAddress;
import org.xmlBlaster.util.qos.address.Destination;
import org.xmlBlaster.client.key.SubscribeKey;
import org.xmlBlaster.client.qos.SubscribeQos;
import org.xmlBlaster.client.qos.SubscribeReturnQos;
import org.xmlBlaster.client.key.UnSubscribeKey;
import org.xmlBlaster.client.qos.UnSubscribeQos;
import org.xmlBlaster.client.qos.PublishQos;
import org.xmlBlaster.client.key.UpdateKey;
import org.xmlBlaster.client.I_Callback;
import org.xmlBlaster.client.qos.UpdateQos;
import org.xmlBlaster.client.qos.UpdateReturnQos;
import org.xmlBlaster.client.I_XmlBlasterAccess;
import org.xmlBlaster.util.dispatch.ConnectionStateEnum;
import org.xmlBlaster.client.I_ConnectionStateListener;

import java.util.Map;
import java.util.Set;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;


/**
* Helper class encapsulates xmlBlaster access for PriorizedDispatchPlugin.
* <p>
* We subscribe to a status message which describes the current connection to the remote side.
* </p>
* <p>
* Exactly one instance of this class exists in the Global scope, the shutdown is
* triggered by util.Global using DispatchPluginManager.shutdown()
* </p>
* @author xmlBlaster@marcelruff.info
*/
public final class XmlBlasterNativeClient implements I_Callback
{
   private String ME = "dispatch.plugins.prio.XmlBlasterNativeClient";
   private Global glob;
   private static Logger log = Logger.getLogger(XmlBlasterNativeClient.class.getName());
   /* // Native xmlBlaster access currently not implemented, using remote client xmlBlasterConnection access instead
   private final I_Authenticate authenticate;
   private final I_XmlBlaster xmlBlasterImpl;
   private final String sessionId;
   */
   private I_XmlBlasterAccess xmlBlasterCon;
   private ConnectQos connectQos;
   private ConnectReturnQos conRetQos;
   private boolean connected;
   private String loginName;

   /** The key is a I_Notify instance, the value is a set with SubscriptionReturnQos objects */
   private Map subscriptionsByNotifierMap = new HashMap();
   /** The key is the message oid, the value is a set with listeners */
   private Map oidListenerMap = new HashMap();
   private final String cbSessionId;

   /**
    * Creates a remote client to xmlBlaster.
    */
   public XmlBlasterNativeClient(final Global glob_, PriorizedDispatchPlugin plugin, String sessionId) throws XmlBlasterException {
      this.glob = glob_.getClone(null);

      /*
      this.authenticate = (I_Authenticate)this.glob.getObjectEntry(Constants.I_AUTHENTICATE_PROPERTY_KEY);
      if (this.authenticate == null) {
         throw new IllegalArgumentException(ME + ": The I_Authenticate handle is not registered in the properties, lookup of '" + Constants.I_AUTHENTICATE_PROPERTY_KEY + "' failed");
      }
      this.xmlBlasterImpl = ((org.xmlBlaster.authentication.Authenticate)this.authenticate).getXmlBlaster();
      this.sessionId = sessionId;
      */
      log.info("Connecting to xmlBlaster to subscribe to status messages");

      // Connect as a remote client ...
      xmlBlasterCon = this.glob.getXmlBlasterAccess();
      this.loginName = this.glob.getProperty().get("PriorizedDispatchPlugin.user", "_PriorizedDispatchPlugin");
      String passwd = this.glob.getProperty().get("PriorizedDispatchPlugin.password", "secret");
      this.cbSessionId = passwd;
      this.connectQos = new ConnectQos(this.glob, loginName, passwd);
      this.connectQos.setSessionTimeout(0L);
      this.connectQos.setMaxSessions(this.glob.getProperty().get("PriorizedDispatchPlugin.session.maxSessions", 10));

      Address address = new Address(this.glob);
      address.setDispatchPlugin("undef")// To avoid recursive loading of this PRIO plugin
      address.setDelay(2000L);      // retry connecting every 2 sec
      address.setRetries(-1);       // -1 == forever
      address.setPingInterval(0L)// switched off
      this.connectQos.setAddress(address);

      CallbackAddress cbAddress = new CallbackAddress(this.glob);
      cbAddress.setDispatchPlugin("undef")// To avoid recursive loading of this PRIO plugin
      cbAddress.setSecretSessionId(this.cbSessionId); // to protect our callback server - see method update()
      this.connectQos.addCallbackAddress(cbAddress);

      this.xmlBlasterCon.registerConnectionListener(new I_ConnectionStateListener() {
           
            public void reachedAlive(ConnectionStateEnum oldState, I_XmlBlasterAccess connection) {
               connected = true;
               conRetQos = connection.getConnectReturnQos();
               log.info("I_ConnectionStateListener: We were lucky, connected to " +
                            connection.getGlobal().getId() + " as " + conRetQos.getSessionName());
            }

            public void reachedAliveSync(ConnectionStateEnum oldState, I_XmlBlasterAccess connection) {
            }

            public void reachedPolling(ConnectionStateEnum oldState, I_XmlBlasterAccess connection) {
               log.warning("I_ConnectionStateListener: No connection to " + connection.getGlobal().getId());
               connected = false;
            }

            public void reachedDead(ConnectionStateEnum oldState, I_XmlBlasterAccess connection) {
               log.severe("I_ConnectionStateListener: Connection to " + connection.getGlobal().getId() + " is dead");
               connected = false;
            }
         });

      try {     
         if (log.isLoggable(Level.FINE)) log.fine("Connecting to xmlBlaster as user '" + loginName + "' to subscribe to status messages");
         this.conRetQos = this.xmlBlasterCon.connect(this.connectQos, this);
         this.connected = true;
         this.loginName = conRetQos.getUserId(); // this.connectQos.getUserId();

         log.info("Succefully initialized");
      }
      catch (XmlBlasterException e) {
         log.severe("Can't subscribe to status messages: " + e.getMessage());
      }
   }

   public String getLoginName() {
      return this.loginName;
   }

   /**
    * Send a PtP message to the publisher notifying him on problems about
    * dispatching his just published message.
    * <p>
    * The message oid remains the same as that one published
    * </p>
    * <p>
    * The sender of this PtP message is the loginName of the plugin itself -
    * to avoid looping we check the sender name in our plugin
    * </p>
    */
   public final void sendPtPMessage(MsgQueueEntry entry, String pluginName, String action, String currStatus) throws XmlBlasterException {
      SessionName receiver = entry.getSender()// Send back (receiver==sender)
      if (log.isLoggable(Level.FINE)) log.fine("Sending PtP notification about special message treatment in plugin, dispatcher state=" + currStatus + " receiver '" + receiver + "' ...");
      PublishQos pq = new PublishQos(glob);
      pq.addDestination(new Destination(receiver));
      pq.setSender(new SessionName(glob, getLoginName())); // Set ourself as sender
      pq.setSubscribable(false); // For the time being we don't allow others to subscribe on the PtP notification
      pq.setState(action);
      pq.setStateInfo("Notification about special message treatment in plugin " + pluginName + ", dispatcher state=" + currStatus);
      MsgUnit msgUnit = new MsgUnit(glob, "<key oid='" + entry.getKeyOid() + "'/>", "", pq.toXml());
      //xmlBlasterImpl.publish(sessionId, msgUnit);
      xmlBlasterCon.publish(msgUnit);
   }

   /**
    * We subscribe to the status message (e.g. "_bandwidth.status') which switches our operational mode.
    * Take care not to invoke it twice for the same message oid,
    * on configuration change call unsSubscribeStatusMessage() first.
    */
   public void subscribeToStatusMessage(String msgOid, I_Notify callback) throws XmlBlasterException {
      if (msgOid == null) {
         return;
      }

      synchronized (oidListenerMap) {
         Set listeners = (Set)oidListenerMap.get(msgOid);
         if (listeners != null && listeners.contains(callback))
            return//has subscribed already
      }

      SubscribeKey sk = new SubscribeKey(glob, msgOid);
      SubscribeQos sq = new SubscribeQos(glob);
      //String ret = xmlBlasterImpl.subscribe(sessionId, sk.toXml(), sq.toXml());
      SubscribeReturnQos subscribeReturnQos = xmlBlasterCon.subscribe(sk.toXml(), sq.toXml());

      // Remember subscriptions of this I_Notify instance ...
      synchronized (subscriptionsByNotifierMap) {
         Set subscriptions = (Set)subscriptionsByNotifierMap.get(callback);
         if (subscriptions == null) {
            subscriptions = new HashSet();
            subscriptionsByNotifierMap.put(callback, subscriptions);
         }
         subscriptions.add(subscribeReturnQos);
      }

      // Remember listeners of this msgOid ...
      synchronized (oidListenerMap) {
         Set listeners = (Set)oidListenerMap.get(msgOid);
         if (listeners == null) {
            listeners = new HashSet();
            oidListenerMap.put(msgOid, listeners);
         }
         listeners.add(callback);
      }
   }

   /**
    * Unsubscribe from all status messages, usually if configuration has changed.
    */
   public void unSubscribeStatusMessages(I_Notify callback) {

      // Remove msg oid listeners ...
      synchronized(oidListenerMap) {
         // Slow linear search, but there are not expected to be too many status messages around
         Iterator it = oidListenerMap.values().iterator();

         while (it.hasNext()) {
            Set listeners = (Set)it.next();
            listeners.remove(callback);
         }

         // Cleanup oids with no listeners
         Set emptyOidSet = new HashSet();
         it = oidListenerMap.keySet().iterator();
         while (it.hasNext()) {
            String oid = (String)it.next();
            Set listeners = (Set)oidListenerMap.get(oid);
            if (listeners.size() == 0)
               emptyOidSet.add(oid);
         }
         it = emptyOidSet.iterator();
         while (it.hasNext()) {
            String oid = (String)it.next();
            oidListenerMap.remove(oid);
         }
      }

      // unSubscribe from xmlBlaster ...
      Set subscriptions = null;
      synchronized (subscriptionsByNotifierMap) {
         subscriptions = (Set)subscriptionsByNotifierMap.get(callback);
      }

      if (subscriptions != null) {
         Iterator it = subscriptions.iterator();

         while (it.hasNext()) {
            SubscribeReturnQos subscribeRetQos = (SubscribeReturnQos)it.next();
            try {
               UnSubscribeKey uk = new UnSubscribeKey(glob, subscribeRetQos.getSubscriptionId());
               UnSubscribeQos uq = new UnSubscribeQos(glob);
               //xmlBlasterImpl.unSubscribe(sessionId, uk.toXml(), uq.toXml());
               xmlBlasterCon.unSubscribe(uk.toXml(), uq.toXml());
            }
            catch (XmlBlasterException e) {
               log.warning("Unsubscribe failed: " + e.getMessage());
            }
         }

         subscriptions.clear();
      }
   }

   /**
    * Callback from xmlBlaster core
    */
   public String update(String cbSessionId, UpdateKey updateKey, byte[] content, UpdateQos updateQos) {
      if (!this.cbSessionId.equals(cbSessionId)) {
         log.warning("The given cbSessionId=" + cbSessionId + " is unknown, we don't trust this callback of a status message with oid=" + updateKey.getOid());
         UpdateReturnQos q = new UpdateReturnQos(glob);
         q.setState("ERROR");
         q.setStateInfo("Callback access denied");
         return q.toXml();
      }

      if (updateKey.isInternal()) return "";
      if (updateQos.isErased()) return "";

      String contentStr = new String(content);

      if (!updateQos.isOk()) {
         log.warning("Receiving unexpected asynchronous status message '" + updateKey.getOid() +
                      "' state=" + updateQos.getState() + " with content='" + contentStr + "'");
         return "";
      }

      log.info("Receiving asynchronous status message '" + updateKey.getOid() +
                     "' state=" + updateQos.getState() + " with content='" + contentStr + "'");

      // notify listeners ...
      synchronized (oidListenerMap) {
         Set listeners = (Set)oidListenerMap.get(updateKey.getOid());
         if (listeners != null) {
            Iterator it = listeners.iterator();
            while (it.hasNext()) {
               I_Notify callback = (I_Notify)it.next();
               callback.statusChanged(contentStr);
            }
         }
         else {
            log.warning("Receiving asynchronous status message '" + updateKey.getOid() +
              "' state=" + updateQos.getState() + " with content='" + contentStr + "' but nobody is interested in it");
         }
      }
      return "";
   }

   void shutdown(I_Notify callback) {
      unSubscribeStatusMessages(callback);
      synchronized (subscriptionsByNotifierMap) {
         subscriptionsByNotifierMap.remove(callback);
      }
      synchronized (this) {
         if (subscriptionsByNotifierMap.size() < 1) {
            shutdown();
         }
      }
   }
  
   /**
    * @see I_MsgDispatchInterceptor#shutdown()
    */
   synchronized void shutdown() {
      if (log.isLoggable(Level.FINE)) log.fine("shutdown()");
      //unSubscribeStatusMessages(); -> disconnect() takes care

      synchronized (subscriptionsByNotifierMap) {
         Iterator it = subscriptionsByNotifierMap.values().iterator();
         while (it.hasNext()) {
            ((Set)it.next()).clear();
         }
         subscriptionsByNotifierMap.clear();
      }
     
      synchronized (oidListenerMap) {
         Iterator it = oidListenerMap.values().iterator();
         while (it.hasNext()) {
            ((Set)it.next()).clear();
         }
         oidListenerMap.clear();
      }

      /*
      if (this.sessionId != null) {
         try { authenticate.disconnect(sessionId, (new DisconnectQos(glob)).toXml()); } catch(XmlBlasterException e) { }
      }
      */
      if (xmlBlasterCon != null) {
         xmlBlasterCon.disconnect(null); // does unsubscribe automatically
         xmlBlasterCon = null;
      }
      log.info("Native xmlBlaster access stopped, resources released.");

      this.glob = null;
      //this.authenticate = null;
      //this.xmlBlasterImpl = null;
      this.connectQos = null;
      this.conRetQos = null;
      this.subscriptionsByNotifierMap = null;
      this.oidListenerMap = null;
   }
}
TOP

Related Classes of org.xmlBlaster.util.dispatch.plugins.prio.XmlBlasterNativeClient

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.