除了直接使用在《Tigase Component (一)》当中介绍的那些接口外,Tigase推荐extend一些现成的抽象类,因为它们已经实现了大部分“繁琐和令人厌倦”的接口实现工作。这里有一个你可能会感兴趣的抽象类列表。
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) { list.add(getName(), "Last second packets", packets_per_second, Level.FINE); list.add(getName(), "Last minute packets", packets_per_minute, Level.FINE); list.add(getName(), "Last hour packets", packets_per_hour, Level.FINE); list.add(getName(), StatisticType.MSG_RECEIVED_OK.getDescription(), statReceivedPacketsOk, Level.FINE); list.add(getName(), StatisticType.MSG_SENT_OK.getDescription(), statSentPacketsOk, Level.FINE); if (list.checkLevel(Level.FINEST)) { int[] in_priority_sizes = in_queues.get(0).size(); for (int i = 1; i < in_queues.size(); i++) { int[] tmp_pr_sizes = in_queues.get(i).size(); for (int j = 0; j < tmp_pr_sizes.length; j++) { in_priority_sizes[j] += tmp_pr_sizes[j]; } } int[] out_priority_sizes = out_queue.size(); for (int i = 0; i < in_priority_sizes.length; i++) { Priority queue = Priority.values()[i]; list.add(getName(), "In queue: " + queue.name(), in_priority_sizes[queue.ordinal()], Level.FINEST); list.add(getName(), "Out queue: " + queue.name(), out_priority_sizes[queue.ordinal()], Level.FINEST); } } int in_queue_size = 0; for (PriorityQueueAbstract<Packet> total_size : in_queues) { in_queue_size += total_size.totalSize(); } int out_queue_size = out_queue.totalSize(); list.add(getName(), "Total In queues wait", in_queue_size, Level.INFO); list.add(getName(), "Total Out queues wait", out_queue_size, Level.INFO); list.add(getName(), StatisticType.MAX_QUEUE_SIZE.getDescription(), maxQueueSize, Level.FINEST); list.add(getName(), StatisticType.IN_QUEUE_OVERFLOW.getDescription(), statReceivedPacketsEr, Level.INFO); list.add(getName(), StatisticType.OUT_QUEUE_OVERFLOW.getDescription(), statSentPacketsEr, Level.INFO); long res = 0; for (long ppt : processPacketTimings) { res += ppt; } long prcessingTime = res / processPacketTimings.length; list.add(getName(), "Average processing time on last " + processPacketTimings.length + " runs [ms]", prcessingTime, Level.FINE); for (PacketFilterIfc packetFilter : incoming_filters) { packetFilter.getStatistics(list); } for (PacketFilterIfc packetFilter : outgoing_filters) { packetFilter.getStatistics(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; } //~--- get methods ---------------------------------------------------------- /** * Method description * * @param address * @return */ @Override public boolean isInRegexRoutings(String address) { // log.finest(getName() + " looking for regex routings: " + address); for (Pattern pat : regexRoutings) { if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "{0} matching: {1} against {2}", new Object[]{getName(), address, pat.toString()}); } if (pat.matcher(address).matches()) { return true; } // log.finest(getName() + " matching failed against pattern: " + pat.toString()); } return false; } //~--- methods -------------------------------------------------------------- /** * Method description * * @param prefix * @return */ public String newPacketId(String prefix) { StringBuilder sb = new StringBuilder(32); if (prefix != null) { sb.append(prefix).append("-"); } sb.append(getName()).append(++packetId); return sb.toString(); } /** * Method description * * @param packet * @param results */ @Override public final void processPacket(final Packet packet, final Queue<Packet> results) { addPacketNB(packet); } /** * Method description * * @return */ public int processingThreads() { return 1; } /** * Method description */ @Override public void release() { stop(); } /** * Method description * * @param address * @return */ public boolean removeRegexRouting(String address) { return regexRoutings.remove(Pattern.compile(address, Pattern.CASE_INSENSITIVE)); } //~--- set methods ---------------------------------------------------------- /** * Method description * * @param maxQueueSize */ public void setMaxQueueSize(int maxQueueSize) { if ((this.maxQueueSize != maxQueueSize) || (in_queues.size() == 0)) { // out_queue = PriorityQueueAbstract.getPriorityQueue(pr_cache.length, maxQueueSize); this.maxQueueSize = maxQueueSize / processingThreads(); if (in_queues.size() == 0) { for (int i = 0; i < in_queues_size; i++) { PriorityQueueAbstract<Packet> queue = PriorityQueueAbstract.getPriorityQueue(pr_cache.length, maxQueueSize); in_queues.add(queue); } } else { for (int i = 0; i < in_queues.size(); i++) { in_queues.get(i).setMaxSize(maxQueueSize); } } out_queue.setMaxSize(maxQueueSize); } // end of if (this.maxQueueSize != maxQueueSize) } /** * Method description * * @param name */ @Override public void setName(String name) { super.setName(name); in_queues_size = processingThreads(); setMaxQueueSize(maxQueueSize); } /** * Method description * * @param parent */ @Override public void setParent(MessageReceiver parent) { this.parent = parent; } /** * 为Component设置配置项 * * @param props */ @Override @TODO(note = "Replace fixed filers loading with configurable options for that") public void setProperties(Map<String, Object> props) { super.setProperties(props); int queueSize = (Integer) props.get(MAX_QUEUE_SIZE_PROP_KEY); setMaxQueueSize(queueSize); incoming_filters.clear(); outgoing_filters.clear(); String filters = (String) props.get(INCOMING_FILTERS_PROP_KEY); if ((filters != null) && !filters.trim().isEmpty()) { String[] incoming = filters.trim().split(","); for (String inc : incoming) { try { PacketFilterIfc filter = (PacketFilterIfc) Class.forName(inc).newInstance(); filter.init(getName(), QueueType.IN_QUEUE); incoming_filters.add(filter); log.log(Level.CONFIG, "{0} loaded incoming filter: {1}", new Object[]{getName(), inc}); } catch (Exception e) { log.log(Level.WARNING, "Problem loading filter: " + inc + " in component: " + getName(), e); } } } filters = (String) props.get(OUTGOING_FILTERS_PROP_KEY); if ((filters != null) && !filters.trim().isEmpty()) { String[] outgoing = filters.trim().split(","); for (String out : outgoing) { try { PacketFilterIfc filter = (PacketFilterIfc) Class.forName(out).newInstance(); filter.init(getName(), QueueType.OUT_QUEUE); outgoing_filters.add(filter); log.log(Level.CONFIG, "{0} loaded outgoing filter: {1}", new Object[]{getName(), out}); } catch (Exception e) { log.log(Level.WARNING, "Problem loading filter: " + out + " in component: " + getName(), e); } } } } //~--- methods -------------------------------------------------------------- /** * Method description */ @Override public void start() { if (log.isLoggable(Level.FINER)) { log.log(Level.INFO, "{0}: starting queue management threads ...", getName()); } startThreads(); } /** * Method description */ public void stop() { if (log.isLoggable(Level.FINER)) { log.log(Level.INFO, "{0}: stopping queue management threads ...", getName()); } stopThreads(); } protected boolean addOutPacket(Packet packet) { if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "[{0}] {1}", new Object[]{getName(), packet.toStringSecure()}); } try { out_queue.put(packet, packet.getPriority().ordinal()); ++statSentPacketsOk; } catch (InterruptedException e) { ++statSentPacketsEr; if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "Packet dropped for unknown reason: {0}", packet); } return false; } // end of try-catch return true; } /** * 非阻塞的addOutPacket版本 * * @param packet 一个packet对象 * @return 成功的布尔类型 */ protected boolean addOutPacketNB(Packet packet) { if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "[{0}] {1}", new Object[]{getName(), packet.toStringSecure()}); } boolean result = false; result = out_queue.offer(packet, packet.getPriority().ordinal()); if (result) { ++statSentPacketsOk; } else { // Queue overflow! ++statSentPacketsEr; if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "Packet dropped due to queue overflow: {0}", packet); } } return result; } protected boolean addOutPacketWithTimeout(Packet packet, ReceiverTimeoutHandler handler, long delay, TimeUnit unit) { new PacketReceiverTask(handler, delay, unit, packet); return addOutPacket(packet); } protected boolean addOutPackets(Queue<Packet> packets) { Packet p = null; boolean result = true; while ((p = packets.peek()) != null) { result = addOutPacket(p); if (result) { packets.poll(); } else { return false; } // end of if (result) else } // end of while () return true; } protected void addTimerTask(TimerTask task, long delay, TimeUnit unit) { receiverTasks.schedule(task, unit.toMillis(delay)); } protected void addTimerTask(TimerTask task, long delay) { receiverTasks.schedule(task, delay); } //~--- get methods ---------------------------------------------------------- protected Integer getMaxQueueSize(int def) { return def; } //~--- methods -------------------------------------------------------------- private Packet filterPacket(Packet packet, CopyOnWriteArrayList<PacketFilterIfc> filters) { Packet result = packet; for (PacketFilterIfc packetFilterIfc : filters) { result = packetFilterIfc.filter(result); if (result == null) { break; } } return result; } private void startThreads() { if (threadsQueue == null) { threadsQueue = new ArrayDeque<QueueListener>(8); for (int i = 0; i < in_queues_size; i++) { QueueListener in_thread = new QueueListener(in_queues.get(i), QueueType.IN_QUEUE); in_thread.setName("in_" + i + "-" + getName()); in_thread.start(); threadsQueue.add(in_thread); } } // end of if (thread == null || ! thread.isAlive()) if ((out_thread == null) || !out_thread.isAlive()) { out_thread = new QueueListener(out_queue, QueueType.OUT_QUEUE); out_thread.setName("out_" + getName()); out_thread.start(); } // end of if (thread == null || ! thread.isAlive()) receiverTasks = new Timer(getName() + " tasks", true); receiverTasks.scheduleAtFixedRate(new TimerTask() { @Override public void run() { everySecond(); } }, SECOND, SECOND); receiverTasks.scheduleAtFixedRate(new TimerTask() { @Override public void run() { everyMinute(); } }, MINUTE, MINUTE); receiverTasks.scheduleAtFixedRate(new TimerTask() { @Override public void run() { everyHour(); } }, HOUR, HOUR); } private void stopThreads() { // stopped = true; try { if (threadsQueue != null) { for (QueueListener in_thread : threadsQueue) { in_thread.threadStopped = true; in_thread.interrupt(); while (in_thread.isAlive()) { Thread.sleep(100); } } } if (out_thread != null) { out_thread.threadStopped = true; out_thread.interrupt(); while (out_thread.isAlive()) { Thread.sleep(100); } } } catch (InterruptedException e) { } threadsQueue = null; out_thread = null; if (receiverTasks != null) { receiverTasks.cancel(); receiverTasks = null; } } //~--- inner classes -------------------------------------------------------- private class PacketReceiverTask extends TimerTask { private ReceiverTimeoutHandler handler = null; private String id = null; private Packet packet = null; //~--- constructors ------------------------------------------------------- private PacketReceiverTask(ReceiverTimeoutHandler handler, long delay, TimeUnit unit, Packet packet) { super(); this.handler = handler; this.packet = packet; id = packet.getFrom().toString() + packet.getStanzaId(); waitingTasks.put(id, this); receiverTasks.schedule(this, unit.toMillis(delay)); // log.finest("[" + getName() + "] " + "Added timeout task for: " + id); } //~--- methods ------------------------------------------------------------ /** * Method description * * @param response */ public void handleResponse(Packet response) { this.cancel(); handler.responseReceived(packet, response); } /** * Method description */ public void handleTimeout() { waitingTasks.remove(id); handler.timeOutExpired(packet); } /** * Method description */ @Override public void run() { handleTimeout(); } } private class QueueListener extends Thread { private String compName = null; private QueueType type = null; private boolean threadStopped = false; private PriorityQueueAbstract<Packet> queue; //~--- constructors ------------------------------------------------------- private QueueListener(PriorityQueueAbstract<Packet> q, QueueType type) { this.queue = q; this.type = type; compName = AbstractMessageReceiver.this.getName(); } //~--- methods ------------------------------------------------------------ /** * Method description */ @Override public void run() { if (log.isLoggable(Level.FINEST)) { log.finest(getName() + " starting queue processing."); } Packet packet = null; Queue<Packet> results = new ArrayDeque<Packet>(2); while (!threadStopped) { try { // 现在处理下一个等待的packet packet = queue.take(); switch (type) { case IN_QUEUE: long startPPT = System.currentTimeMillis(); PacketReceiverTask task = null; if (packet.getTo() != null) { String id = packet.getTo().toString() + packet.getStanzaId(); task = waitingTasks.remove(id); } if (task != null) { task.handleResponse(packet); } else { // 也许这是一个本地处理指令 boolean processed = false; if (packet.isCommand() && (packet.getStanzaTo() != null) && compName.equals(packet.getStanzaTo().getLocalpart()) && isLocalDomain(packet.getStanzaTo().getDomain())) { processed = processScriptCommand(packet, results); if (processed) { Packet result = null; while ((result = results.poll()) != null) { addOutPacket(result); } } } if (!processed && ((packet = filterPacket(packet, incoming_filters)) != null)) { processPacket(packet); } processPacketTimings[pptIdx] = System.currentTimeMillis() - startPPT; pptIdx = (pptIdx + 1) % processPacketTimings.length; } break; case OUT_QUEUE: if ((packet = filterPacket(packet, outgoing_filters)) != null) { if (parent != null) { parent.addPacket(packet); } else { // 非阻塞性的addPacket()方法只能出现在MessageRouter当中 addPacketNB(packet); } // end of else } break; default: log.severe("Unknown queue element type: " + type); break; } // end of switch (qel.type) } catch (InterruptedException e) { } catch (Exception e) { log.log(Level.SEVERE, "[" + getName() + "] Exception during packet processing: " + packet, e); } // end of try-catch } // end of while (! threadStopped) } } }
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)方法会被调用。
public abstract class ConnectionManager<IO extends XMPPIOService> extends AbstractMessageReceiver implements XMPPIOServiceListener<IO> { private static final Logger log = Logger.getLogger(ConnectionManager.class.getName()); /** * Field description */ public static final String NET_BUFFER_ST_PROP_KEY = "--net-buff-standard"; /** * Field description */ public static final String NET_BUFFER_HT_PROP_KEY = "--net-buff-high-throughput"; protected static final String PORT_KEY = "port-no"; protected static final String PROP_KEY = "connections/"; protected static final String PORTS_PROP_KEY = PROP_KEY + "ports"; protected static final String PORT_TYPE_PROP_KEY = "type"; protected static final String PORT_SOCKET_PROP_KEY = "socket"; protected static final String PORT_IFC_PROP_KEY = "ifc"; protected static final String PORT_CLASS_PROP_KEY = "class"; protected static final String PORT_REMOTE_HOST_PROP_KEY = "remote-host"; protected static final String PORT_REMOTE_HOST_PROP_VAL = "localhost"; protected static final String TLS_PROP_KEY = PROP_KEY + "tls/"; protected static final String TLS_USE_PROP_KEY = TLS_PROP_KEY + "use"; protected static final boolean TLS_USE_PROP_VAL = true; protected static final String TLS_REQUIRED_PROP_KEY = TLS_PROP_KEY + "required"; protected static final boolean TLS_REQUIRED_PROP_VAL = false; protected static final String MAX_RECONNECTS_PROP_KEY = "max-reconnects"; protected static final String NET_BUFFER_PROP_KEY = "net-buffer"; protected static final int NET_BUFFER_ST_PROP_VAL = 2 * 1024; protected static final int NET_BUFFER_HT_PROP_VAL = 64 * 1024; /** * Field description */ public static final String PORT_LOCAL_HOST_PROP_KEY = "local-host"; private static ConnectionOpenThread connectThread = ConnectionOpenThread.getInstance(); private static SocketReadThread readThread = SocketReadThread.getInstance(); //~--- fields --------------------------------------------------------------- /** * Field description */ public String[] PORT_IFC_PROP_VAL = {"*"}; private int services_size = 0; private Thread watchdog = null; private long watchdogRuns = 0; private long watchdogStopped = 0; private long watchdogTests = 0; private LinkedList<Map<String, Object>> waitingTasks = new LinkedList<Map<String, Object>>(); private ConcurrentHashMap<String, IO> services = new ConcurrentHashMap<String, IO>(); private Set<ConnectionListenerImpl> pending_open = Collections.synchronizedSet(new HashSet<ConnectionListenerImpl>()); protected int net_buffer = NET_BUFFER_ST_PROP_VAL; private boolean initializationCompleted = false; protected long connectionDelay = 2 * SECOND; //~--- methods -------------------------------------------------------------- /** * Method description * * @param serv * @return */ public abstract Queue<Packet> processSocketData(IO serv); /** * Method description * * @param port_props */ public abstract void reconnectionFailed(Map<String, Object> port_props); //~--- get methods ---------------------------------------------------------- protected abstract long getMaxInactiveTime(); protected abstract IO getXMPPIOServiceInstance(); /** * Method description * * @param params * @return */ @Override public Map<String, Object> getDefaults(Map<String, Object> params) { log.config(getName() + " defaults: " + params.toString()); Map<String, Object> props = super.getDefaults(params); props.put(TLS_USE_PROP_KEY, TLS_USE_PROP_VAL); int buffSize = NET_BUFFER_ST_PROP_VAL; if (isHighThroughput()) { buffSize = DataTypes.parseSizeInt((String) params.get(NET_BUFFER_HT_PROP_KEY), NET_BUFFER_HT_PROP_VAL); } else { buffSize = DataTypes.parseSizeInt((String) params.get(NET_BUFFER_ST_PROP_KEY), NET_BUFFER_ST_PROP_VAL); } props.put(NET_BUFFER_PROP_KEY, buffSize); int[] ports = null; String ports_str = (String) params.get("--" + getName() + "-ports"); if (ports_str != null) { String[] ports_stra = ports_str.split(","); ports = new int[ports_stra.length]; int k = 0; for (String p : ports_stra) { try { ports[k++] = Integer.parseInt(p); } catch (Exception e) { log.warning("Incorrect ports default settings: " + p); } } } int ports_size = 0; if (ports != null) { log.config("Port settings preset: " + Arrays.toString(ports)); for (int port : ports) { putDefPortParams(props, port, SocketType.plain); } // end of for (int i = 0; i < idx; i++) props.put(PORTS_PROP_KEY, ports); } else { int[] plains = getDefPlainPorts(); if (plains != null) { ports_size += plains.length; } // end of if (plains != null) int[] ssls = getDefSSLPorts(); if (ssls != null) { ports_size += ssls.length; } // end of if (ssls != null) if (ports_size > 0) { ports = new int[ports_size]; } // end of if (ports_size > 0) if (ports != null) { int idx = 0; if (plains != null) { idx = plains.length; for (int i = 0; i < idx; i++) { ports[i] = plains[i]; putDefPortParams(props, ports[i], SocketType.plain); } // end of for (int i = 0; i < idx; i++) } // end of if (plains != null) if (ssls != null) { for (int i = idx; i < idx + ssls.length; i++) { ports[i] = ssls[i - idx]; putDefPortParams(props, ports[i], SocketType.ssl); } // end of for (int i = 0; i < idx + ssls.length; i++) } // end of if (ssls != null) props.put(PORTS_PROP_KEY, ports); } // end of if (ports != null) } return props; } /** * 生成组件统计信息 * * @param list 是一个统计项列表 */ @Override public void getStatistics(StatisticsList list) { super.getStatistics(list); list.add(getName(), "Open connections", services_size, Level.INFO); if (list.checkLevel(Level.FINEST)) { int waitingToSendSize = 0; for (IO serv : services.values()) { waitingToSendSize += serv.waitingToSendSize(); } list.add(getName(), "Waiting to send", waitingToSendSize, Level.FINEST); } list.add(getName(), "Watchdog runs", watchdogRuns, Level.FINER); list.add(getName(), "Watchdog tests", watchdogTests, Level.FINE); list.add(getName(), "Watchdog stopped", watchdogStopped, Level.FINE); } //~--- methods -------------------------------------------------------------- /** * Method description * * @param binds */ @Override public void initBindings(Bindings binds) { super.initBindings(binds); binds.put(CommandIfc.SERVICES_MAP, services); } /** * Method description */ @Override public void initializationCompleted() { initializationCompleted = true; for (Map<String, Object> params : waitingTasks) { reconnectService(params, connectionDelay); } waitingTasks.clear(); } /** * Method description * * @param serv * @throws IOException */ @Override public void packetsReady(IO serv) throws IOException { writePacketsToSocket(serv, processSocketData(serv)); } /** * Method description * * @param packet */ @Override public void processPacket(Packet packet) { writePacketToSocket(packet); } /** * Method description */ @Override public void release() { releaseListeners(); super.release(); } /** * Method description * * @param service */ @TODO(note = "Do something if service with the same unique ID is already started, " + "possibly kill the old one...") public void serviceStarted(final IO service) { String id = getUniqueId(service); if (log.isLoggable(Level.FINER)) { log.log(Level.FINER, "[[{0}]] Connection started: {1}", new Object[]{getName(), service}); } IO serv = services.get(id); if (serv != null) { if (serv == service) { log.log(Level.WARNING, "{0}: That would explain a lot, adding the same service twice, ID: {1}", new Object[]{getName(), serv}); } else { // Is it at all possible to happen??? // let's log it for now.... log.log(Level.WARNING, "{0}: Attempt to add different service with the same ID: {1}", new Object[]{getName(), service}); // And stop the old service.... serv.stop(); } } services.put(id, service); ++services_size; } /** * @param service * @return */ @Override public boolean serviceStopped(IO service) { String id = getUniqueId(service); if (log.isLoggable(Level.FINER)) { log.log(Level.FINER, "[[{0}]] Connection stopped: {1}", new Object[]{getName(), service}); } if (id != null) { boolean result = services.remove(id, service); if (result) { --services_size; } else { // Is it at all possible to happen??? // let's log it for now.... log.log(Level.WARNING, "[[{0}]] Attempt to stop incorrect service: {1}", new Object[]{getName(), service}); Thread.dumpStack(); } return result; } return false; } //~--- set methods ---------------------------------------------------------- /** * Method description * * @param name */ @Override public void setName(String name) { super.setName(name); watchdog = new Thread(new Watchdog(), "Watchdog - " + name); watchdog.setDaemon(true); watchdog.start(); } /** * Method description * * @param props */ @Override public void setProperties(Map<String, Object> props) { super.setProperties(props); net_buffer = (Integer) props.get(NET_BUFFER_PROP_KEY); releaseListeners(); int[] ports = (int[]) props.get(PORTS_PROP_KEY); if (ports != null) { for (int i = 0; i < ports.length; i++) { Map<String, Object> port_props = new LinkedHashMap<String, Object>(20); for (Map.Entry<String, Object> entry : props.entrySet()) { if (entry.getKey().startsWith(PROP_KEY + ports[i])) { int idx = entry.getKey().lastIndexOf('/'); String key = entry.getKey().substring(idx + 1); log.log(Level.CONFIG, "Adding port property key: {0}={1}", new Object[]{key, entry.getValue()}); port_props.put(key, entry.getValue()); } // end of if (entry.getKey().startsWith()) } // end of for () port_props.put(PORT_KEY, ports[i]); addWaitingTask(port_props); } // end of for (int i = 0; i < ports.length; i++) } // end of if (ports != null) if ((Boolean) props.get(TLS_USE_PROP_KEY)) { Map<String, String> tls_params = new LinkedHashMap<String, String>(20); } // end of if (use.equalsIgnoreCase()) } //~--- methods -------------------------------------------------------------- /** * Method description */ @Override public void start() { super.start(); } /** * Method description * * @param ios * @param p * @return */ public boolean writePacketToSocket(IO ios, Packet p) { if (ios != null) { if (log.isLoggable(Level.FINER) && !log.isLoggable(Level.FINEST)) { log.log(Level.FINER, "{0}, Processing packet: {1}, type: {2}", new Object[]{ios, p.getElemName(), p.getType()}); } if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "{0}, Writing packet: {1}", new Object[]{ios, p}); } ios.addPacketToSend(p); try { ios.processWaitingPackets(); readThread.addSocketService(ios); return true; } catch (Exception e) { log.log(Level.WARNING, ios + "Exception during writing packets: ", e); try { ios.stop(); } catch (Exception e1) { log.log(Level.WARNING, ios + "Exception stopping XMPPIOService: ", e1); } // end of try-catch } // end of try-catch } else { if (log.isLoggable(Level.FINE)) { log.log(Level.FINE, "Can''t find service for packet: <{0}> {1}, service id: {2}", new Object[]{p.getElemName(), p.getTo(), getServiceId(p)}); } } // end of if (ios != null) else return false; } /** * Method description * * @param serv * @param packets */ public void writePacketsToSocket(IO serv, Queue<Packet> packets) { if (serv != null) { if ((packets != null) && (packets.size() > 0)) { Packet p = null; while ((p = packets.poll()) != null) { if (log.isLoggable(Level.FINER) && !log.isLoggable(Level.FINEST)) { log.log(Level.FINER, "{0}, Processing packet: {1}, type: {2}", new Object[]{serv, p.getElemName(), p.getType()}); } if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "{0}, Writing packet: {1}", new Object[]{serv, p}); } serv.addPacketToSend(p); } // end of for () try { serv.processWaitingPackets(); readThread.addSocketService(serv); } catch (Exception e) { log.log(Level.WARNING, serv + "Exception during writing packets: ", e); try { serv.stop(); } catch (Exception e1) { log.log(Level.WARNING, serv + "Exception stopping XMPPIOService: ", e1); } // end of try-catch } // end of try-catch } } else { if (log.isLoggable(Level.FINE)) { log.log(Level.FINE, "Can''t find service for packets: [{0}] ", packets); } } // end of if (ios != null) else } protected void addWaitingTask(Map<String, Object> conn) { if (initializationCompleted) { reconnectService(conn, connectionDelay); } else { waitingTasks.add(conn); } } /** * 返回活动的网络连接数(IOServices). * * @return 活动的网络连接数(IOServices). */ protected int countIOServices() { return services.size(); } /** * 对所有活动的“IOService(活动的网络连接)”对象执行一个由“ServiceChecker”定义的工作 * * @param checker 是一个定义了动作内容的“ServiceChecker”实例 */ protected void doForAllServices(ServiceChecker checker) { for (IO service : services.values()) { checker.check(service); } } //~--- get methods ---------------------------------------------------------- protected int[] getDefPlainPorts() { return null; } protected int[] getDefSSLPorts() { return null; } protected Map<String, Object> getParamsForPort(int port) { return null; } protected String getServiceId(Packet packet) { return getServiceId(packet.getTo()); } protected String getServiceId(JID jid) { return jid.getResource(); } protected String getUniqueId(IO serv) { return serv.getUniqueId(); } protected IO getXMPPIOService(String serviceId) { return services.get(serviceId); } protected IO getXMPPIOService(Packet p) { return services.get(getServiceId(p)); } protected boolean isHighThroughput() { return false; } //~--- methods -------------------------------------------------------------- /** * @param p * @return */ protected boolean writePacketToSocket(Packet p) { IO ios = getXMPPIOService(p); if (ios != null) { return writePacketToSocket(ios, p); } else { return false; } } protected boolean writePacketToSocket(Packet p, String serviceId) { IO ios = getXMPPIOService(serviceId); if (ios != null) { return writePacketToSocket(ios, p); } else { return false; } } protected void writeRawData(IO ios, String data) { try { ios.writeRawData(data); readThread.addSocketService(ios); } catch (Exception e) { log.log(Level.WARNING, ios + "Exception during writing data: " + data, e); try { ios.stop(); } catch (Exception e1) { log.log(Level.WARNING, ios + "Exception stopping XMPPIOService: ", e1); } // end of try-catch } } private void putDefPortParams(Map<String, Object> props, int port, SocketType sock) { log.log(Level.CONFIG, "Generating defaults for port: {0}", port); props.put(PROP_KEY + port + "/" + PORT_TYPE_PROP_KEY, ConnectionType.accept); props.put(PROP_KEY + port + "/" + PORT_SOCKET_PROP_KEY, sock); props.put(PROP_KEY + port + "/" + PORT_IFC_PROP_KEY, PORT_IFC_PROP_VAL); props.put(PROP_KEY + port + "/" + PORT_REMOTE_HOST_PROP_KEY, PORT_REMOTE_HOST_PROP_VAL); props.put(PROP_KEY + port + "/" + TLS_REQUIRED_PROP_KEY, TLS_REQUIRED_PROP_VAL); Map<String, Object> extra = getParamsForPort(port); if (extra != null) { for (Map.Entry<String, Object> entry : extra.entrySet()) { props.put(PROP_KEY + port + "/" + entry.getKey(), entry.getValue()); } // end of for () } // end of if (extra != null) } private void reconnectService(final Map<String, Object> port_props, long delay) { if (log.isLoggable(Level.FINER)) { log.log(Level.FINER, "Reconnecting service for: {0}, scheduling next try in {1}secs", new Object[]{getName(), delay / 1000}); } addTimerTask(new TimerTask() { @Override public void run() { String host = (String) port_props.get(PORT_REMOTE_HOST_PROP_KEY); if (host == null) { host = (String) port_props.get("remote-hostname"); } int port = (Integer) port_props.get(PORT_KEY); if (log.isLoggable(Level.FINE)) { log.log(Level.FINE, "Reconnecting service for component: {0}, to remote host: {1} on port: {2}", new Object[]{getName(), host, port}); } startService(port_props); } }, delay); } private void releaseListeners() { for (ConnectionListenerImpl cli : pending_open) { connectThread.removeConnectionOpenListener(cli); } pending_open.clear(); } private void startService(Map<String, Object> port_props) { ConnectionListenerImpl cli = new ConnectionListenerImpl(port_props); if (cli.getConnectionType() == ConnectionType.accept) { pending_open.add(cli); } connectThread.addConnectionOpenListener(cli); } //~--- inner classes -------------------------------------------------------- private class ConnectionListenerImpl implements ConnectionOpenListener { private Map<String, Object> port_props = null; //~--- constructors ------------------------------------------------------- private ConnectionListenerImpl(Map<String, Object> port_props) { this.port_props = port_props; } //~--- methods ------------------------------------------------------------ /** * Method description * * @param sc */ @SuppressWarnings({"unchecked"}) @Override public void accept(SocketChannel sc) { IO serv = getXMPPIOServiceInstance(); serv.setIOServiceListener(ConnectionManager.this); serv.setSessionData(port_props); try { serv.accept(sc); if (getSocketType() == SocketType.ssl) { serv.startSSL(false); } // end of if (socket == SocketType.ssl) serviceStarted(serv); readThread.addSocketService(serv); } catch (SocketException e) { if (getConnectionType() == ConnectionType.connect) { // 组件服务的接收方还没有准备好? // 让我们等几秒然后再试一下 log.log(Level.FINEST, "Problem reconnecting the service: {0}", serv); boolean reconnect = false; Integer reconnects = (Integer) port_props.get(MAX_RECONNECTS_PROP_KEY); if (reconnects != null) { int recon = reconnects.intValue(); if (recon != 0) { port_props.put(MAX_RECONNECTS_PROP_KEY, (--recon)); reconnect = true; } // end of if (recon != 0) } if (reconnect) { reconnectService(port_props, connectionDelay); } else { reconnectionFailed(port_props); } } else { } } catch (Exception e) { log.log(Level.WARNING, "Can not accept connection.", e); serv.stop(); } // end of try-catch } //~--- get methods -------------------------------------------------------- /** * Method description * * @return */ @Override public ConnectionType getConnectionType() { String type = null; if (port_props.get(PORT_TYPE_PROP_KEY) == null) { log.warning(getName() + ": connection type is null: " + port_props.get(PORT_KEY).toString()); } else { type = port_props.get(PORT_TYPE_PROP_KEY).toString(); } return ConnectionType.valueOf(type); } /** * Method description * * @return */ @Override public String[] getIfcs() { return (String[]) port_props.get(PORT_IFC_PROP_KEY); } /** * Method description * * @return */ @Override public int getPort() { return (Integer) port_props.get(PORT_KEY); } /** * Method description * * @return */ @Override public int getReceiveBufferSize() { return net_buffer; } /** * Method description * * @return */ public SocketType getSocketType() { return SocketType.valueOf(port_props.get(PORT_SOCKET_PROP_KEY).toString()); } /** * Method description * * @return */ @Override public int getTrafficClass() { if (isHighThroughput()) { return IPTOS_THROUGHPUT; } else { return DEF_TRAFFIC_CLASS; } } //~--- methods ------------------------------------------------------------ /** * Method description * * @return */ @Override public String toString() { return port_props.toString(); } } /** * 检查所有已经建立的连接,看看它们当中有没有已经被断开的 */ private class Watchdog implements Runnable { /** * Method description */ @Override public void run() { while (true) { try { // Sleep... Thread.sleep(10 * MINUTE); ++watchdogRuns; // 遍历所有的连接,检查它们是否都真正的活着。尝试对每一个非激活状态的服务 // 每小时(或更久)发送一个空格,关闭那些出现异常的服务。 doForAllServices(new ServiceChecker() { @Override public void check(final XMPPIOService service) { try { if (null != service) { long curr_time = System.currentTimeMillis(); long lastTransfer = service.getLastTransferTime(); if (curr_time - lastTransfer >= getMaxInactiveTime()) { // 停止那些超过了keep-alive时间的非活动服务 if (log.isLoggable(Level.INFO)) { log.info(getName() + ": Max inactive time exceeded, stopping: " + service); } ++watchdogStopped; service.stop(); } else { if (curr_time - lastTransfer >= (29 * MINUTE)) { // 如果连接还活动着,那么至少每小时检查一次 service.writeRawData(" "); ++watchdogTests; } } } } catch (Exception e) { // 关闭服务 try { if (service != null) { log.info(getName() + "Found dead connection, stopping: " + service); ++watchdogStopped; service.forceStop(); } } catch (Exception ignore) { // 如果抛出异常,则什么事都不做 } } } }); } catch (InterruptedException e) { /* Do nothing here */ } } } } }