﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-DZM.528-随笔分类-IM</title><link>http://www.blogjava.net/duanzhimin528/category/45564.html</link><description>Life is a voyage, not a destination.</description><language>zh-cn</language><lastBuildDate>Thu, 02 Sep 2010 12:48:21 GMT</lastBuildDate><pubDate>Thu, 02 Sep 2010 12:48:21 GMT</pubDate><ttl>60</ttl><item><title>openfire3.6.3插件开发方法</title><link>http://www.blogjava.net/duanzhimin528/archive/2010/07/14/326146.html</link><dc:creator>DZM.528</dc:creator><author>DZM.528</author><pubDate>Wed, 14 Jul 2010 14:53:00 GMT</pubDate><guid>http://www.blogjava.net/duanzhimin528/archive/2010/07/14/326146.html</guid><wfw:comment>http://www.blogjava.net/duanzhimin528/comments/326146.html</wfw:comment><comments>http://www.blogjava.net/duanzhimin528/archive/2010/07/14/326146.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/duanzhimin528/comments/commentRss/326146.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/duanzhimin528/services/trackbacks/326146.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: openfire3.6.3插件开发方法说明： 作用:初始化时在控制台打印测试消息。 开发环境：winxp/myeclipse6.5/jdk1.5&1.6/mysql5.0/openfire3.6.3 QQ群：28588322 (openfire专题研究小组)   开发步骤： 1. 布署openfire3.6.3源码（请参考openfire3.6.3导入ec...&nbsp;&nbsp;<a href='http://www.blogjava.net/duanzhimin528/archive/2010/07/14/326146.html'>阅读全文</a><img src ="http://www.blogjava.net/duanzhimin528/aggbug/326146.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/duanzhimin528/" target="_blank">DZM.528</a> 2010-07-14 22:53 <a href="http://www.blogjava.net/duanzhimin528/archive/2010/07/14/326146.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Openfire插件开发人员指南</title><link>http://www.blogjava.net/duanzhimin528/archive/2010/07/14/326138.html</link><dc:creator>DZM.528</dc:creator><author>DZM.528</author><pubDate>Wed, 14 Jul 2010 14:06:00 GMT</pubDate><guid>http://www.blogjava.net/duanzhimin528/archive/2010/07/14/326138.html</guid><wfw:comment>http://www.blogjava.net/duanzhimin528/comments/326138.html</wfw:comment><comments>http://www.blogjava.net/duanzhimin528/archive/2010/07/14/326138.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/duanzhimin528/comments/commentRss/326138.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/duanzhimin528/services/trackbacks/326138.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 插件是增强Openfire功能。这份文件是一个开发人员创建插件指南。&nbsp;&nbsp;<a href='http://www.blogjava.net/duanzhimin528/archive/2010/07/14/326138.html'>阅读全文</a><img src ="http://www.blogjava.net/duanzhimin528/aggbug/326138.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/duanzhimin528/" target="_blank">DZM.528</a> 2010-07-14 22:06 <a href="http://www.blogjava.net/duanzhimin528/archive/2010/07/14/326138.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开发你自己的XMPP IM 续 - Openfire 插件开发</title><link>http://www.blogjava.net/duanzhimin528/archive/2010/07/12/325911.html</link><dc:creator>DZM.528</dc:creator><author>DZM.528</author><pubDate>Mon, 12 Jul 2010 15:05:00 GMT</pubDate><guid>http://www.blogjava.net/duanzhimin528/archive/2010/07/12/325911.html</guid><wfw:comment>http://www.blogjava.net/duanzhimin528/comments/325911.html</wfw:comment><comments>http://www.blogjava.net/duanzhimin528/archive/2010/07/12/325911.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/duanzhimin528/comments/commentRss/325911.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/duanzhimin528/services/trackbacks/325911.html</trackback:ping><description><![CDATA[
		<a href="http://bangzhuzhongxin.blogbus.com/logs/11205960.html" target="_blank" style="margin: 0px; padding: 0px; text-decoration: none; color: rgb(0, 160, 233);">
		</a>
		<span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Simsun; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;">
				<span class="Apple-style-span" style="color: rgb(51, 51, 51); font-family: Georgia,serif,宋体; font-size: 12px; line-height: 19px; text-align: left;">
						<div class="postBody" style="margin: 10px 0px; padding: 0px 0px 0px 20px; word-wrap: break-word;">
								<p>
										<span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Simsun; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;">
												<span class="Apple-style-span" style="color: rgb(101, 101, 101); font-family: Verdana,Arial,Helvetica,sans-serif; font-size: 14px; line-height: 28px; text-align: left;">此篇文章为《</span>
										</span>
										<span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Simsun; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;">
												<span class="Apple-style-span" style="color: rgb(51, 51, 51); font-family: Georgia,serif,宋体; font-size: 12px; line-height: 19px; text-align: left;">
														<span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Simsun; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;">
																<span class="Apple-style-span" style="color: rgb(101, 101, 101); font-family: Verdana,Arial,Helvetica,sans-serif; font-size: 14px; line-height: 28px; text-align: left;">
																</span>
														</span>
														<font size="3">
																<b>
																		<span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Simsun; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;">
																				<span class="Apple-style-span" style="color: rgb(51, 51, 51); font-family: Georgia,serif,宋体; font-size: 12px; line-height: 19px; text-align: left;">开
发你自己的XMPP IM 续 - Openfire 插件开发</span>
																		</span>
																</b>
														</font>
												</span>
										</span>
										<span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Simsun; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;">
												<span class="Apple-style-span" style="color: rgb(101, 101, 101); font-family: Verdana,Arial,Helvetica,sans-serif; font-size: 14px; line-height: 28px; text-align: left;">》的转发，原文章地址: </span>
										</span>
										<span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Simsun; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;">
												<span class="Apple-style-span" style="color: rgb(51, 51, 51); font-family: Georgia,serif,宋体; font-size: 12px; line-height: 21px; text-align: center;">
														<a href="http://phoenixtoday.blogbus.com/logs/20285574.html" style="margin: 0px; padding: 0px; text-decoration: none; color: rgb(0, 160, 233);">http://phoenixtoday.blogbus.com/logs/20285574.html</a>
												</span>
										</span>
										<font size="3">
												<b>
														<span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Simsun; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;">
																<span class="Apple-style-span" style="color: rgb(51, 51, 51); font-family: Georgia,serif,宋体; font-size: 12px; line-height: 19px; text-align: left;">
																</span>
														</span>
												</b>
										</font>
								</p>
								<span style="margin: 0px; padding: 0px;">继续上一篇的内容，本篇文章介绍开发Openfire 的插件</span>
								<span style="margin: 0px; padding: 0px;">
										<span style="margin: 0px; padding: 0px;">
												<span style="margin: 0px; padding: 0px;">
														<span style="margin: 0px; padding: 0px;">
																<span style="margin: 0px; padding: 0px;">
																		<br style="margin: 0px; padding: 0px;" />
																</span>
														</span>
												</span>
										</span>
								</span>
								<br style="margin: 0px; padding: 0px;" />这篇文章拖了很久了，呵呵，真是千呼万唤始出来呀。Openfire 服务器端是支持插件开发的，开发过程可能会涉及到数据库的操作，本篇文章专注于Openfire 插件的部分，对服务器端涉及到数据库的开发只做简单介绍。<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />Openfire 是一个用Java 实现的XMPP 服务器，客户端可以通过IQ 的方式与其进行通信（其实就是XML），客户端和服务器之间的通信是依靠底层Smack 库提供的各种功能来完成的。其实利用插件方式来扩展Openfire 服务器端主要有两种扩展方式，一种是对服务器控制台页面进行扩展（不是本文的主要内容），其实就是遵循Openfire 页面的布局方式，进行相应的页面扩展和功能扩展；另一种是对通信功能进行扩展。本文主要针对后者进行具体的描述<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />本篇文章的结构如下：<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />1、创建plugin.xml（这是整个插件最关键的文档）<br style="margin: 0px; padding: 0px;" />2、创建服务器插件实例（实现Plugin 接口的一个类还有一批IQHandler）<br style="margin: 0px; padding: 0px;" />3、打包插件（Openfire 插件也有自己的打包方式）和部署插件<br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);"><span style="margin: 0px; padding: 0px; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);"><span style="margin: 0px; padding: 0px; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);"><br style="margin: 0px; padding: 0px;" /></span></span></span></span></span>好滴，实刀实枪的来动手做吧<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />1、创建plugin.xml<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />初次开发Openfire 和Spark 插件的时候，很容易把二者搞混，千万记得，这里是Openfire 的plugin.xml 不是第二篇文章说的那个啦！<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">&lt;plugin&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    &lt;!-- Main plugin class  这里是最重要滴--&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    &lt;class&gt;com.im.server.plugin.GroupTreePlugin&lt;/class&gt;</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    &lt;!-- Plugin meta-data --&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    &lt;name&gt;GroupTreePlugin&lt;/name&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    &lt;description&gt;This is the group plugin.&lt;/description&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    &lt;author&gt;Phoenix&lt;/author&gt;</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    &lt;version&gt;1.0&lt;/version&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    &lt;date&gt;14/03/2008&lt;/date&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    &lt;url&gt;http://localhost:9001/openfire/plugins.jsp&lt;/url&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    &lt;minServerVersion&gt;3.4.1&lt;/minServerVersion&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    &lt;licenseType&gt;gpl&lt;/licenseType&gt;</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    &lt;!-- Admin console entries --&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    &lt;adminconsole&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        &lt;!-- More on this below --&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    &lt;/adminconsole&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">&lt;/plugin&gt;</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />最重要的那一行我已经标记出来啦，就是你这个插件的初始化和垃圾清理类，例子中是在com.im.server.plugin 包中的GroupTreePlugin 类，下文会对这个类进行详细描述。其余的都是描述信息，只要你提供了正确的描述信息，一般都不会出错。建议初次开发者，在写完plugin.xml 文件后，写一个简单的Plugin 实例，并打印出一些信息，如果重新启动Openfire 信息成功显示，恭喜你，你已经迈出一大步了！<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />2、实现Plugin 类和IQHandler<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />Plugin 类主要起到的作用是初始化和释放资源，在初始化的过程中，最重要的的注册一批IQHandler，IQHander 的作用有点类似于Spark 中的IQProvider，其实就是解析XML 文件之后，生成一些有用的实例，以供处理。下面分别给出一个Plugin 类的实例和IQProvider 的实例<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />GroupTreePlugin 类<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">/**</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);"> * 服务器端插件类</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);"> *<span class="Apple-converted-space"> </span></span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);"> * @author Phoenix</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);"> *<span class="Apple-converted-space"> </span></span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);"> * Mar 14, 2008 11:03:11 AM</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);"> *<span class="Apple-converted-space"> </span></span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);"> * version 0.1</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);"> */</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">public class GroupTreePlugin implements Plugin</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">{</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    private XMPPServer server;</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    /*</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">     * (non-Javadoc)</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">     *<span class="Apple-converted-space"> </span></span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">     * @see org.jivesoftware.openfire.container.Plugin#destroyPlugin()</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">     */</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    public void destroyPlugin()</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    {</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    }</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    /*</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">     * (non-Javadoc)</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">     *<span class="Apple-converted-space"> </span></span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">     * @see org.jivesoftware.openfire.container.Plugin#initializePlugin(org.jivesoftware.openfire.container.PluginManager,</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">     *      java.io.File)</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">     */</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    public void initializePlugin(PluginManager manager, File pluginDirectory)</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    {</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        PluginLog.trace("注册群组树IQ处理器");</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        server = XMPPServer.getInstance();</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">       <span class="Apple-converted-space"> </span></span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        server.getIQRouter().addHandler(new GroupTreeIQHander()); //1</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        server.getIQRouter().addHandler(new UserInfoIQHandler());</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        server.getIQRouter().addHandler(new DelUserIQHandler());</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        server.getIQRouter().addHandler(new CreateUserIQHandler());</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        server.getIQRouter().addHandler(new AddGroupUserIQHandler());</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        server.getIQRouter().addHandler(new SetRoleIQHandler());</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    }</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">}</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />上例所示，在初始化中先找到IQRouter，然后通过IQRouter 注册一批IQHandler，这些IQHander 会自动监听相应命名空间的IQ，然后进行处理；由于这个Plugin 不需要做资源释放的工作，所以在destroyPlugin() 方法中没有任何内容。具体的IQHander 类如下<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />GroupTreeIQHander<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">/**</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);"> * 处理客户端发来的IQ，并回送结果IQ</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);"> *<span class="Apple-converted-space"> </span></span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);"> * @author Phoenix</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);"> *<span class="Apple-converted-space"> </span></span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);"> * Mar 14, 2008 4:55:33 PM</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);"> *<span class="Apple-converted-space"> </span></span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);"> * version 0.1</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);"> */</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">public class GroupTreeIQHander extends IQHandler</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">{</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    private static final String MODULE_NAME = "group tree handler";</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    private static final String NAME_SPACE = "com:im:group";</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    private IQHandlerInfo info;</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    public GroupTreeIQHander()</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    {</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        super(MODULE_NAME);</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        info = new IQHandlerInfo("gruops", NAME_SPACE);</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    }</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    /*</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">     * (non-Javadoc)</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">     *<span class="Apple-converted-space"> </span></span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">     * @see org.jivesoftware.openfire.handler.IQHandler#getInfo()</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">     */</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    @Override</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    public IQHandlerInfo getInfo()</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    {</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        return info;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    }</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    /*</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">     * (non-Javadoc)</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">     *<span class="Apple-converted-space"> </span></span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">     * @see org.jivesoftware.openfire.handler.IQHandler#handleIQ(org.xmpp.packet.IQ)</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">     */</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    @Override</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    public IQ handleIQ(IQ packet) throws UnauthorizedException</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    {</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        IQ reply = IQ.createResultIQ(packet);</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        Element groups = packet.getChildElement();//1</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">       <span class="Apple-converted-space"> </span></span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        if (!IQ.Type.get.equals(packet.getType()))</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        {</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">            System.out.println("非法的请求类型");</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">            reply.setChildElement(groups.createCopy());</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">            reply.setError(PacketError.Condition.bad_request);</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">            return reply;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        }</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">       <span class="Apple-converted-space"> </span></span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        String userName = StringUtils.substringBefore(packet.getFrom().toString(),"@");</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        GroupManager.getInstance().initElement(groups,userName);</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">       <span class="Apple-converted-space"> </span></span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        reply.setChildElement(groups.createCopy());//2</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        System.out.println("返回的最终XML" + reply.toXML());</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        return reply;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    }</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">}</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />可以看到主要有两个方法，一个是getInfo() 这个方法的目的是提供要解析的命名空间，在本例中，这个IQHandler 对每个命名空间为"com:im:group" 的实例进行处理；还有一个最重要的方法：handleIQ() 该方法对包含指定命名空间的XML 进行解析，然后返回一个解析好的IQ。其实我认为，这个IQHandler 和IQ 的关系就是Controller 和Model 的关系（如果你了解MVC 的话，那么你一定知道我再说什么），只不过这里并没有指定什么View，你完全可以把IQ 当成Model 类进行理解。在这里，我用了GroupManager 进行了XML 的处理，因为我返回的IQ 内容中要从数据库读取所有群组信息，所以转交给GroupManager 进行处理，你完全可以在这个方法中进行具体的XML 处理，在这里，解析和创建新的XML 主要用到的是JDOM（如果你对Java 解析XML 有所了解，那真的太好了！）。程序//1 处主要是获取创建返回的IQ，并获取原来IQ 的子元素（用于创建我们返回的IQ）；程序//2 处很关键，如果你不调用createCopy 方法，程序会出错（程序会死锁还是什么，忘记咧，不好以西）。<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />这就是程序的主体部分，我在这里有一个建议，能不用Openfire 原始的程序函数，就不要用它们。我的提取数据库方式都是自己写的Bean，这样有利于你自己对程序的掌控，其实更有利于快速开发（这世道不是啥都讲究敏捷么，哇哈哈）<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />3、打包插件<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />打包依然遵循二次打包的原则（如果你不了解啥叫要二次打包，请看上一篇）<br style="margin: 0px; padding: 0px;" />这是我的ant 文件，由于Eclipse 帮我做了build 等很多工作，实际我的ant 工作就是在打包，并放入插件目录下的plugin 文件夹下<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">&lt;project name="IM" default="release" basedir="."&gt;</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    &lt;property name="openfire.path"</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        value="E:/workspace/europa/openfire_src/target/openfire" /&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    &lt;property name="classes.dir" value="classes" /&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    &lt;property name="lib.dir" value="lib" /&gt;</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    &lt;target name="jar"&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        &lt;jar jarfile="${lib.dir}/grouptreeplugin.jar" basedir="${classes.dir}" &gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">            &lt;fileset dir="."&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">                &lt;include name="*.jar"/&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">            &lt;/fileset&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        &lt;/jar&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        &lt;jar jarfile="${openfire.path}/plugins/groupTreePlugin.jar"&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">            &lt;fileset dir="."&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">                &lt;include name="lib/*.jar" /&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">                &lt;include name="plugin.xml" /&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">                &lt;include name="logo_small.gif" /&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">                &lt;include name="logo_large.gif" /&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">                &lt;include name="readme.html" /&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">                &lt;include name="changelog.html" /&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">                &lt;include name="build.xml" /&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">            &lt;/fileset&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">        &lt;/jar&gt;</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    &lt;/target&gt;</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    &lt;target name="release" depends="jar"&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">    &lt;/target&gt;</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(192, 192, 192);">&lt;/project&gt;</span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />好了，至此XMPP+Spark+Openfire 的插件开发三部曲彻底结束了，希望你们对这个开发流程有了系统的了解。<br style="margin: 0px; padding: 0px;" /><br /></div>
				</span>
		</span>
<img src ="http://www.blogjava.net/duanzhimin528/aggbug/325911.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/duanzhimin528/" target="_blank">DZM.528</a> 2010-07-12 23:05 <a href="http://www.blogjava.net/duanzhimin528/archive/2010/07/12/325911.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开发你自己的XMPP IM 续 - Spark 插件开发</title><link>http://www.blogjava.net/duanzhimin528/archive/2010/07/12/325910.html</link><dc:creator>DZM.528</dc:creator><author>DZM.528</author><pubDate>Mon, 12 Jul 2010 14:56:00 GMT</pubDate><guid>http://www.blogjava.net/duanzhimin528/archive/2010/07/12/325910.html</guid><wfw:comment>http://www.blogjava.net/duanzhimin528/comments/325910.html</wfw:comment><comments>http://www.blogjava.net/duanzhimin528/archive/2010/07/12/325910.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/duanzhimin528/comments/commentRss/325910.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/duanzhimin528/services/trackbacks/325910.html</trackback:ping><description><![CDATA[
		<span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Simsun; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;">
				<span class="Apple-style-span" style="color: rgb(101, 101, 101); font-family: Verdana,Arial,Helvetica,sans-serif; font-size: 14px; line-height: 28px; text-align: left;">     此篇文章为《开发你自己的XMPP IM 续 - Spark 插件开发》的转发，原文章地址: </span>
		</span>
		<span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Simsun; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;">
				<span class="Apple-style-span" style="color: rgb(51, 51, 51); font-family: Georgia,serif,宋体; font-size: 12px; line-height: 21px; text-align: center;">
						<a href="http://phoenixtoday.blogbus.com/logs/17878527.html" style="margin: 0px; padding: 0px; text-decoration: none; color: rgb(0, 160, 233);">http://phoenixtoday.blogbus.com/logs/17878527.html</a>
				</span>
		</span>
		<br />
		<span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Simsun; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;">
				<span class="Apple-style-span" style="color: rgb(51, 51, 51); font-family: Georgia,serif,宋体; font-size: 12px; line-height: 19px; text-align: left;">
						<div class="postBody" style="margin: 10px 0px; padding: 0px 0px 0px 20px; word-wrap: break-word;">继续3月18日介绍基于XMPP IM开发的那篇Blog，今天主要总结一下如何基于Spark 的插件架构来新增客户端的功能，这里列举出一个获取服务器端群组信息的实际例子，实现后的效果如下图所示：<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><div id="z4te" style="margin: 0px; padding: 1em 0pt; text-align: left;"><img id="vtx:" src="http://docs.google.com/File?id=dcvntvfs_158gr47zmfr_b" alt="" style="margin: 0px; padding: 0px; width: 307px; height: 514px;" /></div><br style="margin: 0px; padding: 0px;" />Spark 是一个基于XMPP 协议，用Java 实现的IM 客户端。它提供了一些API，可以采用插件机制进行扩展，上图中，“部门”部分就是使用插件机制扩展出来的新功能。要想实现你的扩展，首先要了解 Spark API的架构，其中最关键的是要了解它的工厂类，这些工厂类可以获得Spark 提供的诸如XMPPConnection、ChatContainer 等实例，从而你可以实现获取服务器的信息，与另外的Client 通信等功能。最核心的类是SparkManager，这个类是一系列工厂类的工厂类（呵呵，还真拗口）。它的getChatManager()<font id="yu0-" style="margin: 0px; padding: 0px;" color="#000000">、getSessionManager ()、getMainWindow() 、getConnection() 等方法分别可以获得聊天管理器、会话管理器、主窗口、与服务器的连接等等非常有用的实例。基本上可以说SparkManager 是你与Spark 打交道的衔接口。其实，每一个Manager 都使用了单例模式，你也可以不通过SparkManager 来获取它们，但笔者建议你从单一的入口着手，这样有利于代码的开发和维护。<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />接下来描述一下插件的开发流程：<br style="margin: 0px; padding: 0px;" />1、创建插件配置文件 plugin.xml<br style="margin: 0px; padding: 0px;" />2、实现你自己的Plugin 类的实现（如果你需要实现自己规定格式的XML 发送、接收和处理，那么你需要在这里注册你的IQProvider，关于IQProvider 你可以查询Smack API，简单的来讲是处理你自定义的IQ 处理器。）<br style="margin: 0px; padding: 0px;" />3、打包你的插件（Spark 有自己的打包机制，我研究了半天才发现其中的玄机，后面介绍）<br style="margin: 0px; padding: 0px;" />4、部署你的插件（其实3、4两步可以糅合在一起，当然要利用Ant 啦）<br style="margin: 0px; padding: 0px;" /></font><br style="margin: 0px; padding: 0px;" />好滴，下面结合一个实际的例子讲述上面的四个步骤<br style="margin: 0px; padding: 0px;" />1、plugin.xml<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">&lt;plugin&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">    &lt;name&gt;Enterprise IM Client&lt;/name&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">    &lt;version&gt;1.0&lt;/version&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">    &lt;author&gt;Phoenix&lt;/author&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">    &lt;homePage&gt;http://phoenixtoday.blogbus.com&lt;/homePage&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">    &lt;email&gt;phoenixtoday@gmail.com&lt;/email&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">    &lt;description&gt;Enterprise Client Plug-in&lt;/description&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">   <span class="Apple-converted-space"> </span><font id="ueej" style="margin: 0px; padding: 0px;" color="#ff0000">&lt;!-- 关键是这里，这里要定义你的Plugin 类 --&gt;</font></span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">    &lt;class&gt;com.im.plugin.IMPlugin&lt;/class&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">    &lt;!-- 这里定义你使用的Spark 最低版本 --&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">    &lt;minSparkVersion&gt;2.5.0&lt;/minSparkVersion&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">    &lt;os&gt;Windows&lt;/os&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">&lt;/plugin&gt;<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(255, 255, 255);">这是一个 plugin.xml 文件的内容，插件体系会自动调用你在此文件中定义的Plugin 类，从而完成你自己扩展的功能。最关键的部分我用红色标识出来了，要声明你的插件扩展类，采用完整的命名空间方式（包括包名），其余的部分结合我的注释， 大家应该都能理解，就不做详细的描述了。要注意的是plugin.xml 文件要放在项目的根目录下，这是严格规定好的。<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />2、Plugin 类的实现<br style="margin: 0px; padding: 0px;" />你的类首先要实现Spark 提供的Plugin 接口，然后实现它的一些方法。其中最主要的是实现initialize() 发放，在这里注册你的的IQProvider<span class="Apple-converted-space"> </span><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">ProviderManager providerManager = ProviderManager.getInstance();</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">providerManager.addIQProvider("groups", "com:im:group", //1</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">                new GroupTreeIQProvider());</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">System.out.println("注册GroupTree IQ 提供者");</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">requestGroupTree();<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(255, 255, 255);">上述的代码，就在该类 就是我实现的IMPlugin.initialize() 方法中的一小段，大概的含义是，先获取ProviderManager（这个貌似不能从SparkManager 直接获取），然后注册一个GroupTreeIQProvider（自己创建的）这是一个IQProvider 的具体实现，它用于解析像下面这样的一个XML 文件：<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">&lt;iq type='result' to='domain@server.com' from='phoenixtoday@gmail.com' id='request_1'&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">    &lt;groups xmlns='com:im:group'&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">        &lt;group&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">             &lt;groupId&gt;1&lt;/groupId&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">             &lt;name&gt;西安交通大学&lt;/name&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">             &lt;upGroup&gt;ROOT&lt;/upGroup&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">             &lt;isLeaf&gt;0&lt;/isLeaf&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">             &lt;description&gt;xjtu&lt;/description&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">             &lt;user&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">                 &lt;userGroupId&gt;1&lt;/userGroupId&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">                 &lt;userName&gt;phoenix_test&lt;/userName&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">                 &lt;role&gt;normal&lt;/role&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">             &lt;/user&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">        &lt;/group&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">        &lt;group&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">             &lt;groupId&gt;2&lt;/groupId&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">             &lt;name&gt;电信学院&lt;/name&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">             &lt;upGroup&gt;1&lt;/upGroup&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">             &lt;isLeaf&gt;1&lt;/isLeaf&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">             &lt;description&gt;xjtu info&lt;/description&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">        &lt;/group&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">    &lt;/groups&gt;</span><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(204, 204, 204);">&lt;/iq&gt;<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(255, 255, 255);">可以看到，在注册 IQProvider 的时候（代码中标注的1部分），需要你提供名称和命名空间，我的XML 文件中的iq 下的第一个子节点是&lt;groups&gt; 所以我的名称就写“groups”，命名空间对应于groups 节点的xmlns(XML Name Space)所以是“com:im:group”，其实IQProvider 中最关键的方法是parseIQ(XmlPullParser parser) 该方法就是解析XML，完成你的功能，并返回一个相应的IQ 实例（这里可以把IQ 看做一个回馈的Model 类）。说到底实现基于XMPP 协议的IM 就是解析XML 文件，而这正是客户端的IQProvider 和服务器端的IQHandler（下一篇文章会涉及到）所做的事情。<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />3、打包你的插件<br style="margin: 0px; padding: 0px;" />现在该有的功能都实现了，那么就是打包了。这最好利用Ant 来完成，因为每次你都要打包，要部署，如果纯手动的话，那也太不敏捷了，大大影响开发效率。<br style="margin: 0px; padding: 0px;" /></span><br style="margin: 0px; padding: 0px;" />&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br style="margin: 0px; padding: 0px;" />&lt;project name="IM" default="release" basedir="."&gt;<br style="margin: 0px; padding: 0px;" />    &lt;property name="src.dir" value="src" /&gt;<br style="margin: 0px; padding: 0px;" />    &lt;property name="dest.dir" value="bin" /&gt;<br style="margin: 0px; padding: 0px;" />    &lt;property name="lib.dir" value="lib" /&gt;<br style="margin: 0px; padding: 0px;" />    &lt;property name="im.path"<br style="margin: 0px; padding: 0px;" />        value="E:/workspace/europa/spark_new/doc/spark/target/build" /&gt;<br style="margin: 0px; padding: 0px;" />    &lt;target name="clean"&gt;<br style="margin: 0px; padding: 0px;" />        &lt;!-- <span class="Apple-converted-space"> </span><br style="margin: 0px; padding: 0px;" />            &lt;delete dir="${dest.dir}" /&gt;<br style="margin: 0px; padding: 0px;" />           <span class="Apple-converted-space"> </span><br style="margin: 0px; padding: 0px;" />            &lt;delete dir="${lib.dir}" /&gt;<br style="margin: 0px; padding: 0px;" />        --&gt;<br style="margin: 0px; padding: 0px;" />    &lt;/target&gt;<br style="margin: 0px; padding: 0px;" />    &lt;target name="init" depends="clean"&gt;<br style="margin: 0px; padding: 0px;" />        &lt;!-- <span class="Apple-converted-space"> </span><br style="margin: 0px; padding: 0px;" />            &lt;mkdir dir="${dest.dir}" /&gt;<br style="margin: 0px; padding: 0px;" />           <span class="Apple-converted-space"> </span><br style="margin: 0px; padding: 0px;" />            &lt;mkdir dir="${lib.dir}" /&gt;<br style="margin: 0px; padding: 0px;" />        --&gt;<br style="margin: 0px; padding: 0px;" />    &lt;/target&gt;<br style="margin: 0px; padding: 0px;" />    &lt;target name="build" depends="init"&gt;<br style="margin: 0px; padding: 0px;" />        &lt;!--<br style="margin: 0px; padding: 0px;" />            &lt;javac srcdir="${src.dir}" destdir="${dest.dir}" /&gt;<br style="margin: 0px; padding: 0px;" />        --&gt;<br style="margin: 0px; padding: 0px;" />    &lt;/target&gt;<br style="margin: 0px; padding: 0px;" />   <span class="Apple-converted-space"> </span><font id="wc9i" style="margin: 0px; padding: 0px;" color="#ff0000">&lt;!-- 最重要的是这里，打两次包 --&gt;</font><br style="margin: 0px; padding: 0px;" />    &lt;target name="jar" depends="build"&gt;<br style="margin: 0px; padding: 0px;" />        &lt;jar jarfile="${lib.dir}/eim.jar" basedir="${dest.dir}" /&gt;<br style="margin: 0px; padding: 0px;" />        &lt;jar jarfile="${im.path}/plugins/eim.jar"&gt;<br style="margin: 0px; padding: 0px;" />            &lt;fileset dir="."&gt;<br style="margin: 0px; padding: 0px;" />                &lt;include name="lib/*.jar" /&gt;<br style="margin: 0px; padding: 0px;" />            &lt;/fileset&gt;<br style="margin: 0px; padding: 0px;" />            &lt;fileset dir="."&gt;<br style="margin: 0px; padding: 0px;" />                &lt;include name="plugin.xml" /&gt;<br style="margin: 0px; padding: 0px;" />            &lt;/fileset&gt;<br style="margin: 0px; padding: 0px;" />        &lt;/jar&gt;<br style="margin: 0px; padding: 0px;" />    &lt;/target&gt;<br style="margin: 0px; padding: 0px;" />    &lt;target name="release" depends="jar"&gt;<br style="margin: 0px; padding: 0px;" />        &lt;!-- <span class="Apple-converted-space"> </span><br style="margin: 0px; padding: 0px;" />            &lt;exec executable="cmd.exe"<br style="margin: 0px; padding: 0px;" />            failonerror="true"&gt;<br style="margin: 0px; padding: 0px;" />            &lt;arg line="/c e:"/&gt;<br style="margin: 0px; padding: 0px;" />            &lt;arg line="/c cd workspace\europa\spark_new\doc\spark\target\build\bin"/&gt;<br style="margin: 0px; padding: 0px;" />            &lt;arg line="/c startup.bat"/&gt;<br style="margin: 0px; padding: 0px;" />            &lt;/exec&gt;<br style="margin: 0px; padding: 0px;" />        --&gt;<br style="margin: 0px; padding: 0px;" />    &lt;/target&gt;<br style="margin: 0px; padding: 0px;" />&lt;/project&gt;<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; background-color: rgb(255, 255, 255);">这是我的这个项目的 build.xml 文件中的内容。因为Eclipse 其实帮我自动完成了编译的任务，所以我也就省去了这写编译的步骤，最重要的是大家要看到“jar” 部分，Spark 打包的神秘之处也就在此，打两次包首先把你的项目打包到本项目lib 文件夹下，比如说你的项目目录是MyPlugin 那么，你就将你的类打包到MyPlugin/lib 目录下，然后再次的打包，将所有的lib 文件夹下的内容打包起来，记得这次要包含plugin.xml。也就是说，最后Spark 插件体系会读取你的项目下的lib 文件夹下的内容。这里我也有个疑问，我本来想每次打包后自动执行bat 文件，启动插件，看看效果，为啥死都调用不了呢，那段代码在最后面，注释掉了，谁能帮我解决，我请他吃饭滴！<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />4、最后就是发布了<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />其实我的发布很简单，就是将这个打包好的jar 文件拷到Spark 本身的plugins 目录下，每次启动Spark 的时候，它会自动调用自定义的插件的。我这里用Ant 第二次jar 的时候，就自动拷贝过去了，这里用的是绝对路径，所以你不能直接拷贝就用滴呦（是不是很丑陋呀，这段Ant 代码）。<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />基本上客户端的实现原理就是这样的，只是有些地方需要特别注意，还有就是应该利用像Ant 这样的工具大大简化开发步骤，加快开发效率。还有就是，我建议你在开发自己的插件的时候，多利用MVC 模式，尤其是在IQProvider 解析后，生成的部分可以实例化Model，然后你可以编写自己的Manager 进行这些Model 的处理。多写Log，当然Log4j 貌似不太起作用，那就System.out.println() 吧.</span></span></span></span></span></span></div>
				</span>
		</span>
<img src ="http://www.blogjava.net/duanzhimin528/aggbug/325910.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/duanzhimin528/" target="_blank">DZM.528</a> 2010-07-12 22:56 <a href="http://www.blogjava.net/duanzhimin528/archive/2010/07/12/325910.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开发你自己的XMPP IM</title><link>http://www.blogjava.net/duanzhimin528/archive/2010/07/12/325909.html</link><dc:creator>DZM.528</dc:creator><author>DZM.528</author><pubDate>Mon, 12 Jul 2010 14:54:00 GMT</pubDate><guid>http://www.blogjava.net/duanzhimin528/archive/2010/07/12/325909.html</guid><wfw:comment>http://www.blogjava.net/duanzhimin528/comments/325909.html</wfw:comment><comments>http://www.blogjava.net/duanzhimin528/archive/2010/07/12/325909.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/duanzhimin528/comments/commentRss/325909.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/duanzhimin528/services/trackbacks/325909.html</trackback:ping><description><![CDATA[
		<span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Simsun; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;">
				<span class="Apple-style-span" style="color: rgb(51, 51, 51); font-family: Georgia,serif,宋体; font-size: 12px; line-height: 19px; text-align: left;">
						<div class="postBody" style="margin: 10px 0px; padding: 0px 0px 0px 20px; word-wrap: break-word;">
								<p style="margin: 15px 0px; padding: 0px;">
										<span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Simsun; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;">
												<span class="Apple-style-span" style="color: rgb(101, 101, 101); font-family: Verdana,Arial,Helvetica,sans-serif; font-size: 14px; line-height: 28px; text-align: left;">
												</span>
										</span>
										<span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Simsun; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;">
												<span class="Apple-style-span" style="color: rgb(51, 51, 51); font-family: Georgia,serif,宋体; font-size: 12px; line-height: 19px; text-align: left;">
														<p style="margin: 15px 0px; padding: 0px;">
																<span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Simsun; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;">
																		<span class="Apple-style-span" style="color: rgb(101, 101, 101); font-family: Verdana,Arial,Helvetica,sans-serif; font-size: 14px; line-height: 28px; text-align: left;">
																		</span>
																</span>
																<a href="http://phoenixtoday.blogbus.com/logs/17246727.html" style="margin: 0px; padding: 0px; text-decoration: none; color: rgb(0, 160, 233);">
																</a>此篇文章为《开发你自己的XMPP IM》的转发，原文章地址:<a href="http://phoenixtoday.blogbus.com/logs/17246727.html">http://phoenixtoday.blogbus.com/logs/17246727.html</a><br /></p>
														<p style="margin: 15px 0px; padding: 0px;">最近没在Blog 上露脸，为撒类？应师兄的请求，帮他研究一个XMPP IM 软件的开发。反正最近也没什么大事，每天都想写写代码练练手，就帮忙呗。研究了一通觉得还挺有趣，自己这几天查国内外的资料，发现国内关于这方面间的软件 资料太少了，就想在这里写几篇关于此类IM 软件开发的文章。不过别看东西小，涉及的模块可不少。</p>
												</span>
										</span>
								</p>
								<p style="margin: 15px 0px; padding: 0px;">所以我基本上分为三篇文章来介绍此类软件的开发：</p>
								<p style="margin: 15px 0px; padding: 0px;">第一篇是关于XMPP 协议是啥，IM 是啥以及一个比较有名的开源实现，该开源实现包括三个部分（Spark、Smack和Openfire）；</p>
								<p style="margin: 15px 0px; padding: 0px;">第二篇讲如何开发基于Spark 的客户端IM 插件部分；</p>
								<p style="margin: 15px 0px; padding: 0px;">第三篇讲如何开发基于Openfire 服务器端的插件部分。</p>
								<p style="margin: 15px 0px; padding: 0px;">好了，进入正题吧。</p>
								<p style="margin: 15px 0px; padding: 0px;"> </p>
								<p style="margin: 15px 0px; padding: 0px;">
										<strong style="margin: 0px; padding: 0px;">什么是XMPP？</strong>
										<br style="margin: 0px; padding: 0px;" />Extensible Messaging and Presence Protocol，简单的来讲，它就是一个发送接收处理消息的协议，但是这个协议发送的消息，既不是二进制的东东也不是字符串，而是XML。正是因为使用 了XML作为消息传递的中介，Extensible 才谈的上，不是么？嘿嘿。再详尽的东西，我也就不多介绍了，大家可以去百度百科里查看下，链接在<a href="http://baike.baidu.com/view/189676.htm" target="_blank" style="margin: 0px; padding: 0px; text-decoration: none; color: rgb(0, 160, 233);">这里</a></p>
								<p style="margin: 15px 0px; padding: 0px;">
										<span style="margin: 0px; padding: 0px; font-weight: bold;">什么是IM ？</span>
								</p>
								<p style="margin: 15px 0px; padding: 0px;">Instant Messenger，及时通信软件，就是大家使用的QQ、MSN Messenger和Gtalk等等。其中Gtalk 就是基于XMPP 协议的一个实现，其他的则不是。当前IM 几乎作为每个上网者必然使用的工具，在国外的大型企业中有一些企业级的IM应用，但是其商业价值还没完全发挥出来。设想既然XMPP 协议是一个公开的协议，那么每个企业都可以利用它来开发适合本身企业工作，提高自身生产效率的IM；甚至，你还可以在网络游戏中集成这种通信软件，不但让你 可以边游戏边聊天，也可以开发出适合游戏本身的IM 应用，比如说一些游戏关键场景提醒功能，团队语音交流等等都可以基于IM来实现。说了这么多，就是一个意思，其商业价值远远比你想的高！</p>
								<p style="margin: 15px 0px; padding: 0px;">
										<span style="margin: 0px; padding: 0px; font-weight: bold;">Spark Smack 和 Openfire</span>
								</p>
								<p style="margin: 15px 0px; padding: 0px;">开源界总是有许多有趣的东东，这三个合起来就是一个完整的XMPP IM 实现。包括服务器端——Openfire，客户端——Spark，XMPP 传输协议的实现——Smack（记住，XMPP是一个协议，协议是需要实现的，Smack起到的就是这样的一个作用）。三者都是基于Java 语言的实现，因此对于熟悉Java 的开发者来说不是很难<br style="margin: 0px; padding: 0px;" /></p>
								<br style="margin: 0px; padding: 0px;" />Spark 提供了客户端一个基本的实现，并提出了一个很好的插件架构，这对于开发者来说不能不说是一个福音。我强烈建议基于插件方式来实现你新增加的功能，而不是去 改它的源代码，这样有利于你项目架构，把原始项目的影响降到最低，文章以后的部分也是基于这种插件体系进行开发的<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />Openfire 是基于XMPP 协议的IM 的服务器端的一个实现，虽然当两个用户连接后，可以通过点对点的方式来发送消息，但是用户还是需要连接到服务器来获取一些连接信息和通信信息的，所以服务 器端是必须要实现的。Openfire 也提供了一些基本功能，但真的很基本的！庆幸的是，它也提供插件的扩展，像Spark 一样，我同样强烈建议使用插件扩展的方式来增加新的功能，而不是修改人家的源代码。<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />Smack 是一个XMPP 协议的Java 实现，提供一套可扩展的API，不过有些时候，你还是不得不使用自己定制发送的XML 文件内容的方式来实现自己的功能<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />下图展示了三者之间的关系：<br style="margin: 0px; padding: 0px;" /><div id="pdds" style="margin: 0px; padding: 1em 0pt; text-align: left;"><img src="http://docs.google.com/File?id=dcvntvfs_1552whxf8fq" alt="点击查看原始尺寸" style="margin: 0px; padding: 0px; width: 550px; height: 273.103px; cursor: pointer;" /><br style="margin: 0px; padding: 0px;" />从图上可以了解到，client 端和server端都可以通过插件的方式来进行扩展，smack是二者传递数据的媒介。<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" />嗯，今天就写到这里吧，画图花了好久的时间呀。下一篇介绍一下如何开发基于Spark 的插件程序，并总结一些我自己的一些心得，举一个实际的例子，需要涉及到的一些XML 文件的解析与生成，还有Spark、Smack 自己提供的API ，还有ant 的一些基本知识.</div></div>
				</span>
		</span>
<img src ="http://www.blogjava.net/duanzhimin528/aggbug/325909.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/duanzhimin528/" target="_blank">DZM.528</a> 2010-07-12 22:54 <a href="http://www.blogjava.net/duanzhimin528/archive/2010/07/12/325909.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>UDP打洞，穿透NAT</title><link>http://www.blogjava.net/duanzhimin528/archive/2010/07/12/325882.html</link><dc:creator>DZM.528</dc:creator><author>DZM.528</author><pubDate>Mon, 12 Jul 2010 08:53:00 GMT</pubDate><guid>http://www.blogjava.net/duanzhimin528/archive/2010/07/12/325882.html</guid><wfw:comment>http://www.blogjava.net/duanzhimin528/comments/325882.html</wfw:comment><comments>http://www.blogjava.net/duanzhimin528/archive/2010/07/12/325882.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/duanzhimin528/comments/commentRss/325882.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/duanzhimin528/services/trackbacks/325882.html</trackback:ping><description><![CDATA[
		<span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Verdana; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;">
				<span class="Apple-style-span" style="border-collapse: collapse; color: rgb(68, 68, 68); font-family: Verdana,Helvetica,Arial,sans-serif; font-size: 12px; line-height: 19px; text-align: left;">
						<div class="t_msgfontfix" style="word-wrap: break-word; line-height: normal; min-height: 100px;">
								<span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Verdana; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 27px; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;">
										<span class="Apple-style-span" style="color: rgb(64, 64, 64); font-family: Verdana,Simsun; font-size: 12px; line-height: 20px;">
										</span>
								</span>
								<br />UDP"打洞"原理<br /><br />1.       NAT分类<br /><br />根据Stun协议(RFC3489),NAT大致分为下面四类<br /><br />1)      Full Cone<br /><br />这种NAT内部的机器A连接过外网机器C后,NAT会打开一个端口.然后外网的任何发到这个打开的端口的UDP数据报都可以到达A.不管是不是C发过来的.<br /><br />例如 A:192.168.8.100 NAT:202.100.100.100 C:292.88.88.88<br /><br />A(192.168.8.100:5000) -&gt; NAT(202.100.100.100 : 8000) -&gt; C(292.88.88.88:2000)<br /><br />任何发送到 NAT(202.100.100.100:8000)的数据都可以到达A(192.168.8.100:5000)<br /><br />2)      Restricted Cone<br /><br />这种NAT内部的机器A连接过外网的机器C后,NAT打开一个端口.然后C可以用任何端口和A通信.其他的外网机器不行.<br /><br />例如 A:192.168.8.100 NAT:202.100.100.100 C:292.88.88.88<br /><br />A(192.168.8.100:5000) -&gt; NAT(202.100.100.100 : 8000) -&gt; C(292.88.88.88:2000)<br /><br />任何从C发送到 NAT(202.100.100.100:8000)的数据都可以到达A(192.168.8.100:5000)<br /><br /> <br /><br />3)      Port Restricted Cone<br /><br />这种NAT内部的机器A连接过外网的机器C后,NAT打开一个端口.然后C可以用原来的端口和A通信.其他的外网机器不行.<br /><br />例如 A:192.168.8.100 NAT:202.100.100.100 C:292.88.88.88<br /><br />A(192.168.8.100:5000) -&gt; NAT(202.100.100.100 : 8000) -&gt; C(292.88.88.88:2000)<br /><br />C(202.88.88.88:2000)发送到 NAT(202.100.100.100:8000)的数据都可以到达A(192.168.8.100:5000)<br /><br /> <br /><br />以上三种NAT通称Cone NAT.我们只能用这种NAT进行UDP打洞.<br /><br />4)      Symmetic<br /><br />对于这种NAT.连接不同的外部目标.原来NAT打开的端口会变化.而Cone NAT不会.虽然可以用端口猜测.但是成功的概率很小.因此放弃这种NAT的UDP打洞.<br /><br />2.       UDP hole punching<br /><br />对于Cone NAT.要采用UDP打洞.需要一个公网机器C来充当”介绍人”.内网的A,B先分别和C通信.打开各自的NAT端口.C这个时候知道A,B的公网 IP:Port. 现在A和B想直接连接.比如A给B发.除非B是Full Cone.否则不能通信.反之亦然.但是我们可以这样.<br /><br />A要连接B.A给B发一个UDP包.同时.A让那个介绍人给B发一个命令,让B同时给A发一个UDP包.这样双方的NAT都会记录对方的IP,然后就会允许互相通信.<br /><br />3.       同一个NAT后面的情况<br /><br />如果A,B在同一个NAT后面.如果用上面的技术来进行互连.那么如果NAT支持loopback(就是本地到本地的转换),A,B可以连接,但是比较浪费带宽和NAT.有一种办法是,A,B和介绍人通信的时候,同时把自己的local IP也告诉服务器.A,B通信的时候,同时发local ip和公网IP.谁先到就用哪个IP.但是local ip就有可能不知道发到什么地方去了.比如A,B在不同的NAT后面但是他们各自的local ip段一样.A给B的local IP发的UDP就可能发给自己内部网里面的某某某了.<br /><br />还有一个办法是服务器来判断A,B是否在一个NAT后面.(网络拓朴不同会不会有问题?)<br /><br />-------------------------------------------------------------<span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Verdana; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;"><span class="Apple-style-span" style="border-collapse: collapse; color: rgb(68, 68, 68); font-family: Verdana,Helvetica,Arial,sans-serif; font-size: 12px; line-height: 19px; text-align: left;">-------------------------------------------------------------</span></span><br /><br />From: http://hi.baidu.com/ligh0721/blog/item/0cbbc9384106252b96ddd872.html<br /><br />前天编程做了一下UDP打洞的实验，今天特写了一篇文章总结一下。<br /><br />我们知道网络上两个主机进行通信，如果其中一台主机拥有公网IP那么，那么进行会话是比较简单的，但是如果两台主机是位于不同内网之中的，那么应该如何进行通信呢。一种想法是再找一台公网的服务器，用来转发信息，但是这有一个问题，就是会给服务器带来压力，因此我们就来谈谈一种用于不同内网中的主机互相通信的一个解决方案——NAT打洞。<br /><br />原理还是比较简单的，我们先了解一下什么是“NAT的洞”。当处于内网中的一台主机(ClientA/192.168.1.128)向一个公网的服务器 (Server)发送数据时，这时NAT(NAT1)将会打开一个临时性的端口用于与公网的服务器进行通信，并且会把那个内网主机发送出的IP数据报的头部中源IP地址改为NAT的公网IP(218.7.32.28)，将TCP或UDP数据报中源端口(2347)改为那个临时端口(26756)这样就实现了由 “192.168.1.128:2347”到“218.7.32.28:26756”的源地址源端口的转换。这个数据包到达公网服务器后，服务器就可以根据这个包的头部信息进行回复。当服务器的数据包到达NAT后，NAT在将这个数据发送到内网主机192.168.1.128的2347端口。那么这个 NAT上的26756端口我们就称作“洞”。如果这个NAT不是Full Cone NAT的话(其实大多数的NAT确实不是这种类型的)，那么我们说这个“洞”是有方向性的。一个洞应该会指向一个(也可以是多个)公网主机的IP地址。比如上面说的例子，在NAT1上打的洞是指向Server的IP地址。来自其他公网主机发向这个洞(也就是218.7.32.28:26756)的数据包会被非Full Cone类型的NAT所丢弃。所以如果有另一台处于另一内网的主机(ClientB/192.168.0.5)向218.7.32.28:26756直接发送数据的话，同样也会被NAT1丢弃。<br /><br />那么如何建立ClientA和ClientB的直接会话呢？<br />网络环境描述:<br />内网1NAT:NAT1/218.7.32.28<br />内网1中一台主机:ClientA/192.168.1.128<br />内网2NAT:NAT2/218.7.31.221<br />内网2中一台主机:ClientB/192.168.0.5<br />公网服务器:Server<br /><br />首先让ClientA和ClientB登录到服务器Server(假如两台主机都采用2347端口)，此时NAT1和NAT2会分别为ClientA和 ClientB打开一个指向Server的洞(NAT1上218.7.32.28:26756和NAT2上218.7.31.221:27550)。服务器应改记录这两个客户端的信息(关键是那两个洞的信息)。当ClientA与ClientB要建立会话时，ClientA首先用2347端口向NAT2的洞发送一个数据包，当然这个数据包会被NAT2所丢弃，但是由于这是从NAT1内部向外部发送数据，所以NAT1为ClientA打开了一个指向NAT2 的洞。而且这个新洞与原来NAT1上指向Server的旧洞的是同一个洞(因为是同一个端口26756)，所以这里可以说这个洞具有了两个方向，同时指向 Server和NAT2。这时ClientA应该通知Server，告诉ClientB，现在可以向NAT1的那个洞 (218.7.32.28:26756)发送数据包了。当ClientB向NAT1的那个洞发送数据以后，NAT2也为ClientB打了一个指向 NAT1的洞，这是可以说ClientA与ClientB的会话就建立完成了，他们可以不依赖Server进行通信了。如果以后ClientA和 ClientB还需要建立其他会话 ，那么这个牵线的“媒人”可以不是Server，而可以是ClientA或ClientB了。<br /><br />UDP打洞可以实现不同内网内的主机进行通信，而且实施性比较高，一般用于P2P通信。这也就是为什么常会看见腾讯QQ在开始传输文件时会显示“UDP连接已经建立”了<br /></div>
				</span>
		</span>
<img src ="http://www.blogjava.net/duanzhimin528/aggbug/325882.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/duanzhimin528/" target="_blank">DZM.528</a> 2010-07-12 16:53 <a href="http://www.blogjava.net/duanzhimin528/archive/2010/07/12/325882.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>openfire开发文档</title><link>http://www.blogjava.net/duanzhimin528/archive/2010/07/02/325097.html</link><dc:creator>DZM.528</dc:creator><author>DZM.528</author><pubDate>Fri, 02 Jul 2010 09:58:00 GMT</pubDate><guid>http://www.blogjava.net/duanzhimin528/archive/2010/07/02/325097.html</guid><wfw:comment>http://www.blogjava.net/duanzhimin528/comments/325097.html</wfw:comment><comments>http://www.blogjava.net/duanzhimin528/archive/2010/07/02/325097.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/duanzhimin528/comments/commentRss/325097.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/duanzhimin528/services/trackbacks/325097.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: openfire开发文档版本：Openfire3.5.摘自：IM即时通讯研究&nbsp;&nbsp;&nbsp; 目&nbsp;录一、初始工作&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#1...&nbsp;&nbsp;<a href='http://www.blogjava.net/duanzhimin528/archive/2010/07/02/325097.html'>阅读全文</a><img src ="http://www.blogjava.net/duanzhimin528/aggbug/325097.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/duanzhimin528/" target="_blank">DZM.528</a> 2010-07-02 17:58 <a href="http://www.blogjava.net/duanzhimin528/archive/2010/07/02/325097.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>