簡單插件開發已經講解了簡單的openfire插件怎樣製作,本篇將會講解如何實現一個簡單的和客戶端交互的插件。
首先我們要知道,openfire 的四種常用的插件集成方式:
(1)Component:可以接收一個特定子域(sub-domain)的所有包。比如test_componet.hoo.com。所以一個發送給jojo@test_componet.hoo.com的包將被轉發給這個componet.
(2)IQHandler:相應包中特定的元素名或命名空間。下面的代碼展示瞭如何註冊一個IQHandler.
IQHandlermyHandler = new MyIQHander();
IQRouteriqRouter = XMPPServer.getInstance().getIQRouter();
iqRouter.addHandler(myHandler);
(3)PacketInterceptor:這種方式可以接收系統傳輸的所有包,並可以隨意的丟棄它們。例如,一個interceptor可以攔截並丟棄所有含有不健康信息的消息,或者將它們報告給系統管理員。
(4)使用JiveGlobals.getProperty(String)和 JiveGlobals.setProperty(String, String)方法將我們的插件設置爲openfire的一個全局屬性。通過實現org.jivesoftware.util.PropertyEventListener方法可以將我們的插件做成一個屬性監聽器監聽任何屬性的變化。通過PropertyEventDispatcher.addListener(PropertyEventListener)方法可以註冊監聽。要注意的一點是,一定要在destroyPlugin()方法中將註冊的監聽註銷。
本篇文章將會主要使用IQHandler來實現openfire插件,有關使用PacketInterceptor實現可以參考文章:
http://www.cnblogs.com/hoojo/archive/2013/03/29/2988437.html
(一)確定客戶端和服務器端進行通訊的IQ的樣式
iq類型有四種:get、set、error、result。
這裏我們主要討論get類型,其餘類型的處理方式類似。
我們設計的客戶端請求格式如下:
<iq id="H40yo-4" type="get"from="test@yourhost/Smack">
<username>111</username>
<query xmlns="hoo.iq.userinfo">
</query>
</iq>
服務器端返回格式如下:
<iq type="result" id="VKe0a-4"to="test@yourhost/Smack">
<query xmlns="hoo.iq.userinfos">
<username>111</username>
</query>
</iq>
(二)編寫繼承IQHandler的類,用於攔截來自客戶端的IQ請求:
[java] view plaincopy
1. package com.hoo.server.plugin;
2.
3. import org.dom4j.DocumentHelper;
4. import org.dom4j.Element;
5. import org.dom4j.QName;
6. import org.jivesoftware.openfire.IQHandlerInfo;
7. import org.jivesoftware.openfire.XMPPServer;
8. import org.jivesoftware.openfire.auth.UnauthorizedException;
9. import org.jivesoftware.openfire.handler.IQHandler;
10. import org.jivesoftware.openfire.session.ClientSession;
11. import org.slf4j.Logger;
12. import org.slf4j.LoggerFactory;
13. import org.xmpp.packet.IQ;
14. import org.xmpp.packet.PacketError;
15.
16. import com.hoo.server.plugin.entity.UserInfo;
17. public class UserInfoIQHander extends IQHandler{
18. private static final Logger Log = LoggerFactory.getLogger(UserInfoIQHander.class);
19. private IQHandlerInfo info;
20. @Override
21. public void initialize(XMPPServer server) {
22. super.initialize(server);
23. dbUserInfoManager = DbUserInfoManager.getInstance();
24. }
25. public UserInfoIQHander() {
26. super("user info");
27. info = new IQHandlerInfo("query", "hoo.iq.userinfo"); //這裏和我們自定義的iq格式域相同
28. }
29.
30. @Override
31. public IQHandlerInfo getInfo() {
32. return info;
33. }
34.
35. @Override
36. public IQ handleIQ(IQ packet) throws UnauthorizedException {
37. System.out.println(packet.toXML());
38. IQ reply = null;
[java] view plaincopy
1. reply = IQ.createResultIQ(packet);//根據packet創建一個翻譯iq</pre> try { ClientSession session = sessionManager.getSession(packet.getFrom()); if (session == null) { Log.error("Error
2. during userInfo. Session not found in " + sessionManager.getPreAuthenticatedKeys() + " for key " + packet.getFrom()); // This error packet will probably won't make it through
3. reply.setChildElement(packet.getChildElement().createCopy());
4. reply.setError(PacketError.Condition.internal_server_error);
5. return reply; } if (IQ.Type.get.equals(packet.getType())) { //獲取數據
6. Element iq = packet.getElement(); //獲取packet中的iq段
7. Element query = iq.element("query"); //從iq段中獲取query段
8. Element username = query.element("username");
9. String userNameStr = null; if(username!=null){
10. userNameStr = username.getText();//獲取username段中的文字
11. }else{ //沒有傳給我username reply.setChildElement(packet.getChildElement().createCopy());
12. reply.setError(PacketError.Condition.item_not_found);//爲返回數據中填充錯誤信息
13. return reply;
14. } //在安卓端需要配置ProviderManager對數據進行解析 Element
15. probeResult = DocumentHelper.createElement(QName.get("query", "hoo.iq.userinfo")) ; probeResult.addElement("username").setText("返回的username");return reply; }} catch (Exception e) {Log.error(e.getMessage(), e);
16. reply.setChildElement(packet.getChildElement().createCopy());
17. reply.setError(PacketError.Condition.internal_server_error);//遠程服務器錯誤,客戶端無法明確知道發生了什麼,客戶端也無需知道發生了什麼。
18. return reply;}
19. return reply
20. } }
(三)在自定義的plugin中註冊自定義IQHandler,只要在你的plugin類的initializePlugin方法中添加如下語句:
21. server.getIQRouter().addHandler(new UserInfoIQHander());
這樣在開啓服務後,openfire就會自動監聽hoo.iq.userinfo域的iq query請求。
服務器端的編譯和打包見上一篇文章。
(四)在客戶端添加對hoo.iq.userinfo返回的數據的解析,首先自定義一個iq。
22. //自定義一個iq,用於接收和發送自定義iq</pre><pre code_snippet_id="134255" snippet_file_name="blog_20131230_5_2676678" name="code" class="java" style="color: rgb(51, 51, 51); font-size: 13px; line-height: 19px;">public class UserInfo extends IQ{
23. private String username;
24.
25.
26. public String getElementName() {
27. return "query";
28. }
29.
30. public String getNamespace() {
31. return "hoo.iq.userinfo";
32. }
33.
34. public String getBody() {
35. StringBuilder sb = new StringBuilder();
36. if(null!=username){
37. sb.append("<username>").append(username).append("</username>");
38. }
39. return sb.toString();
40. }
41. public String getUsername() {
42. return username;
43. }
44.
45.
46. public void setUsername(String username) {
47. this.username = username;
48. }
49.
@Override
50. public String getChildElementXML() {
51. StringBuilder sb = new StringBuilder();
52. sb.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append("\">").append(getBody()).append("</").append(getElementName()).append(">");
53. return sb.toString();
54. }
55. }
然後編寫對服務器端傳輸來的數據進行解析的IQProvider
56. import java.io.ByteArrayInputStream;
57. import java.io.IOException;
58.
59. import javax.xml.parsers.DocumentBuilder;
60. import javax.xml.parsers.DocumentBuilderFactory;
61.
62. import org.jivesoftware.smack.packet.IQ;
63. import org.jivesoftware.smack.provider.IQProvider;
64. import org.jivesoftware.smack.util.StringUtils;
65. import org.w3c.dom.Document;
66. import org.w3c.dom.Element;
67. import org.w3c.dom.Node;
68. import org.w3c.dom.NodeList;
69. import org.xmlpull.v1.XmlPullParser;
70. import org.xmlpull.v1.XmlPullParserException;
71.
72. import android.util.Log;
73.
74.
75.
76. public class UserInfoProvider implements IQProvider{
77.
78. private static final String PREFERRED_ENCODING = "UTF-8";
79.
80. /***通過這個方法我們可以獲取從服務器傳來iq**/
81. public IQ parseIQ(XmlPullParser parser) throws Exception {
82. final StringBuilder sb = new StringBuilder();
83. try {
84. int event = parser.getEventType();
85. // get the content
86. while (true) {
87. switch (event) {
88. case XmlPullParser.TEXT:
89. // We must re-escape the xml so that the DOM won't throw an exception
90. sb.append(StringUtils.escapeForXML(parser.getText()));
91. break;
92. case XmlPullParser.START_TAG:
93. sb.append('<').append(parser.getName()).append('>');
94. break;
95. case XmlPullParser.END_TAG:
96. sb.append("</").append(parser.getName()).append('>');
97. break;
98. default:
99. }
100.
101. if (event == XmlPullParser.END_TAG && "query".equals(parser.getName())) break;
102.
103. event = parser.next();
104. }
105. }
106. catch (XmlPullParserException e) {
107. e.printStackTrace();
108. }
109. catch (IOException e) {
110. e.printStackTrace();
111. }
112.
113. String xmlText = sb.toString();
114. return new UserInfo();
115. }
116. }
最後將IQProvider 在客戶端註冊下:在connect 時進行註冊
117. ProviderManager.getInstance().addIQProvider("query", "hoo.iq.userinfo", new UserInfoProvider());
(五)測試
118. 在客戶端拼接請求:
119. UserInfo b = newUserInfo ();
120. b.setUsername("111");
121. b.setType(IQ.Type.GET);
122. PacketFilter filter = new AndFilter(new PacketIDFilter(
123. b.getPacketID()), new PacketTypeFilter(IQ.class));
124. PacketCollector collector = connection.createPacketCollector(filter);
125. connection.sendPacket(b);
126. UserInfo re = (UserInfo) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
127. collector.cancel();// 停止請求results(是否成功的結果)System.out.println(re.toXML());