• tigasecompoent组件(二)


    一、Tigase Component 组件

    一、创建自己的组件需要实现的接口 1、tigase.server.ServerComponent 所有的component都必须实现接口中定义的方法

    /**
     * Interface ServerComponent
     * 这种类型的对象可以被MessageRouter管理.
     * 所有被MessageRouter加载的类必须要实现这个接口。
     */
     public interface ServerComponent {
       void setName(String name);
       String getName();
     
       JID getComponentId();
       void release();
       /**
        * processPacket是所有components都必须实现的阻塞性方法.
        * 这个方法处理packet并且不等待任何资源立即返回results
        *
        */
       void processPacket(Packet packet, Queue results);
       void initializationCompleted();
     }

    2、tigase.server.MessageReceiver

    这个接口extends ServerComponent,所有希望接收数据packets的Component都需要实现接口中定义的方法,比如session manager和c2s connection manager

    /**
     * Interface MessageReceiver
     * 这种类型的对象可以接收消息,它们事实上也可以根据最终地址进行消息路由。
     * 消息在MessageRouter类中被路由到正确的目的地。
     */
    public interface MessageReceiver extends ServerComponent {
     /**
      * 这里缺少方法表述
      *
      * @param packet a Packet value
      * @return 如果packet被成功添加了,返回true。否则否则返回false
      */
      boolean addPacket(Packet packet);
      boolean addPacketNB(Packet packet);
      boolean addPackets(Queue packets);
      BareJID getDefHostName();
      boolean isInRegexRoutings(String address);
      void setParent(MessageReceiver msg_rec);
      void start();  
    }

    3、tigase.conf.Configurable

    如果希望components可以被配置,则需要实现这个接口。在运行时,配置信息会被推送到这种类型的对象。components必须能够在运行时对变更的配置项进行处理,这一点在实现时要留神。

    /**
     * Interface Configurable
     *
     * 实现这个接口的对象实例对象是可配置的。 在Tigase系统中组件是不能够请求配置信息的。
     * 配置信息是可以在任何时候被推送到组件。这种方式提供了运行时的动态配置机制。
     */
    public interface Configurable extends ServerComponent {
     /**
      * 常量 GEN_CONFIG 是所有配置类型的前缀.
      */
    public static final String GEN_CONFIG = "--gen-config";
     /**
      * 常量 GEN_CONFIG_ALL 是所有直接被服务加载的components配置类型的参数名.
      */
    public static final String GEN_CONFIG_ALL = GEN_CONFIG + "-all";
     /**
      * 常量 GEN_CONFIG_SM 是session manager的配置信息参数名,
      * 也是 会预先配置并连接到 “具有ClientConnectionMananger的服务实例”
      * XEP-0114 component的配置信息参数名。
      */
    public static final String GEN_CONFIG_SM = GEN_CONFIG + "-sm";
     /**
      * 常量 GEN_CONFIG_CS 是 ClientConnectionManager 的配置信息参数名。
      * 也是 会预先配置并连接到 “具有SessionManager的服务实例”
      * 的XEP-0114 component的配置信息参数名。
      */
    public static final String GEN_CONFIG_CS = GEN_CONFIG + "-cs";
     /**
      * 常量 GEN_CONFIG_DEF 是 SessionManager,ClientConnectionManager
      * 和ServerConnectionManager的常用配置想参数名。
      */
    public static final String GEN_CONFIG_DEF = GEN_CONFIG + "-default";
     
    public static final String GEN_CONFIG_COMP = GEN_CONFIG + "-comp";
     /**
      * 常量 CLUSTER_MODE 设置集群工作状态,true 或 false
      * 默认状态下 CLUSTER_MODE 为 false
      */
    public static final String CLUSTER_MODE = "--cluster-mode";
     /**
      * 常量 CLUSTER_NODES 用来设置集群中的节点列表
      */
    public static final String CLUSTER_NODES = "--cluster-nodes";
     /** Field description */
    public static final String CLUSTER_LISTEN = "cluster-listen";
     /** Field description */
    public static final String CLUSTER_CONECT = "cluster-connect";
     /** Field description */
    public static final String GEN_CONF = "--gen-";
     /** Field description */
    public static final String GEN_TEST = "--test";
     /** Field description */
    public static final String GEN_COMP_NAME = "--comp-name";
     /** Field description */
    public static final String GEN_COMP_CLASS = "--comp-class";
     /** Field description */
    public static final String GEN_EXT_COMP = "--ext-comp";
     /** Field description */
    public static final String GEN_USER_DB = "--user-db";
     /** Field description */
    public static final String USER_REPO_POOL_CLASS = "--user-repo-pool";
     /** Field description */
    public static final String USER_DOMAIN_POOL_CLASS = "--user-domain-repo-pool";
     /** Field description */
    public static final String GEN_AUTH_DB = "--auth-db";
     /** Field description */
    public static final String AUTH_REPO_POOL_CLASS = "--auth-repo-pool";
     /** Field description */
    public static final String AUTH_DOMAIN_POOL_CLASS = "--auth-domain-repo-pool";
     /** Field description */
    public static final String GEN_USER_DB_URI_PROP_KEY = "user-db-uri";
     /** Field description */
    public static final String GEN_USER_DB_URI = "--" + GEN_USER_DB_URI_PROP_KEY;
     /** Field description */
    public static final String GEN_AUTH_DB_URI = "--auth-db-uri";
     /** Field description */
    public static final String GEN_ADMINS = "--admins";
     /** Field description */
    public static final String GEN_TRUSTED = "--trusted";
     /** Field description */
    public static final String GEN_VIRT_HOSTS = "--virt-hosts";
     /** Field description */
    public static final String GEN_SM_PLUGINS = "--sm-plugins";
     /** Field description */
    public static final String GEN_DEBUG = "--debug";
     /** Field description */
    public static final String GEN_DEBUG_PACKAGES = "--debug-packages";
     /** Field description */
    public static final String GEN_MAX_QUEUE_SIZE = "--max-queue-size";
     /** Field description */
    public static final String GEN_SCRIPT_DIR = "--script-dir";
     /** Field description */
    public static final String GEN_SREC_DB = "--gen-srec-db";
     /** Field description */
    public static final String GEN_SREC_DB_URI = "--gen-srec-db-uri";
     /** Field description */
    public static final String GEN_SREC_ADMINS = "--gen-srec-admins";
     /** Field description */
    public static final String MONITORING = "--monitoring";
     /** Field description */
    public static final String USER_REPO_POOL_SIZE = "--user-repo-pool-size";
     /** Field description */
    public static final String STRINGPREP_PROCESSOR = "--stringprep-processor";
     /** Field description */
    public static final String XML_REPO_CLASS_PROP_VAL = "tigase.db.xml.XMLRepository";
     /** Field description */
    public static final String MYSQL_REPO_CLASS_PROP_VAL = "tigase.db.jdbc.JDBCRepository";
     /** Field description */
    public static final String DERBY_REPO_CLASS_PROP_VAL = "tigase.db.jdbc.JDBCRepository";
     /** Field description */
    public static final String PGSQL_REPO_CLASS_PROP_VAL = "tigase.db.jdbc.JDBCRepository";
     /** Field description */
    public static final String TIGASE_AUTH_REPO_CLASS_PROP_VAL = "tigase.db.jdbc.TigaseAuth";
     /** Field description */
    public static final String TIGASE_CUSTOM_AUTH_REPO_CLASS_PROP_VAL = "tigase.db.jdbc.TigaseCustomAuth";
     /** Field description */
    public static final String DRUPALWP_REPO_CLASS_PROP_VAL = "tigase.db.jdbc.DrupalWPAuth";
     /** Field description */
    public static final String LIBRESOURCE_REPO_CLASS_PROP_VAL = "tigase.db.jdbc.LibreSourceAuth";
     /** Field description */
    public static final String SHARED_USER_REPO_PROP_KEY = "shared-user-repo";
     /** Field description */
    public static final String SHARED_USER_REPO_PARAMS_PROP_KEY = "shared-user-repo-params";
     /** Field description */
    public static final String SHARED_AUTH_REPO_PROP_KEY = "shared-auth-repo";
     /** Field description */
    public static final String SHARED_AUTH_REPO_PARAMS_PROP_KEY = "shared-auth-repo-params";
     /** Field description */
    public static final String XML_REPO_URL_PROP_VAL = "user-repository.xml";
     /** Field description */
    public static final String MYSQL_REPO_URL_PROP_VAL ="jdbc:mysql://localhost/tigase?user=root&password=mypass";
     /** Field description */
    public static final String DERBY_REPO_URL_PROP_VAL = "jdbc:derby:tigase-derbydb;create=true";
     /** Field description */
    public static final String PGSQL_REPO_URL_PROP_VAL = "jdbc:postgresql://localhost/tigase?user=tigase";
     /** Field description */
    public static final String TIGASE_AUTH_REPO_URL_PROP_VAL = "jdbc:mysql://localhost/tigasedb?user=tigase_user&password=mypass";
     /** Field description */
    public static final String DRUPAL_REPO_URL_PROP_VAL = "jdbc:mysql://localhost/drupal?user=root&password=mypass";
     /** Field description */
    public static final String LIBRESOURCE_REPO_URL_PROP_VAL = "jdbc:postgresql://localhost/libresource?user=demo";
     /** Field description */
    public static final String DEF_SM_NAME = "sess-man";
     /** Field description */
    public static final String DEF_MONITOR_NAME = "monitor";
     /** Field description */
    public static final String DEF_C2S_NAME = "c2s";
     /** Field description */
    public static final String DEF_S2S_NAME = "s2s";
     /** Field description */
    public static final String DEF_EXT_COMP_NAME = "ext-comp";
     /** Field description */
    public static final String DEF_COMP_PROT_NAME = "ext";
     /** Field description */
    public static final String DEF_CL_COMP_NAME = "cl-comp";
     /** Field description */
    public static final String DEF_SSEND_NAME = "ssend";
     /** Field description */
    public static final String DEF_SRECV_NAME = "srecv";
     /** Field description */
    public static final String DEF_BOSH_NAME = "bosh";
     /** Field description */
    public static final String DEF_STATS_NAME = "stats";
     /** Field description */
    public static final String DEF_CLUST_CONTR_NAME = "cluster-contr";
     /** Field description */
    public static final String DEF_VHOST_MAN_NAME = "vhost-man";
     /** Field description */
    public static final String ROUTER_COMP_CLASS_NAME = "tigase.server.MessageRouter";
     /** Field description */
    public static final String C2S_COMP_CLASS_NAME = "tigase.server.xmppclient.ClientConnectionManager";
     /** Field description */
    public static final String C2S_CLUST_COMP_CLASS_NAME = "tigase.cluster.ClientConnectionClustered";
     /** Field description */
    public static final String S2S_COMP_CLASS_NAME = "tigase.server.xmppserver.S2SConnectionManager";
     /** Field description */
    public static final String S2S_CLUST_COMP_CLASS_NAME = "tigase.cluster.S2SConnectionClustered";
     /** Field description */
    public static final String SM_COMP_CLASS_NAME = "tigase.server.xmppsession.SessionManager";
     /** Field description */
    public static final String SM_CLUST_COMP_CLASS_NAME = "tigase.cluster.SessionManagerClustered";
     /** Field description */
    public static final String EXT_COMP_CLASS_NAME = "tigase.server.xmppcomponent.ComponentConnectionManager";
     /** Field description */
    public static final String MONITOR_CLASS_NAME = "tigase.server.monitor.MonitorComponent";
     /** Field description */
    public static final String MONITOR_CLUST_CLASS_NAME = "tigase.server.monitor.MonitorClustered";
     /** Field description */
    public static final String COMP_PROT_CLASS_NAME = "tigase.server.ext.ComponentProtocol";
     /** Field description */
    public static final String CL_COMP_CLASS_NAME = "tigase.cluster.ClusterConnectionManager";
     /** Field description */
    public static final String SSEND_COMP_CLASS_NAME = "tigase.server.ssender.StanzaSender";
     /** Field description */
    public static final String SRECV_COMP_CLASS_NAME = "tigase.server.sreceiver.StanzaReceiver";
     /** Field description */
    public static final String BOSH_COMP_CLASS_NAME = "tigase.server.bosh.BoshConnectionManager";
     /** Field description */
    public static final String STATS_CLASS_NAME = "tigase.stats.StatisticsCollector";
     /** Field description */
    public static final String CLUSTER_CONTR_CLASS_NAME = "tigase.cluster.ClusterController";
     /** Field description */
    public static final String VHOST_MAN_CLASS_NAME = "tigase.vhosts.VHostManager";
     /** Field description */
    public static final String USER_REPO_URL_PROP_KEY = "user-repo-url";
     /** Field description */
    public static final String USER_REPO_PARAMS_NODE = "user-repo-params";
     /** Field description */
    public static final String USER_REPO_POOL_SIZE_PROP_KEY = "user-repo-pool-size";
     /** Field description */
    public static final String USER_REPO_DOMAINS_PROP_KEY = "user-repo-domains";
     /** Field description */
    public static final String AUTH_REPO_DOMAINS_PROP_KEY = "auth-repo-domains";
     /** Field description */
    public static final String AUTH_REPO_URL_PROP_KEY = "auth-repo-url";
     /** Field description */
    public static final String AUTH_REPO_PARAMS_NODE = "auth-repo-params";
     /** Field description */
    public static final String HOSTNAMES_PROP_KEY = "hostnames";
     /** Field description */
    public static final String ADMINS_PROP_KEY = "admins";
     /** Field description */
    public static final String TRUSTED_PROP_KEY = "trusted";
     /** Field description */
    public static final String DEF_HOSTNAME_PROP_KEY = "def-hostname";
     /** Field description */
    public static final String COMPONENT_ID_PROP_KEY = "component-id";
     /** Field description */
    public static final String CLUSTER_NODES_PROP_KEY = "cluster-nodes";
     //~--- get methods ----------------------------------------------------------
     /**
       * 返回对象的默认配置信息
       * @param params
       * @return
       */
    Map getDefaults(Map params);
     //~--- set methods ----------------------------------------------------------
     /**
       * 批量设置配置信息
       * @param properties
       */
    void setProperties(Map properties);
    }

    4、tigase.disco.XMPPService

    实现了这个对象的类可以对“ServiceDiscovery”请求做出响应

    /**
    
    Interface XMPPService
    
     
    
    实现了这个接口的对象可以对“ServiceDiscovery”请求做出应答。
    
    ManagerRouter管理所有的“ServiceDiscovery”请求 */ 

    public interface XMPPService extends ServerComponent { /** 一个方便的服务发现信息xmlns字符串常量 / public static final String INFO_XMLNS = "http://jabber.org/protocol/disco#info"; /* 一个方便的服务发现对象xmlns字符串常量 / public static final String ITEMS_XMLNS = "http://jabber.org/protocol/disco#items"; /* 一个方便的服务发现 二进制流特性 常量 / public static final String[] DEF_FEATURES = {INFO_XMLNS, ITEMS_XMLNS}; /* A convenience constant with component features related to ad-hoc commands. */

    public static final String[] CMD_FEATURES = { "http://jabber.org/protocol/commands", "jabber:x:data"}; /** 为component返回服务发现信息。如果jid是null,那么返回top level的服务发现信息。 SessionMananger可以返回top level的服务发现信息,其他component不行。 @param node 服务发现的对象节点,如果是null,则返回top level的服务发现信息 @param jid 服务发现请求的接收者,“被询问人” @param from 服务发现请求的发出者. “询问人”。一些服务发现信息仅能提供给系统管理器 @return returns 一个 XML 元素格式的服务发现数据 */
    Element getDiscoInfo(String node, JID jid, JID from); /** 为component返回服务发现对象。 如果jid是空,则返回top level的服务发现对象, SessionMananger可以返回top level的服务发现对象,其他component则返回null。 @param node 服务发现的对象节点 @param jid 服务发现请求的接收者,“被询问人” @param from 服务发现请求的发出者. “询问人”。一些服务发现对象仅能提供给系统管理器 @return 一个服务发现对象的列表,对于top level请求,返回component自己的服务发现对象 / List getDiscoItems(String node, JID jid, JID from); /* 获取top level的服务发现特性 @param from 请求发出者的地址。 一些服务发现特性仅能返回给系统管理器, 所以component需要检查请求的发起者是不是系统管理器,并返回响应的内容。 @return 服务发现特性列表 */
    List getDiscoFeatures(JID from); }

    5、tigase.stats.StatisticsContainer 实现了这个对象的类可以返回运行时的统计信息。任何一个对象都可以实现这个接口用来收集统计信息

    public interface StatisticsContainer extends ServerComponent {
        public void getStatistics(StatisticsList list);
    }

    二、Tigase Component 继承抽象类

    1、tigase.server.AbstractMessageReceiver – 它已经实现了四个接口:ServerComponent,MessageReceiver,Configurable和StatisticsContainer。它通过自己的多个线程来管理内部数据队列,且能避免死锁。 它使用事件驱动的方式来处理数据,当packet被发送到AbstractMessageReceiver实例的abstract void processPacket(Packet packet)方法时,就立即启动了packet的处理工作。 当然你还是需要实现抽象类当中的抽象方法,如果你还希望输出packet数据(例如当它收到请求时还需要发送响应),可以调用boolean addOutPacket(Packet packet)方法。

    /**
     * 它是所有用来处理用户packet的对象的父类。它使用内部队列和
     * 多个独立线程(线程个数取决于cpu个数)来应对高负载的packet处理工作。如果对它进行适当扩展
     * 还可以通过ad-hoc指令来处理普通的用户和管理员发送的packets。这一类的组件有 MUC,PubSub,SessionManager。
     * 它为管理员发送的ad-hoc指令提供脚本API支持。
     * 它内部使用的优先级队列在某些罕见情况下可能导致packet的顺序重新排列。如果你不允许在部署时
     * 发生这种情况,那么可以使用非优先级队列。队列的长度是有限制的,这取决于可用内存的大小。
     * packet在调用“processPacket(Packet packet)”时被处理,方法的调用是多线程并发处理的。
     */
    public abstract class AbstractMessageReceiver extends BasicComponent implements StatisticsContainer, MessageReceiver {
    /**
     *在component层设置的传入packet过滤器 键名
     */
    public static final String INCOMING_FILTERS_PROP_KEY = "incoming-filters";
    /**
     *被Tigase服务器载入的传入packet过滤器值名
     *这是一个由“,”分割的packet过滤器类名列表,过滤器必须实现“PacketFilterIfc”接口
     */
    public static final String INCOMING_FILTERS_PROP_VAL = "tigase.server.filters.PacketCounter";
    
    /**
     *component的内部队列长度键名,如果修改这个键值会覆写默认的队列长度,在默认情况下
     *这个值会根据内存的大小来进行调整。
     */
    public static final String MAX_QUEUE_SIZE_PROP_KEY = "max-queue-size";
    
    /**
     *最大队列长度的默认值。这个值在服务启动时被计算出来,计算公式为:
     *Runtime.getRuntime().maxMemory() / 400000L
     *你可以通过修改服务配置项“MAX_QUEUE_SIZE_PROP_KEY”的值来修改队列长度
     */
    public static final Integer MAX_QUEUE_SIZE_PROP_VAL = new Long(Runtime.getRuntime().maxMemory()
            / 400000L).intValue();
    
    /**
     *在component层设置的传出packet过滤器键名
     */
    public static final String OUTGOING_FILTERS_PROP_KEY = "outgoing-filters";
    
    /**
     *被Tigase服务器载入的传出packet过滤器值名
     *这是一个由“,”分割的packet过滤器类名列表,过滤器必须实现“PacketFilterIfc”接口
     */
    public static final String OUTGOING_FILTERS_PROP_VAL = "tigase.server.filters.PacketCounter";
    
    /**
     *在时间计算程序中被用到的常量,1秒=1000毫秒
     */
    protected static final long SECOND = 1000;
    
    /**
     *在时间计算程序中被用到的常量,1分钟=60秒
     */
    protected static final long MINUTE = 60 * SECOND;
    
    /**
     *在时间计算程序中被用到的常量,1小时=60分钟
     */
    protected static final long HOUR = 60 * MINUTE;
    
    /**
     *logger
     */
    private static final Logger log = Logger.getLogger("tigase.debug.AbstractMessageReceiver");
    //~--- fields ---------------------------------------------------------------
    
    private int in_queues_size = 1;
    private long last_hour_packets = 0;
    private long last_minute_packets = 0;
    private long last_second_packets = 0;
    protected int maxQueueSize = MAX_QUEUE_SIZE_PROP_VAL;
    private QueueListener out_thread = null;
    private long packetId = 0;
    private long packets_per_hour = 0;
    private long packets_per_minute = 0;
    private long packets_per_second = 0;
    private MessageReceiver parent = null;
    private int pptIdx = 0;
    
    // 队列缓存可以提高程序运行效能
    private final Priority[] pr_cache = Priority.values();
    private final CopyOnWriteArrayList<PacketFilterIfc> outgoing_filters =
            new CopyOnWriteArrayList<PacketFilterIfc>();
    private final PriorityQueueAbstract<Packet> out_queue =
            PriorityQueueAbstract.getPriorityQueue(pr_cache.length, maxQueueSize);
    private final CopyOnWriteArrayList<PacketFilterIfc> incoming_filters =
            new CopyOnWriteArrayList<PacketFilterIfc>();
    private final List<PriorityQueueAbstract<Packet>> in_queues =
            new ArrayList<PriorityQueueAbstract<Packet>>(pr_cache.length);
    private final long[] processPacketTimings = new long[100];
    private Timer receiverTasks = null;
    /**
     *变量statAddedMessagesEr用来记录因为队列满而无法成功添加到队列的消息个数
     */
    private long statReceivedPacketsEr = 0;
    /**
     *变量statAddedMessagesOk用来记录已经成功添加到队列的消息个数
     */
    private long statReceivedPacketsOk = 0;
    private long statSentPacketsEr = 0;
    private long statSentPacketsOk = 0;
    private ArrayDeque<QueueListener> threadsQueue = null;
    private final ConcurrentHashMap<String, PacketReceiverTask> waitingTasks =
            new ConcurrentHashMap<String, PacketReceiverTask>(16, 0.75f, 4);
    private final Set<Pattern> regexRoutings =
            new ConcurrentSkipListSet<Pattern>(new PatternComparator());
    
    //~--- methods --------------------------------------------------------------
    
    /**
     *这是packet处理的主方法。由于它会被多线程并发调用,因此在实现它的时候要养成首先对packet进行备份的习惯
     *所有被定位到该组件的packet都会通过这个方法被处理。
     *需要提醒的是,packet的实例可能在运行时被多个component,多个plug-in同时处理。
     *所以最好把packet视作为immutable对象
     *这个方法的处理过程是异步的,因此它没有返回值。如果在某些情况下需要返回值,可以把返回值传给
     *addOutPacket(Packet)方法
     *
     *@param packet 作为入口参数的packet实例
     */
    public abstract void processPacket(Packet packet);
    /**
     *这个方法把一个packet实例添加到内部的输入队列。队列里面的packet将异步的传递到“processPacket(Packet)”
     *方法进行处理。当队列满了的时候,这会是一个阻塞性的方法,直到有了足够的空间能够容纳新加入的packet。
     *方法被设计为阻塞性的目的是避免系统过载,并避免因产生压根无法处理的packet时所造成的资源浪费。
     *方法返回布尔类型变量,如果成功添加到队列则返回true,否则返回false。
     *在运行时可以有多个队列和多个线程处理packet,这个方法可以有效保证packet按照正确顺序
     *进行处理。举个例子,同一个用户的的多个packet会出现在同一个队列里面。你可以调整“hashCodeForPacket(Packet)”
     *方法来改变packets的分发策略。如果有N个线程,那么packet的分发会依照下面的逻辑:
     *int threadNo = Math.abs(hashCodeForPacket(packet) % N);
     *大部分的Tigase组件的都会使用这个方法。
     *
     *@param packet 被添加到内部队列的packet实例
     *@return 布尔类型,如果成功返回true,否则返回false
     */
    @Override
    public boolean addPacket(Packet packet) {
        int queueIdx = Math.abs(hashCodeForPacket(packet) % in_queues_size);
    
        if (log.isLoggable(Level.FINEST)) {
            log.log(Level.FINEST, "[{0}] queueIdx={1}, {2}", new Object[]{getName(), queueIdx,
                    packet.toStringSecure()});
        }
    
        try {
            in_queues.get(queueIdx).put(packet, packet.getPriority().ordinal());
            ++statReceivedPacketsOk;
        } catch (InterruptedException e) {
            ++statReceivedPacketsEr;
    
            if (log.isLoggable(Level.FINEST)) {
                log.log(Level.FINEST, "Packet dropped for unknown reason: {0}", packet);
            }
    
            return false;
        }    // end of try-catch
    
        return true;
    }
    
    /**
     * 这是一个添加packet到内部队列的非阻塞性方法。
     * 方法返回布尔型变量,如果成功添加到队列则返回true,否则返回false。
     * 在大多数情况,这个方法是不被推荐的。唯一一个允许使用该方法的组件是“MessageRouter”,
     * 因为它不允许因为任何原因造成阻塞。
     * 使用非阻塞性方法添加packet可能造成死锁,所有非MessageRounter组件必须使用阻塞性方法,
     * 在内部队列满的高负载情况下必须等待。请参考阻塞性“addPacket(Packet packet)”方法
     *
     * @param packet 被添加到内部队列的packet实例
     * @return 布尔类型,如果成功返回true,否则返回false
     *         请参考阻塞式的“addPacket(Packet packet)”方法
     */
    @Override
    public boolean addPacketNB(Packet packet) {
        int queueIdx = Math.abs(hashCodeForPacket(packet) % in_queues_size);
    
        if (log.isLoggable(Level.FINEST)) {
            log.log(Level.FINEST, "[{0}] queueIdx={1}, {2}", new Object[]{getName(), queueIdx,
                    packet.toStringSecure()});
        }
    
        boolean result = in_queues.get(queueIdx).offer(packet, packet.getPriority().ordinal());
    
        if (result) {
            ++statReceivedPacketsOk;
        } else {
    
            // Queue overflow!
            ++statReceivedPacketsEr;
    
            if (log.isLoggable(Level.FINEST)) {
                log.log(Level.FINEST, "Packet dropped due to queue overflow: {0}", packet);
            }
        }
    
        return result;
    }
    
     /**
     * 这是通过传递一个packet队列达到批量添加packet到component内部队列的方法。
     * 这个方法循环调用“addPacket(Packet)”方法
     * 如果入口参数队列中的所有packet对象都被移出并添加到component内部队列,返回true。否则中断
     * 循环,返回false。
     * 需要提醒的是,如果方法返回true,那么入口参数队列中的每一个packet实例都已经被传递
     * 给“addPacket(Packet)”方法,那么这时入口参数队列应该已经被清空。如果返回false,
     * 那么入口参数队列中应该至少含有一个packet没有成功的被addPacket(Packet)方法执行,
     * 和所有压根还没有被来得及被传递给addPacket(Packet)方法的packet。
     *
     * @param packets 一个包含所有需要被添加到内部队列的packet对象队列。所有的packet对象如果由
     *                一个线程处理,那么它们会严格按照在入口参数队列的顺序被处理。请参考“hashCodeForPacket(Packet)”
     *                的方法备注或文档来了解如何分发packet到特定的线程。
     * @return 如果入口参数的队列中的所有packet对象都被移出并添加到component内部队列,返回true。否则
     *         返回false。
     *         请参考“hashCodeForPacket(Packet)”的方法备注或文档来了解如何分发packet到特定的线程。
     */
    @Override
    public boolean addPackets(Queue<Packet> packets) {
        boolean result = true;
        Packet p = packets.peek();
    
        while (p != null) {
            result = addPacket(p);
    
            if (result) {
                packets.poll();
            } else {
                break;
            }    // end of if (result) else
    
            p = packets.peek();
        }      // end of while ()
    
        return result;
    }
    
    /**
     * 这个方法为组件添加一个新的路由地址。MessageRouter会通过路由地址用来计算得出packet的
     * 下一步去向。如果packet的最终地址与一个组件的路由地址匹配。那么这个packet将会被添加到该
     * 组件的内部输入队列。
     * 默认情况下,所有的component接受两种方式的寻址方式,一为组件id
     * 二是component.getName() + '@' + any virtual domain
     *
     * @param address 是一个java正则表达式字符串,所有与该正则表达式匹配的packet地址都将被
     *                component接收。在将来这个地址很可能被调整为any virtual domain + '/' + component.getName()
     *                作为入口参数传递的java正则表达式是component能够接收的packet的地址的严格匹配。在大多数情况下,
     *                这个方法为外部component的协议调用准备的,这个方法为外部component提供了动态修改component的接收
     *                packet地址实现。
     */
    
     public void addRegexRouting(String address) {
        if (log.isLoggable(Level.FINE)) {
            log.log(Level.FINE, "{0} - attempt to add regex routing: {1}", new Object[]{getName(),
                    address});
        }
    
        regexRoutings.add(Pattern.compile(address, Pattern.CASE_INSENSITIVE));
    
        if (log.isLoggable(Level.FINE)) {
            log.log(Level.FINE, "{0} - success adding regex routing: {1}", new Object[]{getName(),
                    address});
        }
    }
    
    /**
     * 清除路由正则表达式方法,将所有的component路由正则表达式地址移除。当这个方法被执行后,
     * component只接收默认的路由地址packet,仍然是两种方式:一为componentid
     * 二是component.getName() + '@' + any virtual domain
     */
    public void clearRegexRoutings() {
        regexRoutings.clear();
    }
    
    
    /**
     * 每个小时都会执行的工具类方法。一个实际的组件可以覆写这个方法,把自己的实现代码放进去,
     * 达到每小时执行一次的效果。
     * 需要提醒的是,复杂的计算或者需要运行很久的操作应该避免被放进这个方法,这个方法的执行需要确保
     * 在一个小时内完成!覆写方法时必须首先调用父类的everyHour()方法,然后执行自己的实现代码。
     */
    public synchronized void everyHour() {
        packets_per_hour = statReceivedPacketsOk - last_hour_packets;
        last_hour_packets = statReceivedPacketsOk;
    }
    
    /**
     * 每分钟都会执行的工具类方法。一个实际的组件可以覆写这个方法,把自己的实现代码放进去,
     * 达到每分钟执行一次的效果。
     * 需要提醒的是,复杂的计算或者需要运行很久的操作应该避免被放进这个方法,这个方法的执行需要确保
     * 在一分钟内完成!覆写方法时必须首先调用父类的everyMinute()方法,然后执行自己的实现代码。
     */
    public synchronized void everyMinute() {
        packets_per_minute = statReceivedPacketsOk - last_minute_packets;
        last_minute_packets = statReceivedPacketsOk;
        receiverTasks.purge();
    }
    
    /**
     * 每一秒都会执行的工具类方法。一个实际的component可以覆写这个方法,把自己的实现代码放进去,
     * 达到每秒执行一次的效果。
     * 需要提醒的是,复杂的计算或者需要运行很久的操作应该避免被放进这个方法,这个方法的执行需要确保
     * 在一秒钟内完成!覆写方法时必须首先调用父类的everySecond()方法,然后执行自己的实现代码。
     */
    public synchronized void everySecond() {
        packets_per_second = statReceivedPacketsOk - last_second_packets;
        last_second_packets = statReceivedPacketsOk;
    }
    /~--- get methods ----------------------------------------------------------
    
    /**
     * 返回组件的缺省配置信息。返回值为Map类型,配置的属性id作为key,属性值作为value。
     * 通过调用该方法获得的缺省配置项将会在之后传递给“setProperties(...)”方法,它们当中的一些
     * 值可能会因为服务器配置文件被改变,配置项的值是一下几种元类型:int,long,boolean,String
     *
     * @param params 是一个在启动服务器时被初始化的属性Map,这些属性将会在生成组件缺省
     *               配置信息的时作为暗示和预设值
     * @return a 组件的默认配置Map
     */
    @Override
    public Map<String, Object> getDefaults(Map<String, Object> params) {
        Map<String, Object> defs = super.getDefaults(params);
        String queueSize = (String) params.get(GEN_MAX_QUEUE_SIZE);
        int queueSizeInt = MAX_QUEUE_SIZE_PROP_VAL;
    
        if (queueSize != null) {
            try {
                queueSizeInt = Integer.parseInt(queueSize);
            } catch (NumberFormatException e) {
                queueSizeInt = MAX_QUEUE_SIZE_PROP_VAL;
            }
        }
    
        defs.put(MAX_QUEUE_SIZE_PROP_KEY, getMaxQueueSize(queueSizeInt));
        defs.put(INCOMING_FILTERS_PROP_KEY, INCOMING_FILTERS_PROP_VAL);
        defs.put(OUTGOING_FILTERS_PROP_KEY, OUTGOING_FILTERS_PROP_VAL);
    
        return defs;
    }
    
    /**
     * 这个方法返回所有欲编译完(正则表达式)之后的组件路由信息。返回值Set的长度可以为0,但是
     * set本身不可以为null。
     *
     * @return 所有欲编译完(正则表达式)之后的组件路由信息,封装为Set
     */
    public Set<Pattern> getRegexRoutings() {
        return regexRoutings;
    }
    
    /**
     * 方法返回组件的统计信息(多个统计项)。需要提醒的是,因为方法可能被服务监控系统每秒
     * 调用一次,因此方法体中不能有复杂/大运算量/耗时久的计算操作。
     * 如果统计信息当中有些项是需要较长时间才能产生的(比如查询数据库),那么它们必须要设置为
     * Level.FINEST,并且把它放到level guard当中,以阻止监控系统要求组件产生这些
     * 统计信息。监控系统不会收集Level.FINEST级别的统计项。
     * level guard的代码看起来就像下面的代码实例一样:
     * if (list.checkLevel(Level.FINEST)) {
     * // 一些cpu密集计算或长耗时的操作
     * list.add(getName(), "Statistic description", stat_value, Level.FINEST);
     * }
     * 这种方法可以避免你的密集计算型操作每秒中都执行,避免影响服务器性能。
     *
     * @param list 是一个StatistcsList实例,它保存了所有的统计项信息
     */
    @Override
    public void getStatistics(StatisticsList list) {
    }
    
     //~--- methods --------------------------------------------------------------
    
    /**
     * 这个方法决定了传入packet被分发给哪个线程进行处理。不同的组件需要不同的分发逻辑,
     * 因为这样可以让所有的线程更有效率的工作,并避免packet重新排序。
     * 如果有N个处理线程,packet的分发规则逻辑如下:
     * int threadNo = Math.abs(hashCodeForPacket(packet) % N);
     * 比如对于PubSub组件,基于PubSub频道名称进行分发是一个较好选择;对于SessionMananger而言
     * 基于目的地地址分发是一个较好选择。
     *
     * @param packet 一个需要多线程处理的packet
     * @return 为线程提供的packet哈希值
     */
    public int hashCodeForPacket(Packet packet) 
        if ((packet.getFrom() != null) && (packet.getFrom() != packet.getStanzaFrom())) {
     
                // 这个packet来源于connection manager,所以最好是通过connectionid生成哈希值,
                // getFrom()方法返回connectionid。
                return packet.getFrom().hashCode();
            }
     
            // 否则,一个较好的方法是通过elemTo地址,因为elemTo地址是目的地用户的名称
            if (packet.getStanzaTo() != null) {
                return packet.getStanzaTo().getBareJID().hashCode();
            }
     
            if (packet.getTo() != null) {
                return packet.getTo().hashCode();
            }
     
            return 1;
     }

    2、tigase.server.ConnectionManager 这是一个extend AbstractMessageReceiver的抽象类。 这个类专注于对连接进行管理工作。如果你的组件需要通过网络直接发送或接受数据(比如c2s connection,s2s connection 或者 连接到外部第三方jabber服务),你可以把它作为基类进行扩展。 它会帮你把所有和网络有关的工作都打理好(例如io,重连,socket监听和连接握手等工作)。如果你extend这个类,你需要知道数据来源于哪里:如果来源于MessageRouter,那么abstract void processPacket(Packet packet)方法会被调用; 如果来源于网络连接,那么abstract Queue processSocketData(XMPPIOService serv)方法会被调用。

    学海无涯、何时是岸
  • 相关阅读:
    mvc中使用Hangfire处理后台任务
    mvc中图片的保存和显示
    WebVTT 及 HTML5 <track> 元素简介
    vs2015发布网站至azure web应用服务
    jquery上传大文件至azure blob存储
    dapper利用DynamicParameters构建动态参数查询
    aspnetcore的那些actionresult们
    vs2015 debugger,unable to attach to application iisexpress.exe
    ASP.NET’s compilation system
    Introducing .NET Standard
  • 原文地址:https://www.cnblogs.com/veblen/p/14703612.html
Copyright © 2020-2023  润新知