開發XMPP IM

    Openfire 是一個用Java 實現的XMPP 服務器,客戶端能夠經過IQ的方式與其進行通訊(其實就是XML),客戶端和服務器之間的通訊是依靠底層Smack 庫提供的各類功能來完成的。其實利用插件方式來擴展Openfire 服務器端主要有兩種擴展方式,一種是對服務器控制檯頁面進行擴展(不是本文的主要內容),即遵循Openfire 頁面的佈局方式,進行相應的頁面擴展和功能擴展另外一種是對通訊功能進行擴展。本文主要針對後者進行具體的描述。html

    本篇文章的結構以下:java

    一、建立plugin.xml(這是整個插件最關鍵的文檔)。
    二、建立服務器插件實例(實現Plugin 接口的一個類還有一批IQHandler)。
    三、打包插件(Openfire 插件也有本身的打包方式)和部署插件。數據庫


一、建立plugin.xml服務器

初次開發Openfire 和Spark 插件的時候,很容易把兩者搞混,千萬記得,這裏是Openfire 的plugin.xml 不是第二篇文章說的那個啦!jsp

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <plugin>
 3     <!-- Main plugin class  這裏是最重要滴-->
 4     <class>com.im.server.plugin.GroupTreePlugin</class>
 5 
 6     <!-- Plugin meta-data -->
 7     <name>GroupTreePlugin</name>
 8     <description>This is the group plugin.</description>
 9     <author>Phoenix</author>
10 
11     <version>1.0</version>
12     <date>14/03/2008</date>
13     <url>http://localhost:9001/openfire/plugins.jsp</url>
14     <minServerVersion>3.4.1</minServerVersion>
15     <licenseType>gpl</licenseType>
16 
17     <!-- Admin console entries -->
18     <adminconsole>
19         <!-- More on this below -->
20     </adminconsole>
21 </plugin>

    最重要的那一行我已經標記出來啦,就是你這個插件的初始化垃圾清理類,例子中是在com.im.server.plugin 包中的GroupTreePlugin 類,下文會對這個類進行詳細描述。其他的都是描述信息,只要你提供了正確的描述信息,通常都不會出錯。建議初次開發者,在寫完plugin.xml 文件後,寫一個簡單的Plugin 實例,並打印出一些信息,若是從新啓動Openfire 信息成功顯示,恭喜你,你已經邁出一大步了!ide

二、實現Plugin 類和IQHandler函數

   Plugin 類主要起到的做用是初始化和釋放資源,在初始化的過程當中,最重要的的註冊一批IQHandler,IQHander 的做用有點相似於Spark 中的IQProvider,其實就是解析XML 文件以後,生成一些有用的實例,以供處理。下面分別給出一個Plugin 類的實例和IQProvider 的實例:佈局

GroupTreePlugin 類ui

 1 /**
 2  * 服務器端插件類
 3  * 
 4  * @author Phoenix
 5  * 
 6  * Mar 14, 2008 11:03:11 AM
 7  * 
 8  * version 0.1
 9  */
10 public class GroupTreePlugin implements Plugin
11 {
12     private XMPPServer server;
13 
14     /*
15      * (non-Javadoc)
16      * 
17      * @see org.jivesoftware.openfire.container.Plugin#destroyPlugin()
18      */
19     public void destroyPlugin()
20     {
21 
22     }
23 
24     /*
25      * (non-Javadoc)
26      * 
27      * @see org.jivesoftware.openfire.container.Plugin#initializePlugin(org.jivesoftware.openfire.container.PluginManager,
28      *      java.io.File)
29      */
30     public void initializePlugin(PluginManager manager, File pluginDirectory)
31     {
32         PluginLog.trace("註冊羣組樹IQ處理器");
33         server = XMPPServer.getInstance();
34         
35         server.getIQRouter().addHandler(new GroupTreeIQHander()); //1
36         server.getIQRouter().addHandler(new UserInfoIQHandler());
37         server.getIQRouter().addHandler(new DelUserIQHandler());
38         server.getIQRouter().addHandler(new CreateUserIQHandler());
39         server.getIQRouter().addHandler(new AddGroupUserIQHandler());
40         server.getIQRouter().addHandler(new SetRoleIQHandler());
41     }
42 }

    上例所示,在初始化中先找到IQRouter,而後經過IQRouter 註冊一批IQHandler,這些IQHander 會自動監聽相應命名空間的IQ,而後進行處理;因爲這個Plugin 不須要作資源釋放的工做,因此在destroyPlugin() 方法中沒有任何內容。具體的IQHander 類以下:this

GroupTreeIQHander 類

 1 /**
 2  * 處理客戶端發來的IQ,並回送結果IQ
 3  * 
 4  * @author Phoenix
 5  * 
 6  * Mar 14, 2008 4:55:33 PM
 7  * 
 8  * version 0.1
 9  */
10 public class GroupTreeIQHander extends IQHandler
11 {
12     private static final String MODULE_NAME = "group tree handler";
13     private static final String NAME_SPACE = "com:im:group";
14     private IQHandlerInfo info;
15 
16     public GroupTreeIQHander()
17     {
18         super(MODULE_NAME);
19         info = new IQHandlerInfo("gruops", NAME_SPACE);
20     }
21 
22     /*
23      * (non-Javadoc)
24      * 
25      * @see org.jivesoftware.openfire.handler.IQHandler#getInfo()
26      */
27     @Override
28     public IQHandlerInfo getInfo()
29     {
30         return info;
31     }
32 
33     /*
34      * (non-Javadoc)
35      * 
36      * @see org.jivesoftware.openfire.handler.IQHandler#handleIQ(org.xmpp.packet.IQ)
37      */
38     @Override
39     public IQ handleIQ(IQ packet) throws UnauthorizedException
40     {
41         IQ reply = IQ.createResultIQ(packet);
42         Element groups = packet.getChildElement();//1
43         
44         if (!IQ.Type.get.equals(packet.getType()))
45         {
46             System.out.println("非法的請求類型");
47             reply.setChildElement(groups.createCopy());
48             reply.setError(PacketError.Condition.bad_request);
49             return reply;
50         }
51         
52         String userName = StringUtils.substringBefore(packet.getFrom().toString(),"@");
53         GroupManager.getInstance().initElement(groups,userName);
54         reply.setChildElement(groups.createCopy());//2
55         System.out.println("返回的最終XML" + reply.toXML());
56         return reply;
57     }
58 }

    能夠看到主要有兩個方法,一個是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 方法,程序會出錯(程序會死鎖仍是什麼,忘記咧,很差以西)。

這就是程序的主體部分,我在這裏有一個建議,能不用Openfire 原始的程序函數,就不要用它們。個人提取數據庫方式都是本身寫的Bean,這樣有利於你本身對程序的掌控,其實更有利於快速開發(這世道不是啥都講究敏捷麼,哇哈哈)

三、打包插件

    打包依然遵循二次打包的原則(若是你不瞭解啥叫要二次打包,請看上一篇)。這是個人ant 文件,因爲Eclipse 幫我作了build 等不少工做,實際個人ant 工做就是在打包,並放入插件目錄下的plugin 文件夾下:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project name="IM" default="release" basedir=".">
 3 
 4     <property name="openfire.path"
 5         value="E:/workspace/europa/openfire_src/target/openfire" />
 6     <property name="classes.dir" value="classes" />
 7     <property name="lib.dir" value="lib" />
 8 
 9     <target name="jar">
10         <jar jarfile="${lib.dir}/grouptreeplugin.jar" basedir="${classes.dir}" >
11             <fileset dir=".">
12                 <include name="*.jar"/>
13             </fileset>
14         </jar>
15         <jar jarfile="${openfire.path}/plugins/groupTreePlugin.jar">
16             <fileset dir=".">
17                 <include name="lib/*.jar" />
18                 <include name="plugin.xml" />
19                 <include name="logo_small.gif" />
20                 <include name="logo_large.gif" />
21                 <include name="readme.html" />
22                 <include name="changelog.html" />
23                 <include name="build.xml" />
24             </fileset>
25         </jar>
26 
27     </target>
28 
29     <target name="release" depends="jar">
30     </target>
31 
32 </project>
相關文章
相關標籤/搜索