从3月开始研究Openfire,其实就是要做一套IM系统,也正是这个原因才了解到Openfire。之前还真没想过有这么多的开源产品可以做IM,而且也没想到XMPP这个协议竟然如何强大。看来还是标准为先,好的标准可以推动产业发展啊。

Openfire的搭建与简单的demo之前写过篇《技术笔记:XMPP之openfire+spark+smack》,当时主要关注的怎么让这套体系跑起来吧,只不过现在还是在这个阶段,只是多学了点东西留下点笔记吧。

1、对于XMPP的学习很重要

最开始觉得搭建一套Openfire+spark太简单啦,而且将spark的界面修改一下就可以变成一个新的产品,所以当时觉得XMPP协议这么高深的东西不用太深入。只不过随着简单的事情结束了才发现,最核心的还是协议本身,了解协议可以更了解系统的运作,才能体会到这套系统是有多复杂。当然是对于我来说有点复杂,特别是涉及到前后端结合设计与开始时。

为此我推荐一个国内的XMPP协议翻译网站:http://wiki.jabbercn.org/%E9%A6%96%E9%A1%B5。

当然如果英文好那就原版吧:http://xmpp.org/about/technology-overview.html

经过一段时间学习后,感觉QQ和微信在基础原理上真的和XMPP很类似,只是使用的协议格式有些差别,或许这就是即时通讯的抽象层次吧。但是使用XML这种标记语言是不是很浪费流量呢?虽然XMPP扩展起来非常方便,但是就这些标签也着实够大的,像平常的文字聊天时,或许中间标记产生的流量也和聊天内容相当了。毕竟我还没到这种需要考虑大流量的阶段,所以这只是一个想法而已。

2、Openfire的一些设计点与思路

Openfire的源代码整体看了看还是比较清晰的,扩展上支持插件与组件模式。在最近扩展的中发现openfire的源代码本身不太好去修改,依赖性很强,唯独模块间的依赖比较松散些,模块内的类依赖基本是紧耦合的。只不过Openfire可以通过插件扩展,对源代码本身的依赖就小了许多,所以说整体来说还是很不错的。

在Openfire中的插件扩展方式主要是:

  • IQHandler

在XMPP协议中IQ包是指的信息/查询,可以用于服务器与客户端之间进行数据查询,Openfir中实现了一个IQRouter来处理IQ包。自然IQHandler就是具体的IQ包处理单元啦。IQHandler是基于namespace来进行拦截处理的,自定义自己的命名空间后即可。

IQHandler提供了两个抽象方法,用于派生类实现:

    /**
* Handles the received IQ packet.
*
* @param packet the IQ packet to handle.
* @return the response to send back.
* @throws UnauthorizedException if the user that sent the packet is not
* authorized to request the given operation.
*/
public abstract IQ handleIQ(IQ packet) throws UnauthorizedException; /**
* Returns the handler information to help generically handle IQ packets.
* IQHandlers that aren't local server iq handlers (e.g. chatbots, transports, etc)
* return <tt>null</tt>.
*
* @return The IQHandlerInfo for this handler
*/
public abstract IQHandlerInfo getInfo();

handleIQ方法就是解包和业务处理过程,最后返回结果包。

getInfo是用于返回当前IQHandler的命令空间

然后要使这个handler生效还需要注册到IQRouter,可以在插件启动时创建IQHandler的对象并add进去:

@Override
public void initializePlugin(PluginManager manager, File pluginDirectory) {
iqHandler = new IQGroupChatHander();
iqRouter = XMPPServer.getInstance().getIQRouter();
iqRouter.addHandler(iqHandler);
} @Override
public void destroyPlugin() {
iqRouter.removeHandler(iqHandler);
}
  • Compoent

Compoent也是一种比较常用的扩展方法,可以通过对特定子域的包进行处理。比如MUC通过注册不同的Service,每个Service都有一个subdomain,系统会将不同的subdomain的数据包分发到专门服务中处理。这样就带来了一个好处,可以完全自定义一套子域的包处理业务,后面实现公众号订阅号就想通过这种思路来解决。而且Openfire还有远程组件的机制,可以扩展成为一个独立的业务系统,这样openfire可以只充当消息处理的核心。

具体的应用也比较简单,实现Component接口,并注册到ComponentManager中。而Component接口中最为重要的方法就是processPacket方法,代码如下:

    /**
* Processes a packet sent to this Component.
*
* @param packet the packet.
* @see ComponentManager#sendPacket(Component, Packet)
*/
public void processPacket(Packet packet);

注意方法中的参数是Packet,这个表示所有子域下的通讯原语都可以用于这里处理。

注册与退出的方法如下:

       componentManager = ComponentManagerFactory.getComponentManager();
try {
componentManager.addComponent("subdomain", this);
}
catch (Exception e) {
Log.error(e.getMessage(), e);
System.err.println(e);
} try {
componentManager.removeComponent("subdomain");
} catch (ComponentException e) {
Log.error(e.getMessage(), e);
}
  • PacketInterceptor

在Openfire中所以的传输都是基于packet,在packet上再派生出不同的通讯原语,如message、roster、JID、IQ等等。基于这个原理只要提供一套对于包的拦截机制就可以实现一套比较强大的扩展机制。在Openfire中就提供了这样的机制处理。在IQRouter\PresenceRouter\MessageRouter中都提供了对于包的拦截器。

// Invoke the interceptors before we process the read packet
InterceptorManager.getInstance().invokeInterceptors(packet, session, true, false);

拦截器都注册在InterceptorManager中,在路由处理包时都会调用拦截器,上面的代码就是在路由中截取的代码例子。

那么同样的实现一个拦截器PacketInterceptor接口,并注册到InterceptorManager即可。

InterceptorManager.getInstance().addInterceptor(interceptor);

InterceptorManager.getInstance().removeInterceptor(interceptor);

有了以上三种方法,Openfire可以发展出各种用法,所以官方自己也实现了放多插件供使用。在此也建议对于openfire的扩展最好还是使用插件吧,除非自己的定制要求很高,Openfire本身已经不适应了的。

我的要求基本都可以达成,而且这样以后升级新版本也非常简单,不会出现问题。

3、Spark的纠结

Spark同样出自于jivesoftware,但感觉扩展上就不那么好了。也许是我没有完全弄明白它的扩展原理吧。其实我的需求是重写Spark的UI,同时加入自己的功能,比如群、订阅号等。最开始想着Spark也是支持插件的,但是最后改代码时才发现,里面依赖太深了,基本上和界面相关的都存在依赖,最后可能都要重写一套。

其实在Spark中是有一个UIComponentRegistry类的,一些主要的界面都在这个类中注册的。但可恶的是这些注册的类大多都不能派生出新类来替换这些注册的类。比如

private static Class<? extends ChatRoom> chatRoomClass = ChatRoomImpl.class;

这是聊天窗口的注册类,那么如果我想写一个自己的聊天窗口,是不是直接把这个注册类替换即可呢?不行,因为在其他代码会尽然会这样使用

    @Override
public void filesDropped(Collection<File> files, Component component) {
if (component instanceof ChatRoomImpl) {
ChatRoomImpl roomImpl = (ChatRoomImpl) component; for (File file : files) {
SparkManager.getTransferManager().sendFile(file,
roomImpl.getParticipantJID());
} SparkManager.getChatManager().getChatContainer()
.activateChatRoom(roomImpl);
}
}

在另一个类里尽然直接使用派生类进行了类型判断,这还只是一个点,类似的点太多了。所以最开始想着通过派生UIComponentRegistry中注册的类来达到预期目的已经不大可能了。再加上时间有限,也就懒得管这么多了,就让开发直接在源代码上改。

可恶的是2.7.7版本升级时发现代码大变,这个版本升级smack4.x版本,而且大量使用了1.8的新特性。所以又经过了一番代码合并才升级上来。另外说到smack基本不提供扩展,只提供事件的订阅。

只不过spark是跨平台的,很容易就能在mac下运行,而且代码是java的,暂时还不想抛弃掉,等将来考虑是不是再重写吧。

Openfire阶段实践总结的更多相关文章

  1. openfire的组件(Component)开发

    在之前的文章<Openfire阶段实践总结>中提到过一种openfire的扩展模式Compoent.本文将主要探讨对这种模式的应用与开发方法. 内部与外部组件介绍 在openfire中的许 ...

  2. 分享下对JAVA程序员成长之路的总结<转>

    我也搞了几年JAVA了,由于一向懒惰,没有成为大牛,只是一普通程序猿,手痒来给新人分享下从新手成长为老鸟的已见.   首先初识语法的阶段,必须要学会怎么操作对象,操作if和for,操作list set ...

  3. 【转载】分享下多年积累的对JAVA程序员成长之路的总结

    注:该文是从百度贴吧转载过来,之前看到觉得写得还不错,对Java开发学习者来说很有意义的,可以看看. 我也搞了几年JAVA了,由于一向懒惰,没有成为大牛,只是一普通程序猿,不爱玩社交网站,不爱玩微博, ...

  4. java学习大方向

    总结Java程序员成长之路   转载  http://bbs.javazhijia.com/topic/1bb0733f80d94aedb50cc3b66d9792b6.html 我也搞了几年JAVA ...

  5. Java学习之路(转)

    我也搞了几年JAVA了.因为一向懒惰,没有成为大牛,仅仅是一普通程序员,不爱玩社交站点.不爱玩微博,只有喜欢百度贴吧,潜水非常久了,手痒来给新人分享下从新手成长为老鸟的已见,也刷刷存在感,应该不比曝照 ...

  6. 分享下多年积累的对JAVA程序员成长之路的总结

    http://blog.csdn.net/zhongzelin/article/details/8643269我也搞了几年JAVA了,由于一向懒惰,没有成为大牛,只是一普通程序猿,不爱玩社交网站,不爱 ...

  7. java实习生的成长之路<转>

    首先初识语法的阶段,必须要学会怎么操作对象,操作if和for,操作list set map,然后是线程.IO和jdbc什么的,其余的,若是一时不理解,可以后边需要时再学. 这阶段完了,你可以写些能在控 ...

  8. 阿里云场景化阿里云企业数字化转型售前方法PSA

    阿里云场景化阿里云企业数字化转型售前方法PSA 目录 01 课程收获 理解企业数字化转型的概念.内涵.本质 了解企业数字化转型的要点.目标和切入点 掌握数字化转型项目售前阶段实践方法 场景化方案 阿里 ...

  9. Dockerfile 多阶段构建实践

    写在前面 在Docker Engine 17.05 中引入了多阶段构建,以此降低构建复杂度,同时使缩小镜像尺寸更为简单.这篇小作文我们来学习一下如何编写实现多阶段构建的Dockerfile 关于doc ...

随机推荐

  1. Hello Web API系列教程——Web API与国际化

    软件国际化是在软件设计和文档开发过程中,使得功能和代码设计能处理多种语言和文化习俗,在创建不同语言版本时,不需要重新设计源程序代码的软件工程方法.这在很多成熟的软件开发平台中非常常见.对于.net开发 ...

  2. Objective-C三种定时器CADisplayLink / NSTimer / GCD的使用

    OC中的三种定时器:CADisplayLink.NSTimer.GCD 我们先来看看CADiskplayLink, 点进头文件里面看看, 用注释来说明下 @interface CADisplayLin ...

  3. 浏览器中用JavaScript获取剪切板中的文件

    本文转自我的个人网站  , 原文地址:http://www.zoucz.com/blog/2016/01/29/get-file-from-clipboard/  ,欢迎前往交流讨论 在网页上编辑内容 ...

  4. 独立开发 一个社交 APP 的架构分享 (已实现)

    (本博客为原创:http://www.cnblogs.com/linguanh/)   My BananaCloud Android Application 前言:  这算是我的第一个 完完全全 由自 ...

  5. MSYS2——Windows平台下模拟linux环境的搭建

    最近从MSYS1.0迁移到了MSYS2.0,简单讲,MSYS2.0功能更强大,其环境模拟更加符合linux.虽然本身来自cygwin,但其集成了pacman软件管理工具,很有linux范,并且可以直接 ...

  6. 安装angular-cli

    最近在学习angular2,并尝试用这个框架来做公司的一个新项目. 终于要开始开发了,等了1个多月. 因为第一次用这个新框架做项目,不太熟悉,就找了angular-cli这个脚手架来搭建项目. 安装了 ...

  7. Android菜单项内容大全

    一.介绍: 菜单是许多应用中常见的用户界面组件. Android3.0版本以前,Android设备会提供一个专用"菜单"按钮呈现常用的一些用户操作, Android3.0版本以后, ...

  8. swift 中关于open ,public ,fileprivate,private ,internal,修饰的说明

    关于 swift 中的open ,public ,fileprivate,private, internal的区别 以下按照修饰关键字的访问约束范围 从约束的限定范围大到小的排序进行说明 open,p ...

  9. Oracle中的commit详解

    本文转自 : http://blog.csdn.net/hzhsan/article/details/9719307 它执行的时候,你不会有什么感觉.commit在数据库编程的时候很常用,当你执行DM ...

  10. PowerShell 数组以及XML操作

    PowerShell基础 PowerShell数组操作 将字符串拆分成数据的操作 cls #原始字符串 $str = "abc,def,ghi,mon" #数据定义 #$StrAr ...