一、Smack庫概述
????Smack是一個(gè)開源、易用的XMPP/Jabber客戶端庫,它使用Java語言開發(fā),由Jive Software開發(fā)。
????Smack的優(yōu)點(diǎn)是編程簡單。
????Smack的缺點(diǎn)是其API并非為大量并發(fā)用戶設(shè)計(jì),每個(gè)客戶都要1個(gè)線程,占用資源相對較大,因此用Smack做模擬測試時(shí),1臺機(jī)器只能模擬有限(數(shù)千個(gè))客戶。
????截止2015年10月25日,Smack庫已經(jīng)發(fā)展到4.1.4版。最新的好消息是Smack在4.1.0版后將直接支持Android系統(tǒng),而無需再使用以前的Smack移植版aSmack庫了。Smack庫源碼托管于GitHub,主頁見: https://github.com/igniterealtime/Smack/
1.1 Smack 4的改變
Smack庫從3.4版發(fā)展到4.0.x版后,其API有較大的變化,主要有:
??1. 把Connection類重命名為XMPPConnection類,XMPPConnection類是XMPPTCPConnection類和XMPPBOSHConnection類的父類。
??2. 把各種Provider類進(jìn)行了分包
??3. keep-alive(持久連接)機(jī)制從smack-core庫移到了smack-extensions庫,keep-alive機(jī)制現(xiàn)在由PingManager類提供。
??4. PrivacyList類的toString()方法重命名為getName()
??5. 當(dāng)Chat實(shí)例的所有引用都撤掉后,應(yīng)該調(diào)用Chat.close()方法
否則Chat對象會有內(nèi)存泄露的隱患,直到ChatManager對象被垃圾回收器回收后內(nèi)存泄露隱患才會消失。
??6. ServerTrustManager類被移除了,如果要使用帶SSL認(rèn)證的XMPP,你只需提供自己的SSLContext對象給ConnectionConfiguration對象即可。
??7. Packet.setProperty()從smack-core庫移到了smack-extensions庫,其API現(xiàn)在可以在org.jivesoftware.smackx.jiveproperties包中找到。
??8. Connection.getAccountManager()方法現(xiàn)在改成了AccountManager.getInstance(XMPPConnection)方法
??9. 異常API做了改進(jìn)
??10.ToContains過濾器被移除了
1.2 Smack庫的組成
Smack庫可以內(nèi)嵌到任意的Java應(yīng)用程序中。Smack庫有數(shù)個(gè)JAR文件組成,非常具有靈活性。
??1. smack-core.jar提供了核心XMPP功能。都是XMPP RFC規(guī)范定義的XMPP特性。
??2. smack-extensions.jar支持許多由XMPP Standards Foundation定義的擴(kuò)展(XEP)功能。包括群聊、文件傳輸、用戶搜索等等。以后可查看文檔《擴(kuò)展手冊》:https://github.com/igniterealtime/Smack/blob/master/documentation/extensions/index.html(目前還是無效的)
??3. smack-experimental.jar支持許多由XMPP Standards Foundation定義的體驗(yàn)性(XEP)功能。其API和功能特性都被認(rèn)為是不穩(wěn)定的。
??4.smack-legacy.jar支持許多由XMPP Standards Foundation定義的遺留(XEP)功能。
??5. smack-bosh.jar支持BOSH通信(XEP-0124規(guī)范定義的)。此代碼被認(rèn)為處于Beta階段。
??6. smack-jingle.jar支持Jingle。此代碼很老,目前處于無維護(hù)的狀態(tài)。
??7. smack-resolver-dnsjava.jar支持對DNS SRV記錄的解析,主要用于那些不支持javax.naming API的平臺。
??8. smack-debug.jar用于協(xié)議流量的增強(qiáng)型GUI調(diào)試器。當(dāng)調(diào)試模式開啟后,如果它在類路徑下,它會自動(dòng)被使用。以后可查看文檔《調(diào)試模式》:https://github.com/igniterealtime/Smack/blob/master/documentation/debugging.html(目前還是無效的)
二、Smack的配置
Smack的初始化過程涉及到2階段的調(diào)用。
??1.初始化系統(tǒng)屬性通過SmackConfiguration類初始化所有的系統(tǒng)可訪問屬性,這些屬性都是通過getXXX方法取回屬性值的。
??2. 初始化啟動(dòng)類如果繼承了SmackInitializer接口后,都可以在調(diào)用initialize()方法后得到初始化,這意味著得到初始化的類在啟動(dòng)后都是活動(dòng)的。如果沒有繼承SmackInitializer接口,那么要實(shí)現(xiàn)初始化,必須要放置一個(gè)靜態(tài)代碼塊來實(shí)現(xiàn)——他在類裝載時(shí)會自動(dòng)執(zhí)行。初始化是通過配置文件來完成的。默認(rèn)情況下,Smack會載入Smack JAR文件中內(nèi)嵌的配置文件(它位于org.jivesoftware.smack/smack-config.xml)。這個(gè)指定的配置文件包含了一系列需載入初始化的類列表。所有的管理器類型的類都需要被初始化,這些管理器類就包含在上面所說的初始化列表中。
三、XMPPConnection管理
3.1 創(chuàng)建連接
org.jivesoftware.smack.XMPPConnection類可管理到XMPP服務(wù)器的連接,它默認(rèn)的連接實(shí)現(xiàn)類是org.jivesoftware.smack.XMPPTCPConnection。它主要使用兩個(gè)構(gòu)造方法,
??一個(gè)是XMPPTCPConnection(StringserverName)方法,參數(shù)為服務(wù)器名。連接會使用所有默認(rèn)的設(shè)置,有:
????1)執(zhí)行DNSSRV查詢,找到服務(wù)器確切的地址和端口(通常是5222)。
????2)與服務(wù)器協(xié)商最大數(shù)安全,包括TLS加密。但如果有必要,連接會回落到較低的安全設(shè)置。
????3)XMPP資源名“Smack”會被用于連接。
??第二個(gè)是XMPPTCPConnection(ConnectionConfigurationcc)構(gòu)造器,它會指定高級的連接設(shè)置。其中包括:
????1)手動(dòng)指定服務(wù)器地址和端口,而不是通過DNSSRV查詢。
????2)能開啟連接壓縮。
????3)指定自定義的連接資源名(如Work或Home)。用戶到服務(wù)器的每一個(gè)連接都必須有唯一的資源名。比如對于用戶"jsmith@example.com",完整的帶資源的地址應(yīng)該是"jsmith@example.com/Smack"。通過攜帶唯一的資源名,用戶可以同時(shí)從不同的位置登錄到同一個(gè)服務(wù)器,這適用于多設(shè)備的情況。每一個(gè)資源使用的在線優(yōu)先級值:用于決定由哪一個(gè)帶資源的指定連接來接收到地址"jsmith@example.com"的消息。
第一種連接方式:
boolean target = false;
AbstractXMPPConnection conn = new XMPPTCPConnection(username, password, serverName);
try {
conn.connect();
target = conn.isConnected();
if(target){
System.out.println("XMPP 服務(wù)器連接成功");
}else{
System.out.println("XMPP 服務(wù)器連接不成功");
}
} catch (SmackException | IOException | XMPPException e) {
e.printStackTrace();
}
第二種連接方式:
boolean target=false;
XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder()
.setUsernameAndPassword(username, password)
.setServiceName(serverName)
.setHost(serverName)
.setPort(5222)
.build();
AbstractXMPPConnection conn = new XMPPTCPConnection(config);
try {
conn.connect();
target = conn.isConnected();
if(target){
System.out.println("XMPP 服務(wù)器連接成功");
}else{
System.out.println("XMPP 服務(wù)器連接不成功");
}
} catch (SmackException | IOException | XMPPException e) {
e.printStackTrace();
}
3.2 連接和關(guān)閉連接
//為新連接創(chuàng)建配置
ConnectionConfigurationconfig = new ConnectionConfiguration(“jabber.org”, 5222);
AbstractXMPPConnectionconn = new XMPPTCPConnection(config);
//連接到服務(wù)器
conn.connect();
//登錄到服務(wù)器
conn.login(“username”,“password”,“SomeResource”);
…
//關(guān)閉連接
conn.disconnect();
????默認(rèn)情況下,一旦連接斷開,Smack會嘗試重建連接。使用ConnectionConfiguration類的setReconnectionAllowed(Boolean)方法可以開啟或關(guān)閉重連的功能。重連管理器會立即嘗試重連到服務(wù)器,并且會增加延時(shí)設(shè)置,以便提高重連的成功率。在重連管理器正在等待下一次重連的期間,如果你想強(qiáng)制重連,可以使用AbstractXMPPConnection類的connect()方法,它會嘗試建立一個(gè)新連接。如果手動(dòng)嘗試也失敗了,那么重連管理器會繼續(xù)重連的工作。
四、使用Chat消息通信
????來回收發(fā)消息是即時(shí)通信的核心功能。盡管單條消息是以包的形式發(fā)送和接收的,通常還是把他視為聊天的消息字符串,使用org.jivesoftware.smack.Chat類。
4.1 Chat類
????一個(gè)聊天Chat會在兩個(gè)用戶之間創(chuàng)建一個(gè)消息線程(通過線程ID)。下面的代碼片段演示了怎樣創(chuàng)建一個(gè)新聊天,然后向用戶發(fā)送一條文本消息:
//假設(shè)已經(jīng)創(chuàng)建了一個(gè)名為"connection"的XMPPConnection
ChatManager chatmanager = connection.getChatManager();
Chat newChat = chatmanager.createChat("jsmith@jivesoftware.com", newMessageListener(){
public void processMessage(Chat chat,Message message){
System.out.println(“Receivedmessage: “+ message);
}
});
try{
newChat.sendMessage(“Howdy!”);
}catch(XMPPExceptione){
System.out.println(“Error Deliveringblock”);
}
Chat.sendMessage(String)方法可以方便地創(chuàng)建一個(gè)消息Message對象,用字符串參數(shù)設(shè)置消息正文Body,然后發(fā)送消息。在某些情況下你可能希望在發(fā)送消息前設(shè)置額外的值,使用Chat.createMessage()方法和Chat.sendMessage(Message)方法,如下面的代碼片段所示:
Message newMessage = new Message();
newMessage.setBody(“Howdy!”);
message.setProperty(“favoriteColor”,“red”);
newChat.sendMessage(newMessage);
前面的例子中,我們可以注意到,在創(chuàng)建聊天Chat時(shí)指定了一個(gè)消息監(jiān)聽器MessageListener,在任意時(shí)刻,當(dāng)來自其它用戶的聊天消息到達(dá)后,消息監(jiān)聽器會得到通知。下面的代碼片段使用了監(jiān)聽器做鸚鵡學(xué)舌,它會回顯來自其他用戶傳遞的消息。
//假設(shè)在聊天Chat中已經(jīng)設(shè)置了消息監(jiān)聽器MessageListener
publicvoid processMessage(Chat chat, Message message){
// 把用戶發(fā)送的消息內(nèi)容發(fā)送給用戶
chat.sendMessage(message.getBody());
}
4.2 來電聊天
????當(dāng)提示有另一個(gè)用戶的聊天消息到了后,設(shè)置有輕微的不同,因?yàn)槟闶鞘状谓邮盏搅奶煜ⅰH〈鞔_地創(chuàng)建一個(gè)Chat來發(fā)送消息,當(dāng)ChatManager創(chuàng)建了Chat實(shí)例后,你需要注冊處理新創(chuàng)建的Chat實(shí)例。ChatManager會通過線程ID找到匹配的Chat,如果Chat不存在,那么它會創(chuàng)建一個(gè)新Chat對象來匹配。要得到這個(gè)新Chat,你必須注冊來得到通知。可以注冊一個(gè)消息監(jiān)聽器來接收所有要到來的消息。
//假定已經(jīng)創(chuàng)建了名為”connection”的XMPPConnection
ChatManager cm = connection.getChatManager().addChatListener(
new ChatManagerListener(){
@Override
public void chatCreated(Chat chat, BooleancreatedLocally){
if(!createdLocally)
chat.addMessageListener(newMyNewMessageListener());
}
});
????除了基于線程的Chat消息,也有一些客戶端不發(fā)送線程ID作為Chat的一部分。要處理這種情況,Smack會基于JID嘗試匹配接收的消息到最匹配現(xiàn)有的Chat。它會嘗試用完整的JID來查找Chat,如果搜不到,再嘗試用基本的JID來查找Chat。如果找不到現(xiàn)有的Chat來匹配,那么會創(chuàng)建一個(gè)新Chat。
五、名單Roster和在線狀態(tài)Presence
名單可以讓你跟蹤其他用戶是否在線,而且名單可以讓你把用戶組織到群組,比如朋友群或工作群。而其它的即時(shí)通信IM系統(tǒng)則把名單Roster視為好友列表、聯(lián)系人列表等等。
5.1 名單條目
????Roster用于跟蹤其他用戶是否在線。用戶的聯(lián)系人可以以分組的方式進(jìn)行組織,比如“好友”、“同事”。然后就可以查看組中的每個(gè)用戶是否在線了。要檢索Roster,使用XMPPConnection.getRoster()方法。Roster類允許你查找所有的Roster實(shí)體,以及他們屬于哪個(gè)組,每個(gè)實(shí)體當(dāng)前的在線狀態(tài)。名單中的每一個(gè)用戶都由RosterEntry來表示,它包括:
????1)一個(gè)XMPP地址(比如”jsmith@example.com”)
????2)你為用戶編寫的備注姓名(比如”Joe”)
????3)名單中的群列表。如果名單的條目不屬于任何群組,那么它被稱為"unfiledentry"。
下面的代碼片段會打印名單中所有的條目:
//假定已經(jīng)創(chuàng)建了名為”connection”的XMPPConnection
Rosterroster = connection.getRoster();
Collection<RosterEntry>entries = roster.getEntries();
for(RosterEntryentry : entries){
System.out.println(entry);
}
還有獲取單個(gè)條目的方法、獲取"unfiledentry"的方法,獲取一個(gè)群或所有群的方法。
5.2 在線狀態(tài)
????名單中的每個(gè)條目都有一個(gè)與之相關(guān)的在線狀態(tài)。Roster.getPresence(Stringuser)方法會返回一個(gè)表示用戶是否在線的Presence對象。如果為空是你還沒有訂閱用戶是否在線的返回。注意:通常情況下,在線狀態(tài)的訂閱總是綁定到名單中的用戶,但這并不適應(yīng)所有的情況。
????一個(gè)用戶的在線狀態(tài)要么是在線,要么是離線。當(dāng)用戶在線時(shí),他們的在線狀態(tài)還可以包含擴(kuò)展的信息,比如用戶當(dāng)前正在做什么,用戶是否愿意被打擾等等。具體參考Presence類。
5.3 監(jiān)聽名單Roster和在線狀態(tài)Presence的改變
????Roster類的典型應(yīng)用場景是以樹狀結(jié)構(gòu)顯示用戶群和列表,并且用戶列表中包含用戶是否在線的狀態(tài)。比如,參考下圖所示的一個(gè)ExodusXMPP客戶端的Roster。
????在線狀態(tài)的信息可能會經(jīng)常變化,Roster條目也可能經(jīng)常修改或刪除。要監(jiān)聽Roster和Presence數(shù)據(jù)的變化,你應(yīng)該使用RosterListener。要得到Roster改變的所有提醒,那么必須在登錄XMPP服務(wù)器之前注冊RosterListener。下面的代碼片段注冊了一個(gè)Roster的RosterListener,它能夠在標(biāo)準(zhǔn)輸出中打印任何Presence的改變。一個(gè)標(biāo)準(zhǔn)的客戶端可以使用類似的代碼用變化的信息來更新Roster界面。
//假定已經(jīng)創(chuàng)建了名為”connection”的XMPPConnection
Roster roster = connection.getRoster();
roster.addRosterListener(new RosterListener(){
// 忽略事件
public void entriesAdded(Collection<String> addresses){}
public void entriesDeleted(Collection<String>addresses){}
public void entriesUpdated(Collection<String> addresses){}
public void presenceChanged(Presencepresence){
System.out.println(“Presencechanged: “+ presence.getFrom() + “ “ + presence);
}
});
5.4 添加Entries到Roster
????Roster和Presence使用一種基于權(quán)限的模型,用戶必須得到其他人的許可才能把這些人添加到Roster。這樣可以保護(hù)用戶的隱私,確保了只有獲得同意的用戶才能查看到他們的Presence信息。因此,當(dāng)你想添加某個(gè)用戶到你的Roster中,必須得到該用戶接受你的請求才可以。
如果有用戶請求訂閱你的在線狀態(tài)Presence,這個(gè)用戶必須先把你添加到他的Roster,因此他會發(fā)起請求,你必須選擇接受或拒絕該請求。Smack通過以下三種方式來處理Presence的預(yù)訂請求:
????1)自動(dòng)接受所有Presence的預(yù)訂請求
????2)自動(dòng)拒絕所有Presence的預(yù)訂請求
????3)手動(dòng)處理每一個(gè)Presence預(yù)訂請求
????這三種方式可以通過Roster.setSubscriptionMode(intsubscriptionMode)方法來設(shè)置請求的處理方式。簡單的客戶端通常使用第一種自動(dòng)方式處理預(yù)訂請求,而功能比較全的客戶端應(yīng)該選擇第三種手動(dòng)處理請求的方式,讓終端用戶自行決定是接受請求或是拒絕請求。如果使用手動(dòng)方式,應(yīng)該注冊一個(gè)PacketListener來監(jiān)聽Presence.Type.SUBSCRIBE類型的Presence包。
六、讀寫Packet(數(shù)據(jù)包)
????從客戶端發(fā)送到XMPP服務(wù)器的每一條消息都稱為一個(gè)Packet(數(shù)據(jù)包)。org.jivesoftware.smack.packet庫中包含了XMPP支持的消息Message、在線狀態(tài)Presence、IQ)三種不同的基本數(shù)據(jù)包類型的封裝類。而像Chat或GroupChat這樣的類則提供了更高層的結(jié)構(gòu)來管理數(shù)據(jù)包的自動(dòng)創(chuàng)建和發(fā)送。但是,開發(fā)者還是可以直接創(chuàng)建和發(fā)送數(shù)據(jù)包的。下面的代碼就是修改自己的在線狀態(tài),讓其他人知道你不在線。
//假設(shè)已經(jīng)創(chuàng)建了一個(gè)名為"connection"的XMPPConnection
Presence presence = new Presence(Presence.Type.unavailable);
presence.setStatus("Gone fishing");
connection.sendStanza(presence);
6.1 PacketListener與PacketCollector
Smack提供了兩種讀取到來的數(shù)據(jù)包的方式:
????org.jivesoftware.smack.PacketCollector(包收集器):該類提供synchronously(同步)方法,等待接受數(shù)據(jù)包(Packets).
????org.jivesoftware.smack.PacketListener(包監(jiān)聽器): 該接口提供asynchronously(異步)方法,等待結(jié)束數(shù)據(jù)(Packets)。
????兩者都使用PacketFilter實(shí)例來判斷應(yīng)該處理哪一個(gè)數(shù)據(jù)包。PacketListener(包監(jiān)聽器)用于事件風(fēng)格的編程,而PacketCollector(包收集器)有一個(gè)數(shù)據(jù)包的結(jié)果隊(duì)列,你可以做輪詢或阻塞等操作。也就是說,如果你想在數(shù)據(jù)包到來時(shí)執(zhí)行一些動(dòng)作,那么包監(jiān)聽器很適合。如果你想等待指定的數(shù)據(jù)包的到來,那么包收集器很適合。包收集器和包監(jiān)聽器都使用Connection連接實(shí)例創(chuàng)建。
????數(shù)據(jù)包集合(PacketCollector)和數(shù)據(jù)包接口(PacketListener)是通過XMPPConnection實(shí)例對象創(chuàng)建。
6.2 攔截器
????org.jivesoftware.smack.filter.StanzaFilter接口可以決定,那些特定的數(shù)據(jù)包會被傳遞到PacketCollector 或者PacketListener。
許多預(yù)先定義的攔截器都實(shí)現(xiàn)了org.jivesoftware.smack.filter接口。
下面的代碼片段,演示了注冊的數(shù)據(jù)包監(jiān)聽器和數(shù)據(jù)包集合
// Create a packet filter to listen for new messages from a particular
// user. We use an AndFilter to combine two other filters._
StanzaFilter filter = new AndFilter(new StanzaTypeFilter(Message.class),
new FromContainsFilter("mary@jivesoftware.com"));
// Assume we've created an XMPPConnection name "connection".
// First, register a packet collector using the filter we created.
PacketCollector myCollector = connection.createPacketCollector(filter);
// Normally, you'd do something with the collector, like wait for new packets.
// Next, create a packet listener. We use an anonymous inner class for brevity.
PacketListener myListener = new PacketListener() {
**public** **void** processPacket(Packet packet) {
// Do something with the incoming packet here._
}
};
// Register the listener._
connection.addPacketListener(myListener, filter);
標(biāo)準(zhǔn)段落(字符)攔截器
????smack 類庫中已經(jīng)包含很多包攔截器,你也可以創(chuàng)建屬于自己的攔截器,只需要代碼實(shí)現(xiàn)StanzaFilter接口,默認(rèn)的攔截器包含如下:
- StanzaTypeFilter --特定類型的數(shù)據(jù)包過濾器
- StanzaIdFilter -- 特定數(shù)據(jù)包標(biāo)識篩選器
- ThreadFilter -- 特定線程標(biāo)識數(shù)據(jù)包過濾器
- ToContainsFilter -- 特定發(fā)送地址數(shù)據(jù)包過濾器
- FromContainsFilter -- 特定接受地址數(shù)據(jù)包過濾器.
- StanzaExtensionFilter -- 特定包擴(kuò)展數(shù)據(jù)包過濾器
- AndFilter -- implements the logical AND operation over two filters.
- OrFilter -- implements the logical OR operation over two filters.
- NotFilter -- implements the logical NOT operation on a filter.
七、信息包插件提供者
????Smack提供的體系是堵塞自定義的XML信息包擴(kuò)展和IQ包分析器的系統(tǒng)(The Smack provider architecture is a system for plugging in custom XML parsing of packet extensions and IQ packets)。標(biāo)準(zhǔn)的Smack擴(kuò)展(Smack Extensions)是使用提供者的體系結(jié)構(gòu)搭建的。存在以下兩種類型的提供者:
- IQProvider –將IQ請求( IQ requests)解析成Java對象(Java objects)
- PacketExtension – 將附屬在信息包上的XML子文檔解析成信息包擴(kuò)展實(shí)例(PacketExtension instances)
7.1 IQProvider
???? IQProvider 默認(rèn)情況下,Smack只知道如何處理類似以下幾個(gè)名字空間的子信息包的IQ信息包(IQ packets):
???? jabber:iq:auth
???? jabber:iq:roster
???? jabber:iq:register
因?yàn)樵S多IQ類型是XMPP及其擴(kuò)展部分的一部分,所以提供一個(gè)可插入的IQ分析機(jī)制。IQ Providers被程序自動(dòng)的注冊或通過創(chuàng)建在你的JAR 文件的META-INF目錄下創(chuàng)建一個(gè)mack.providers文件。該文件是一個(gè)包含一個(gè)或多個(gè)iqProvider條目(iqProvider entries)的XML文檔,如下例所示:
<?xml version="1.0"?>
<smackProviders>
<iqProvider>
<elementName>query</elementName>
<namespace>jabber:iq:time</namespace>
<className>org.jivesoftware.smack.packet.Time</className>
</iqProvider>
</smackProviders>
????每一個(gè)IQ provider都和一個(gè)元素名(element name)和名字空間( namespace)相聯(lián)系。在上面的例子中,元素名是query,名字空間是abber:iq:time。如果有多重提供者條目(multiple provider entries)嘗試注冊并控制相同的名字空間,那么從類路徑(classpath)載入的第一個(gè)條目將有優(yōu)先權(quán)。
????IQ provider類可以實(shí)現(xiàn)IQProvide接口,或者繼承IQ類。在前面的例子中,每一個(gè)IQProvider負(fù)責(zé)解析原始的XML流從而創(chuàng)建一個(gè)IQ實(shí)例。在下面的例子中,bean introspection將被用于嘗試自動(dòng)使用在IQ packet XML中發(fā)現(xiàn)的值設(shè)置IQ實(shí)例的屬性。一個(gè)XMPP時(shí)間信息包如下所示:
<iq type=’result’ to=’joe@example.com’ from=’mary@example.com’ id=’time_1’>
<query xmlns=’jabber:iq:time’>
<utc>20020910T17:58:35</utc>
<tz>MDT</tz>
<display>Tue Sep 10 12:58:35 2002</display>
</query>
</iq>
????為了讓這個(gè)信息包自動(dòng)的映射成上面的providers file中所列的時(shí)間對象(Time object),它必須有以下幾個(gè)方法:setUtc(String), setTz(String), 和 setDisplay(String)。自動(dòng)檢查(introspection)的服務(wù)將試著自動(dòng)的將字符串值轉(zhuǎn)化成a boolean, int, long, float, double,或 Class 類型。轉(zhuǎn)化成何種類型由IQ實(shí)例的需要來決定。
7.2 PacketExtensionProvider
????PacketExtensionProvider 信息包插件提供者(Packet extension providers)為信息包提供一個(gè)可插入的系統(tǒng),這些信息包是一個(gè)IQ, message和presence packets的自定義名字空間的子元素。每一個(gè)插件提供者(extension provider)使用一個(gè)元素名(element name)和名字空間(namespace)在smack.providers文件中注冊,如下例所示:
<?xml version="1.0"?>
<smackProviders>
<extensionProvider>
<elementName>x</elementName>
<namespace>jabber:iq:event</namespace>
<className>org.jivesoftware.smack.packet.MessageEvent</className>
</extensionProvider>
</smackProviders>
????如果有多重提供者條目(multiple provider entries)嘗試注冊并控制相同的名字空間,那么從類路徑(classpath)載入的第一個(gè)條目將有優(yōu)先權(quán)。 一旦在一個(gè)信息包中發(fā)現(xiàn)信息包插件,解析器將傳遞到正確的提供者。每一個(gè)提供者可以實(shí)現(xiàn)PacketExtensionProvider接口或者是一個(gè)標(biāo)準(zhǔn)的Java Bean。在前面的例子中,每一個(gè)插件提供者(extension provider)負(fù)責(zé)解析原始的XML流去構(gòu)造一個(gè)實(shí)例。在下面的例子中,bean introspection將被用于嘗試自動(dòng)使用在信息包插件子元素(packet extension sub-element)中的值設(shè)置類的屬性。 當(dāng)一個(gè)插件提供者(extension provider)沒有用元素名(element name)和名字空間(namespace)對注冊是,Smack將存儲所有在缺省信息包插件(DefaultPacketExtension)對象中的最高級別元素(top-level elements),并匹配到信息包上。
七、smack debug模式講解
????smakc 內(nèi)置兩套debuging 控制臺,讓你追蹤XMPP服務(wù)端和客戶端之間所有XML 流動(dòng),在smack-debug.jar包含Lite調(diào)試器和增強(qiáng)調(diào)試器,在smack-core.jar包含debug輸出控制臺。
調(diào)試模式可以用不同的方式來啟用:
1、在創(chuàng)建新連接之前,先添加以下代碼行:
SmackConfiguration.DEBUG = true;
1、設(shè)置Java系統(tǒng)屬性smack.debugenabled真實(shí)。可以在命令行上設(shè)置系統(tǒng)屬性,例如:
java -Dsmack.debugEnabled=true SomeApp
如果你希望在你的應(yīng)用程序中顯式禁用調(diào)試模式,可以通過使用命令行參數(shù) 或者是在在打開新連接之前,請?jiān)趹?yīng)用程序中添加以下行:
SmackConfiguration.DEBUG = false;
smack 使用以下邏輯決定調(diào)試控制臺使用;
1、它首先會嘗試使用在Java系統(tǒng)屬性指定類smack.debuggerclass調(diào)試器。如果你需要開發(fā)屬于自己的Debug,需要smackdebugger接口,然后設(shè)置系統(tǒng)屬性的命令行如:
java -Dsmack.debuggerClass=my.company.com.MyDebugger SomeApp
2、如果第一步失敗,Smack會嘗試使用增強(qiáng)型Debug,smackx-debug.jar文件中包含了增強(qiáng)型Debug,因此,你需要將jar文件的路徑添加到ClassPath路徑中。smack-core.jar Lite調(diào)試器和增強(qiáng)調(diào)試器不可用的情況下,只有控制臺調(diào)試器。
增強(qiáng)調(diào)試器
????當(dāng)smack Debug模式是啟動(dòng)時(shí),Debug窗口會顯示每一個(gè)創(chuàng)建連接信息,同時(shí)也包含以下信息:
- XMPPConnection tabs -- 每個(gè)選項(xiàng)卡顯示有關(guān)連接的調(diào)試信息.
- Smack info tab -- shows information about Smack (e.g. Smack version, installed components, etc.). The connection tab will contain the following information:
- All Stanzas --通過smack 分析發(fā)送和接收的數(shù)據(jù)包的信息分析.
- Raw Sent Stanzas -- 通過Smack產(chǎn)生的原始XML流量發(fā)送到服務(wù)器.
- Raw Received Stanzas -- 服務(wù)器向客戶端發(fā)送的原始XML.
- Ad-hoc message -- 允許發(fā)送特定類型的數(shù)據(jù)包.
- Information -- 顯示連接狀態(tài)和統(tǒng)計(jì)數(shù).
Lite Debugger
????當(dāng)smack Debug模式是啟動(dòng)時(shí),Debug窗口會顯示每一個(gè)創(chuàng)建連接信息,同時(shí)也包含以下信息:
- Client Traffic (red text) -- 通過Smack產(chǎn)生的原始XML流量發(fā)送到服務(wù)器.
- Server Traffic (blue text) --服務(wù)器向客戶端發(fā)送的原始XML.
- Interpreted Stanzas (green text) -- shows XML packets from the server as parsed by Smack. Right click on any of the panes to bring up a menu with the choices to copy of the contents to the system clipboard or to clear the contents of the pane.