目錄
一、開發(fā)環(huán)境設(shè)置.... 3
1.1 JDK 安裝.... 3
1.2 MySql Server安裝.... 4
1.3 OpenFire安裝.... 6
1.4 Openfire Admin 功能.... 14
1.4.1 用戶摘要:.... 16
1.4.2 組摘要:.... 16
1.4.3 用戶組管理:.... 17
1.4.4 發(fā)送管理消息:.... 17
二、TVBox Client 端APK開發(fā).... 18
2.1 自啟動(dòng)Webview播放視頻.... 18
2.1.1自啟動(dòng)任務(wù):.... 18
2.1.2WebView 播放視頻.... 19
2.2 登錄Openfire並抓取群組數(shù)據(jù).... 20
2.2.1Openfire 登錄設(shè)定.... 20
2.2.2Openfire登錄及數(shù)據(jù)抓取.... 22
2.3 Openfire自動(dòng)註冊(cè)登錄並加入群組.... 25
2.3.1 Openfire自動(dòng)註冊(cè)登錄.... 26
2.3.2加入群組並建立消息監(jiān)聽(tīng).... 27
三、FHWTVBox Manager 開發(fā).... 28
3.1 界面及功能.... 28
3.1.1群組管理.... 29
3.1.2機(jī)頂盒管理.... 30
3.2 開發(fā)涉及技術(shù)點(diǎn).... 30
3.2.1Java Swing組件.... 30
3.2.2生成可執(zhí)行jar. 30
3.2.3 HttpClient獲取及操作openfire 數(shù)據(jù).... 31
四、FHWTVBox Client和FHWTVBoxManager整合應(yīng)用.... 34
4.1FHWTVBox Client端安裝設(shè)定.... 34
4.2FHWTVBoxManager 管理操作.... 37
五、android版FHWTVBox Admin. 42
六、FHWTVBox Openfire Plugin JSP. 47
6.1 TVBox JSP Plugin插件及部署.... 47
6.2TVBox Plugin登錄及使用.... 48
6.2.1 發(fā)送消息.... 49
6.2.2查看回復(fù).... 50
6.2.3 機(jī)頂盒管理.... 52
6.3TVBox Plugin開發(fā).... 53
6.3.1Openfire源碼部署及設(shè)定.... 53
6.3.2Openfire源碼編譯.... 56
6.3.3Openfire plugin 代碼結(jié)構(gòu).... 57
6.3.4Openfire plugin Class開發(fā).... 61
6.3.5Openfire plugin JSP開發(fā).... 64
七、FHW TVBox按群組管理 69
正文
運(yùn)行jdk-6u27-windows-i586.exe(或者其他更高版本的JDK安裝程序,64位windows系統(tǒng),請(qǐng)安裝64位JDK),設(shè)定好環(huán)境變量,按照提示一步一步安裝完畢。
1.安裝JDK,安裝過(guò)程中可以自定義安裝目錄等信息,例如我們選擇安裝目錄為D:/java/jdk1.6.0_27;
2.安裝完成后,右擊“我的計(jì)算機(jī)”,點(diǎn)擊“屬性”;
3.選擇“高級(jí)”選項(xiàng)卡,點(diǎn)擊“環(huán)境變量”;
4.在“系統(tǒng)變量”中,設(shè)置3項(xiàng)屬性,JAVA_HOME,PATH,CLASSPATH(大小寫無(wú)所謂),若已存在則點(diǎn)擊“編輯”,不存在則點(diǎn)擊“新建”;
5.JAVA_HOME指明JDK安裝路徑,就是剛才安裝時(shí)所選擇的路徑D:/java/ jdk1.6.0_27,此路徑下包括lib,bin,jre等文件夾(此變量最好設(shè)置,因?yàn)橐院筮\(yùn)行tomcat,eclipse等都需要依此變量);
Path使得系統(tǒng)可以在任何路徑下識(shí)別java命令,向已有的PATH變量中繼續(xù)添加如下: %JAVA_HOME%/bin;%JAVA_HOME%/jre/bin, 記得要與前一個(gè)用; 隔開;
CLASSPATH為java加載類(class or lib)路徑,只有類在classpath中,java命令才能識(shí)別,設(shè)為: .;%JAVA_HOME%/lib/dt.jar;%JAVA_HOME%/lib/tools.jar (注意要加 . 表示當(dāng)前路徑)
6.“開始”->;“運(yùn)行”,鍵入“cmd”;
7.鍵入命令“java -version”,“java”,“javac”幾個(gè)命令,出現(xiàn)以下畫面,說(shuō)明環(huán)境變量配置成功;若不成功,請(qǐng)重復(fù)前面的步驟仔細(xì)檢查。
8. 鍵入命令 java -Xms4095M -Xmx4096M –version 如果報(bào)錯(cuò), 說(shuō)明為32位JDK因?yàn)槠淅碚搩?nèi)存最大為4G;如果不報(bào)錯(cuò),說(shuō)明為64位JDK。
先下載MySql Server,目前我們安裝的是mysql-5.1.31-win32.msi版本(64位windows系統(tǒng),請(qǐng)安裝mysql-essential-5.1.59-winx64.msi)的,按照提示一步一步安裝完畢即可。記住設(shè)定的root用戶的密碼,下面會(huì)用到。
因MySql Server的windows版與linux版對(duì)大小寫的敏感度不一致,因此需要設(shè)定下:
a. Windows下在MySql Server安裝目錄下,找到my.ini檔,在最后面加上:lower_case_table_names=0, (0為大小寫敏感,1為大小寫不敏感), 然后重啟MySql Server;
b. Linux下,在/etc/my.cnf中的[mysqld]后添加lower_case_table_names=0 (0為大小寫敏感,1為大小寫不敏感),重啟MySql Server即可。
mySql安裝完畢,需打開MySql Query Browser(或者用其他數(shù)據(jù)庫(kù)連接工具也可以),創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)給Openfire使用(通常命名為openfire):
登錄后,在Resultset 1中輸入create database openfire,執(zhí)行;
或者在右邊Schemata里的任一數(shù)據(jù)庫(kù)上單擊右鍵,選擇create new schema:
Schema name輸入openfire后點(diǎn)擊OK:
后在右邊Schema里的任一數(shù)據(jù)庫(kù)上單擊右鍵選擇refresh,就會(huì)看到openfire數(shù)據(jù)庫(kù):
運(yùn)行下載的openfire安裝程序(openfire_3_7_1.exe) ,按照提示,選擇語(yǔ)言(中文簡(jiǎn)體或英文均可)及安裝路徑,完成安裝。
待安裝程序運(yùn)行完后,會(huì)運(yùn)行一個(gè)OpenFire的程序(如下圖),當(dāng)出現(xiàn)如圖所示的接口時(shí),打開瀏覽器,訪問(wèn)http://127.0.0.1:9090或單擊Launch Admin按鈕,進(jìn)行具體配置:
如果在此界面提示錯(cuò)誤:log4j:ERROR setFile(null,true) call failed.
java.io.FileNotFoundException: C:\Program Files (x86)\Openfire\bin\..\logs\error.log (Access is denied)
請(qǐng)?jiān)诎惭b完成后,設(shè)定以管理員權(quán)限運(yùn)行openfire:
登陸或Launch Admin后,整個(gè)界面圖如下:
左邊可以看到相應(yīng)的安裝進(jìn)度:
首先選擇語(yǔ)言,按個(gè)人喜好來(lái);接下來(lái)是服務(wù)器設(shè)置,如果沒(méi)有特殊設(shè)定,可以使用default值;
數(shù)據(jù)庫(kù)設(shè)置:可按服務(wù)器或項(xiàng)目配置設(shè)置相應(yīng)的數(shù)據(jù)庫(kù),在此我們選擇標(biāo)準(zhǔn)數(shù)據(jù)庫(kù)以連接到目前我們項(xiàng)目同源的MySql數(shù)據(jù)庫(kù)中;
在數(shù)據(jù)庫(kù)驅(qū)動(dòng)選項(xiàng)選擇MySql,然后修改數(shù)據(jù)庫(kù)URL:jdbc:mysql://[host-name]:3306/[database-name] 為
jdbc:mysql://127.0.0.1:3306/openfire
數(shù)據(jù)庫(kù)IP請(qǐng)?zhí)钸x正確的MySQL程序安裝的服務(wù)器IP,openfire為數(shù)據(jù)庫(kù)名(請(qǐng)?zhí)崆霸跀?shù)據(jù)庫(kù)中新建好openfire的DB);
用戶名與密碼請(qǐng)?zhí)顚懻_的MySQL用戶名與密碼;剩下的如果沒(méi)有特殊要求可用默認(rèn)值;然后在特性設(shè)置中,選用初始設(shè)置即可;
在設(shè)置管理員賬戶中,請(qǐng)?zhí)顚懞孟鄳?yīng)的密碼并牢記,該密碼即為初始admin的密碼;
設(shè)置完成后,點(diǎn)繼續(xù)會(huì)提示安裝完成.
選擇登陸到管理控制臺(tái),賬號(hào)為admin,密碼即為上面設(shè)置的管理員密碼。
登錄控制臺(tái),上傳并安裝在openfire官方網(wǎng)站下載的Presence Service插件presence.jar:
切換到服務(wù)器設(shè)置,對(duì)presenceservice插件進(jìn)行設(shè)置,確??稍试S任何人訪問(wèn)(如果是跨域?yàn)g覽的話):記得要點(diǎn)擊save button!
然后重啟openfire。每次安裝一個(gè)插件,最好都重啟以使插件及其設(shè)置生效。
在重啟之前,可以為openfire 設(shè)定環(huán)境變量 openfireHome 為openfire的安裝目錄:
同樣安裝下載到的broadcast插件:
可使openfire 具備對(duì)group或?qū)λ杏脩魪V播message的功能。
安裝broadcast插件之后,需在openfire 管理控制臺(tái)的 服務(wù)器->服務(wù)器管理器->系統(tǒng)屬性總增加以下屬性設(shè)置(紅色為必須設(shè)定,其他可選):
Property Key 屬性值
plugin.broadcast.serviceName broadcast
plugin.broadcast.disableGroupPermissions true
plugin.broadcast.groupMembersAllowed true.
plugin.broadcast.allowedUsers the comma-delimitted list of users allowed to broadcast messages to all connected users at once. When this property isn't set, anyone is allowed to broadcast messages to all users. Users should be specified by their bare JID (e.g. john@myserver.com)
plugin.broadcast.all2offline true.
plugin.broadcast.messagePrefix (broadcast).
拖動(dòng)右邊滾動(dòng)條到最下面,依次添加以上屬性:
點(diǎn)擊保存,確保屬性保存成功:
也可以到網(wǎng)上搜索下載或自行開發(fā)更多的插件,對(duì)openfire的功能進(jìn)行擴(kuò)展。
Openfire Server的admin功能也是通過(guò)插件方式提供的,可以從Openfire啟動(dòng)后在任務(wù)欄的圖標(biāo)
點(diǎn)擊launchAdmin
或者直接輸入openfire的安裝網(wǎng)址和設(shè)定的訪問(wèn)端口(缺省為9090)來(lái)登錄:
例如:http//10.139.8.167:9090
輸入用戶名和密碼,即可登錄到Openfire的管理控制臺(tái):
FHWTVBox管理主要用到的用戶群組管理功能有:
上述四項(xiàng)功能,已經(jīng)完全在FHWTVBox Manager中實(shí)現(xiàn),具體操作見(jiàn)后面FHWTVBoxManager的介紹。
本項(xiàng)目的需求是要求在控制TVBox上的android系統(tǒng)啟動(dòng)后,自動(dòng)在連接的TV上播放指定的網(wǎng)站視頻內(nèi)容。由于管理上的原因,不希望端用戶改變?cè)L問(wèn)的網(wǎng)站,所以此功能不能使用android 原生的browser來(lái)實(shí)現(xiàn),必須自己開發(fā)一個(gè)開機(jī)自啟動(dòng)的應(yīng)用,完成視頻播放;然后,再開發(fā)一個(gè)服務(wù)器端的控制端,實(shí)現(xiàn)對(duì)TVBox Client端的播放內(nèi)容的控制。在本項(xiàng)目中,我們使用Openfire push message的機(jī)制實(shí)現(xiàn)對(duì)TVBox的服務(wù)器端控制。
在android中建立一個(gè)自啟動(dòng)任務(wù),是很簡(jiǎn)單的。首先,在AndroidManifest.xml文件中添加接收boot complete系統(tǒng)廣播消息的權(quán)限以及聲明一個(gè)receiver:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<receiver android:name=".BootReceiver">
<intent-filter> <!-- 系統(tǒng)啟動(dòng)完成后會(huì)調(diào)用-->
<action android:name="android.intent.action.BOOT_COMPLETED"></action>
</intent-filter>
</receiver>
然后在工程文件中,建立那個(gè)receiver的Class文件:
這個(gè)receiver的任務(wù)很簡(jiǎn)單,就是接受到系統(tǒng)啟動(dòng)完畢的廣播事件后,啟動(dòng)我們的應(yīng)用activity。在這里就是TVActivity。
唯一值得注意的是,在android 4.0里面, 已經(jīng)禁止了未經(jīng)用戶同意的自啟動(dòng)的應(yīng)用。所以,一個(gè)自啟動(dòng)應(yīng)用要能正常自啟動(dòng),必須至少手工啟動(dòng)一次。這個(gè)TVBox應(yīng)用需要設(shè)定一些數(shù)據(jù),所以,在安裝完畢之后,需要手工啟動(dòng),完成設(shè)定之后才會(huì)真正成為自啟動(dòng)應(yīng)用。
利用WebView播放視頻,最主要的是設(shè)定它的兩個(gè)client WebViewClient:webView.setWebViewClient(new WebViewClient() {
public boolean shouldOverrideUrlLoading(
android.webkit.WebView view, java.lang.String url) {
view.loadUrl(url);
return true;
}
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
// view.loadUrl("javascript: var v=document.getElementsByTagName('video')[0]; "+"v.play(); ");
}
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
}
});
和WebChromeClient:
webView.setWebChromeClient(new WebChromeClient() {
/**
* 顯示自定義視圖,無(wú)此方法視頻不能播放
*/
private View myView = null;
private CustomViewCallback myCallback = null;
@Override
public void onShowCustomView(View view, CustomViewCallback callback) {
if (myCallback != null) {
myCallback.onCustomViewHidden();
myCallback = null;
return;
}
long id = Thread.currentThread().getId();
Log.v("WidgetChromeClient", "rong debug in showCustomView Ex: "
+ id);
ViewGroup parent = (ViewGroup) webView.getParent();
String s = parent.getClass().getName();
Log.v("WidgetChromeClient", "rong debug Ex: " + s);
parent.removeView(webView);
parent.addView(view);
myView = view;
myCallback = callback;
chromeClient = this;
isVideoPlaying = true;
}
public void onHideCustomView() {
long id = Thread.currentThread().getId();
Log.v("WidgetChromeClient", "rong debug in hideCustom Ex: "
+ id);
if (myView != null) {
if (myCallback != null) {
myCallback.onCustomViewHidden();
myCallback = null;
}
ViewGroup parent = (ViewGroup) myView.getParent();
parent.removeView(myView);
parent.addView(webView);
// added by Dumbbell Yang at 2012-12-04
webView.requestFocus();
myView = null;
}
isVideoPlaying = false;
}
});
還有其他一些WebView的設(shè)定需要調(diào)整。加載的網(wǎng)頁(yè)內(nèi)容有時(shí)候也需要調(diào)整。當(dāng)然,不能忘記需要在AndroidManifest.xml文件中添加訪問(wèn)網(wǎng)絡(luò)的權(quán)限:
<uses-permission android:name="android.permission.INTERNET"/>
TVBox client端要能接收到Openfire Server端push過(guò)來(lái)的消息,必須能夠登錄Openfire Server并建立一個(gè)http 的長(zhǎng)連接。為此,需要設(shè)定openfire server的IP及端口數(shù)據(jù)。另外,由于用戶提出還要對(duì)TVBox進(jìn)行群組管理,所以,還必須能夠獲取Openfire server上定義的用戶群組的列表,供client端設(shè)定時(shí)選擇。界面如下:
需要注意的是,這里的UserID和password,是抓取Openfire server用戶群組列表的賬號(hào),這個(gè)賬號(hào)必須具有管理員權(quán)限。這個(gè)設(shè)定及抓取動(dòng)作只在安裝完成后執(zhí)行一次。真正Client每次啟動(dòng)后,連接Openfire server的賬戶,是用設(shè)定的TVBox名稱作為前綴,加下劃線加TVBox的device ID作為賬號(hào),IP Addressz作為名詞,固定密碼自動(dòng)注冊(cè),然后自動(dòng)登錄的,由于需要獲取Client端的IP Address,所以還需要在AndroidManifest.xml文件中添加權(quán)限:
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
利用設(shè)定的管理員賬號(hào)和密碼,登錄Openfire,是通過(guò)WebView的load url功能實(shí)現(xiàn)的:
webView.loadDataWithBaseURL(null, util.getOpenfireAutoHTML("http://" + strServer + ":9090", strUserName, strPassword), "text/html", "utf-8", null);
其中g(shù)etOpenfireAutoHTML 方法根據(jù)Openfire Server IP,賬號(hào)和密碼構(gòu)造一個(gè)自動(dòng)登錄的HTML頁(yè)面,然后在WebView中加載,完成登錄Openfire的功能。具體實(shí)現(xiàn)方法,可見(jiàn)代碼。
然后,在WebView中實(shí)現(xiàn)JavaScript與WebView內(nèi)容的交互。
首先定義JavaScript interface:
/* An instance of this class will be registered as a JavaScript interface */
class MyJavaScriptInterface {
@SuppressWarnings("unused")
public void loadGroup(String html) {
//System.out.println(html);
//System.out.println("Extract Groups:");
List<Link> groups = util.extractOpenfireGroups(html);
if (groups.size() > 0){
notGetGroups = false;
String[] arrGroup = new String[groups.size()];
int i = 0;
for(Link group:groups){
Log.i(TAG, "Group:" + group.getText() + "," + group.getRef());
arrGroup[i++] = group.getText();
}
cmbGroups.setSuggestionData(arrGroup);
showGroupMessage(arrGroup.length);
}
}
@SuppressWarnings("unused")
public void add2Group(String html) {
//System.out.println(html);
if (html.indexOf("success") != -1){
notAdd2Group = false;
System.out.println("add to Group Success!");
}
}
//added by Dumbbell Yang at 2013-02-28
//取得用戶所在group的admin賬號(hào),和他建立chat
@SuppressWarnings("unused")
public void getGroupAdmin(String html) {
//System.out.println("Get Group Admin:" + html);
TVBox.adminUsers = util.extractHTMLPageAllAdminUsers(html);
if (TVBox.adminUsers.size() > 0){
notGetGroupAdmin = false;
System.out.println("got Group Administrator!");
showGroupAdminMessage(TVBox.adminUsers.size());
}
else{
System.out.println("Not got Group Administrator!");
}
}
}
然后,在WebView的WebViewClient中,增加方法:
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view,url);
/* This call inject JavaScript into the page which just finished loading. */
//從加載的session-summary.jsp頁(yè)面的內(nèi)容中提取Openfire用戶
if (url.endsWith("group-summary.jsp") && notGetGroups){
webView.loadUrl("javascript:window.HTMLOUT.loadGroup('<head>'+document.getElementsByTagName('html')[0].innerHTML+'</head>');");
}
//added by Dumbbell Yang at 2013-02-28
else if (url.indexOf("group-edit.jsp") != -1 && notGetGroupAdmin){
webView.loadUrl("javascript:window.HTMLOUT.getGroupAdmin('<head>'+document.getElementsByTagName('html')[0].innerHTML+'</head>');");
}
else if (url.indexOf("group-edit.jsp") != -1 && notAdd2Group){
webView.loadUrl("javascript:window.HTMLOUT.add2Group('<head>'+document.URL+'</head>');");
}
}
在WebView中的頁(yè)面加載完成后,依次執(zhí)行l(wèi)oadGroup 獲取群組列表;getGroupAdmin,獲取群組所有的管理員(因?yàn)橛脩粢罂梢杂卸鄠€(gè)管理員同時(shí)來(lái)控制Client端,所以,每個(gè)group的管理員賬號(hào)可能有多個(gè));add2Group 把自動(dòng)注冊(cè)的client用戶加入群組。
上面三項(xiàng)功能都是通過(guò)解析WebView加載完成后的HTML page內(nèi)容的方式完成的。具體實(shí)現(xiàn)方法,可見(jiàn)代碼。當(dāng)獲取用戶群組數(shù)據(jù)后,效果圖如下
在這里選定一個(gè)群組,設(shè)定TVBox名稱前綴及缺省打開的網(wǎng)站URL,單擊保存,完成設(shè)定。
第一次啟動(dòng)TVBox Activity,會(huì)進(jìn)入2.2的Settings Dialog,完成設(shè)定后,進(jìn)入TVBox Activity,創(chuàng)建XMPP connection,自動(dòng)注冊(cè)openfire賬號(hào),加入設(shè)定的群組,自動(dòng)登錄,并和群組的管理員建立chat,并對(duì)chat設(shè)定message listener,監(jiān)聽(tīng)管理員發(fā)來(lái)的信息,達(dá)到Server端對(duì)Client的控制。
自動(dòng)注冊(cè)登錄的流程大致如下代碼所示:
// added by Dumbbell Yang at 2012-11-20
new Thread(new Runnable() {
@Override
public void run() {
if (createXMPPConnection()) {
String username = getTVBoxName();
if (autoRegister(username, XMPPPassword)) {
if (autoLoginToOpenfire(username, XMPPPassword)) {
addChatListener();
}
}
}
}
}).start();
具體實(shí)現(xiàn)可見(jiàn)代碼。要注意的是,在android 4.0中,已不允許同步訪問(wèn)網(wǎng)絡(luò);另外,由于android的線程現(xiàn)在,所以,這些注冊(cè)登錄動(dòng)作最好都另起線程來(lái)做。最后的結(jié)果,需要更新界面的話,如下使用給handler發(fā)消息的方式:
browseHandler = new Handler() {
// @Override
public void handleMessage(android.os.Message msg) {
// TODO Auto-generated method stub
// browseURL();
if (msg.what == LOADING) {
browseWebView();
}
else if (msg.what == REFRESH) {
Message message = (Message)msg.obj;
refreshWebView(message.getBody(),message.getFrom());
//refreshWebView(msg.obj.toString());
}
}
};
browseHandler.sendEmptyMessageDelayed(LOADING, 5000);
登錄成功后,加入群組,是采用在settings dialog中加載相應(yīng)web 頁(yè)面,并同過(guò)javascript interface,解析WebView的HTML內(nèi)容方式完成的:
// add current user into group
String strGroup = prefs.getString(SettingsDialog.GROUP,FHWTVBOX_GROUP);
String strServer = prefs.getString(SettingsDialog.SERVER,XMPPHost);
if (isAdd2Group == false) {
mDialog.addUserToGroup(strServer, xmppConn.getUser(),strGroup);
}
獲取群組的admin 賬號(hào)列表:
mDialog.getGroupAdminUserList(strServer,
prefs.getString(SettingsDialog.USERNAME, FHW_ADMIN),
prefs.getString(SettingsDialog.PASSWORD, FHW_ADMIN_PASSWORD), strGroup);
然后和dmin及群組的所有admin賬號(hào)建立chat并監(jiān)聽(tīng)chat message:
createChatListener(cm, FHW_ADMIN + "@" + xmppConn.getServiceName());
// added by Dumbbell Yang at 2013-02-28 和Group里的每個(gè)admin都建立聊天
if ((this.adminUsers != null) && (this.adminUsers.size() > 0)) {
for (String adminUser : this.adminUsers) {
createChatListener(cm,adminUser + "@" + xmppConn.getServiceName());
}
}
下面是createChatListener的代碼:
private void createChatListener(ChatManager cm, String strChatUser) {
cm.createChat(strChatUser, null);
cm.addChatListener(new ChatManagerListener() {
@Override
public void chatCreated(Chat chat, boolean able) {
chat.addMessageListener(new MessageListener() {
@Override
public void processMessage(Chat chat, Message message) {
String strMessage = message.getBody();
Log.i(TAG, "received message:" + strMessage + " from "
+ message.getFrom());
if (strMessage != null) {
android.os.Message msg = browseHandler
.obtainMessage();
msg.what = REFRESH;
msg.obj = message;//strMessage;
msg.sendToTarget();
}
}
});
}
});
}
接收到消息時(shí),會(huì)調(diào)用前面handler里面refreshview的方法,切換WebView的內(nèi)容到消息指定的網(wǎng)頁(yè)。
至此,client端的開發(fā)就完成了。
要對(duì)Client端的TVBox進(jìn)行控制,還必須得有Server端的程序。FHWTVBox Manager是一個(gè)桌面的Java Application,實(shí)現(xiàn)了對(duì)TVBox的控制功能,安裝了jre就可以運(yùn)行,最新程序可以從本機(jī)openfire的安裝網(wǎng)站下載,路徑通常為:
http//10.139.8.167:9090/FHWTVBoxManager.jar
在瀏覽器中輸入上面的地址,下載FHWTVBox Manager到本地,雙擊打開,界面如下:
最上面是openfire server的各項(xiàng)設(shè)定,輸入openfire各項(xiàng)設(shè)定及管理員用戶名和密碼,后單擊登錄按鈕,就會(huì)登錄到openfire server,把用戶及群組信息抓取出來(lái):
中間有群組管理和機(jī)頂盒管理兩個(gè)Tab:
群組管理中,列表顯示openfire上所有的用戶(也就是FHWTVBox),綠色為online,灰色為offline。選擇群組下拉框顯示里openfire server 上目前所有的用戶群組,最低部的FHWTV 頻道下拉框顯示了從指定網(wǎng)站抓取的所有電視頻道,可用于發(fā)送切換消息。
可以使用新增,更新和刪除按鈕,管理openfire server上的用戶群組。
可以使用加入和移除按鈕,將選定的用戶加入指定的群組或從其中移除。
也可以在此刪除選中的TVBox。
機(jī)頂盒管理界面如下:
er的數(shù)據(jù)。
在開發(fā)中主要用到了JTree和JList兩種組件,用來(lái)顯示TVBox的群組樹狀結(jié)構(gòu)及列表結(jié)構(gòu)。其數(shù)據(jù)Adapter是參考網(wǎng)上的代碼修改而成,比較簡(jiǎn)單,在此不再贅述。
生成可執(zhí)行jar的方法網(wǎng)上也有很多數(shù)據(jù)可供參考,也一并略過(guò)。
此處利用的是apache 開源的HttpClient 3.0.1的版本,最新的4.0的版本改動(dòng)比較大,需要注意。
首先是用戶點(diǎn)擊login之后,createXMPPConnection成功,并調(diào)用autoLoginToOpenfire 登錄一個(gè)管理員賬號(hào)到Openfire,做好對(duì)client端發(fā)消息的準(zhǔn)備,并建立一個(gè)PacketListner,接收Client對(duì)push的回饋:
private void addMessageListener(){
System.out.println("addMessageListener");
//添加chat message listener
PacketFilter filter = new MessageTypeFilter(Message.Type.chat);
xmppConn.addPacketListener(new PacketListener() {
public void processPacket(Packet packet) {
Message message = (Message) packet;
System.out.println("Got message");
具體實(shí)現(xiàn)請(qǐng)參見(jiàn)詳細(xì)代碼。
然后調(diào)用fillTVBoxes方法,使用httpClient,去抓取openfire的用戶信息,群組信息,在線用戶信息等,填充JList和JTree及群組combox。以后每次openfire群組及用戶數(shù)據(jù)有改變, 都會(huì)重新調(diào)用fillTVBoxes方法,刷新顯示數(shù)據(jù)。
在fillTVBox中,使用httpClient抓取數(shù)據(jù),示例如下:
首先判斷是否已經(jīng)有session:
//如果已有cookie,直接GET
if (sessionID.equals("") == false){
return getURLContent(client, strServerPort + "/" + strURL);
}
//否則
//使用POST方法
PostMethod postMethod = new PostMethod(strServerPort + "/login.jsp"){
@Override
public boolean getFollowRedirects(){
return true;
}
};
然后,構(gòu)造需要提交的數(shù)據(jù):
/**
* 使用POST方式提交數(shù)據(jù)
*/
NameValuePair[] loginInfo = {
new NameValuePair("url",strServerPort + "/" + strURL),
new NameValuePair("login","true"),
new NameValuePair("username",strUserName),
new NameValuePair("password",strPassword),};
postMethod.setRequestBody(loginInfo);
提交數(shù)據(jù)后,處理返回值并解析:
String info = null;
try {
client.executeMethod(postMethod);
if (sessionID.equals("")){
Cookie[] cookies = client.getState().getCookies();
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies[i];
if (cookie.getName().trim().equalsIgnoreCase("JSESSIONID")){
sessionID = cookie.getValue().trim();
}
}
}
int code = postMethod.getStatusCode();
//System.out.println("Status:" + code);
if (code == HttpStatus.SC_OK){
info = new String(postMethod.getResponseBodyAsString());
}
如果調(diào)用返回的數(shù)據(jù)不為空,則調(diào)用不同的方法解析出所需要的值,可能會(huì)用到正則表達(dá)式,例如:
extractFHWTVChannels 解析出指定的URL web 頁(yè)面上能夠播放的TV頻道的URL;
extractOpenfireOnlineUsers解析出所有online的openfire user lis;
extractOpenfireUsersInGroup解析出某一個(gè)openfire group所包含的user list。
這些都是通過(guò)構(gòu)造不同的post 數(shù)據(jù),利用httpClient提交到相應(yīng)的頁(yè)面,然后解析返回的HTML內(nèi)容的方式完成的。
對(duì)用戶群組數(shù)據(jù)的操作同樣也是利用httpClient,提交不同的post數(shù)據(jù)到指定頁(yè)面來(lái)完成的。例如增加用戶到群組:
public static String addOpenfireUserToGroup(HttpClient client,
String strServerPort, String strUserName, String strGroupName){
//使用POST方法
PostMethod postMethod = new PostMethod(strServerPort + "/group-edit.jsp?group=" + strGroupName);//{
// @Override
// public boolean getFollowRedirects(){
// return true;
// }
// };
/**
* 使用POST方式提交數(shù)據(jù)
*/
NameValuePair[] postInfo = {
new NameValuePair("group", strGroupName),
new NameValuePair("add", "Add"),
new NameValuePair("username", strUserName),
new NameValuePair("addbutton", "添加"),};
postMethod.setRequestBody(postInfo);
String info = null;
try {
client.executeMethod(postMethod);
int code = postMethod.getStatusCode();
//添加成功,會(huì)跳轉(zhuǎn),添加失敗,則不跳轉(zhuǎn)
if (postMethod.getResponseHeader("location") == null){
return "Faield!";
}
String strRedirectURL = postMethod.getResponseHeader("location").getValue();
if (code == HttpStatus.SC_OK){
info = new String(postMethod.getResponseBodyAsString());
}
else if (strRedirectURL.endsWith("/group-edit.jsp?group=" +
strGroupName + "&success=true")){
return "success";
}
}
catch (HttpException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally {
postMethod.releaseConnection();
}
return info;
}
其他刪除群組用戶,增加群組,修改群組,刪除群組代碼邏輯完全類似。
FHWTVBox Client端是一個(gè)運(yùn)行在android 4.0.3平臺(tái)上的apk,可以從以下網(wǎng)址下載安裝:。
http://10.139.8.167:9090/FHWTVBox.apk
安裝完畢后,單擊打開或者重新啟動(dòng),程序界面如下,首先出現(xiàn)設(shè)定界面(此為仿真器所截取的豎屏,在TVBox上為橫屏,內(nèi)容一致):
輸入openfire server的ip,端口,service及管理員的用戶名和密碼,系統(tǒng)會(huì)去抓取openfire、 server上已建立的用戶群組,顯示在下面的下拉框中,供用戶選擇:
選擇一個(gè)當(dāng)前TVBox要加入的群組,輸入TVBox的名稱前綴(TVBox命名為前綴_設(shè)備ID),以及TVBox啟動(dòng)后打開的缺省網(wǎng)站地址:
單擊保存,就會(huì)自動(dòng)注冊(cè)用戶到openfire server,加入到選擇的群組,并進(jìn)入預(yù)設(shè)的網(wǎng)站:
此時(shí),打開FHWTVBox Manager,登錄成功后,可以看到有三個(gè)用戶在線:(admin,一個(gè)測(cè)試TVBox,一個(gè)仿真器):
此時(shí)可以在列表中選中相應(yīng)的TVBox,進(jìn)行刪除。
刪除完成,系統(tǒng)會(huì)給出提示:
點(diǎn)擊確定,可看到剛才選擇的TVBox已經(jīng)不見(jiàn)了:
切換到機(jī)頂盒管理Tab,選擇仿真器及測(cè)試TVBox,選擇湖南衛(wèi)視,發(fā)送切換消息:
在Manager上會(huì)得到頻道切換成功的消息:
點(diǎn)擊確定后,展開群組,可以看到頻道已經(jīng)切換:
由于仿真器缺少播放視頻所需的硬件支持,所以雖然頻道已經(jīng)切換,但內(nèi)容無(wú)法播放。
頻道切換的實(shí)際效果在測(cè)試TVBox上可以看到。
前面開發(fā)的FHWTVBoxManager是一個(gè)PC端的TVBox的管理工具,必須在計(jì)算機(jī)上完成。后來(lái)用戶提出要把管理功能在手機(jī)實(shí)現(xiàn),于是,就有了FHWTVBox Admin。這是一個(gè)可以運(yùn)行在android設(shè)備(android智能手機(jī)或TVBox機(jī)頂盒)上的FHWTVBox的管理工具。安裝路徑與FHWTVBox android client端一樣,只是apk名稱不同:
http://10.139.8.167:9090/FHWTVBoxAdmin.apk
安裝完成后,界面如下所示(手機(jī)和仿真器上是豎屏,機(jī)頂盒上是橫屏,但內(nèi)容一致):
和FHWTVBoxManager一樣,也是分了三個(gè)Tab,服務(wù)器設(shè)定,群組管理和機(jī)頂盒管理。功能也和HWTVBoxManager類似。在服務(wù)器設(shè)定tab輸入openfire 服務(wù)器相關(guān)設(shè)定及登錄用戶名密碼后,點(diǎn)擊登錄,會(huì)登錄到openfire server上,抓取所有群組,機(jī)頂盒用戶及在線機(jī)頂盒信息,填充在群組管理和機(jī)頂盒管理的列表中:
在這個(gè)界面上,可以和FHWTVBoxManager一樣,進(jìn)行群組增刪改,把機(jī)頂盒用戶加入群組或從群組中移除,以及刪除機(jī)頂盒用戶等操作。操作完成后,系統(tǒng)會(huì)給出提示信息并刷新顯示數(shù)據(jù)。
可以在機(jī)頂盒管理 Tab進(jìn)行機(jī)頂盒頻道切換的消息發(fā)送操作:
和FHWTVBoxManager同樣操作,先選中要切換頻道或發(fā)送信息的機(jī)頂盒,然后在頻道下拉選框中選擇頻道,然后頻道的網(wǎng)址就會(huì)顯示在推送消息的輸入框中(如果要發(fā)送消息,可以在此直接輸入),點(diǎn)擊發(fā)送按鈕,就會(huì)把相應(yīng)的內(nèi)容推送到選中的機(jī)頂盒中。機(jī)頂盒成功接收消息或切換頻道后,會(huì)回送一個(gè)消息通知,顯示在FHWTVBoxAdmin的狀態(tài)區(qū):(手機(jī)及仿真器在頂部,F(xiàn)HWTVBox機(jī)頂盒在底部)
點(diǎn)擊狀態(tài)區(qū)的消息通知,可以查看消息的內(nèi)容。點(diǎn)擊之后,消息通知就自動(dòng)關(guān)掉了。
FHWTVBox admin所用到的開發(fā)技術(shù),android的界面顯示有ExpandableListView和checkable的listView,都是很簡(jiǎn)單的,其余獲取及操作Openfire的數(shù)據(jù),和PC上的FHWTVBoxManager一樣,是通過(guò)httpClient進(jìn)行的,代碼幾乎都完全一樣,在此不再贅述。
本來(lái)到這里項(xiàng)目開發(fā)已經(jīng)可以結(jié)束了,但可惜用戶的需求是永無(wú)止境的。Android手機(jī)上可以管理TVBox了,那么iPad上呢?iPhone上呢?這一來(lái),我就不得不考慮采用plugin JSP的方式來(lái)開發(fā)這個(gè)TVBox的管理平臺(tái)了。雖然這樣界面體驗(yàn)在移動(dòng)端不是很好,但是作為管理應(yīng)用,功能實(shí)現(xiàn)才是首要目標(biāo)。所以才有了TVBox plugin。
TVBox plugin是一個(gè)可以運(yùn)行在openfire Server上的插件,用JSP的形式實(shí)現(xiàn)了FHWTVBoxManager的全部功能:TVBox組添加修改刪除,TVBox加入組或從組中移除,TVBox刪除,TVBox消息推送以及TVBox回復(fù)消息顯示及TVBox消息推送歷史記錄。這個(gè)插件JSP可以從PC瀏覽器或android平臺(tái)瀏覽器或IOS平臺(tái)的瀏覽器訪問(wèn),實(shí)現(xiàn)了移動(dòng)Web形式的通用TVBox管理平臺(tái),可以使用電腦或android平板,anroid手機(jī),機(jī)頂盒或iPhone,iPad等等任一支持http瀏覽的終端上訪問(wèn)。
插件部署步驟:
1. 復(fù)制tvbox.jar插件安裝包到openfire的安裝目錄下的plugins目錄。
重新啟動(dòng)openfire,會(huì)看到如下包含hello,TVBox Plugin!的提示:
在openfire的安裝目錄下的plugins的tvbox目錄下的Web文件夾中,發(fā)現(xiàn)tvbox.htm:
2. 到openfire的安裝目錄下的plugins的admin目錄的webapp下。
登錄openfire服務(wù)器后,在插件tab中能看到如下FHWTVBox Plugin的條目:
要訪問(wèn)插件jsp,可以在瀏覽器中輸入http://10.139.8.167:9090,登錄openfire后,點(diǎn)擊用戶/組tab,選擇富鴻網(wǎng)機(jī)頂盒項(xiàng)目;也可以在瀏覽器中直接輸入http://10.139.8.167:9090/tvbox.htm:
登錄后直接轉(zhuǎn)到用戶/組tab的富鴻網(wǎng)機(jī)頂盒項(xiàng)目的消息推送頁(yè)面:
其中tvboxclient_c22b13562afa5c05是測(cè)試用機(jī)頂盒,test是電腦上登錄spark端用戶,綠色圖標(biāo)表示在線,灰色圖標(biāo)表示離線。
在這里,可以選擇頻道:
然后選中要推送消息的機(jī)頂盒:
單擊發(fā)送后,系統(tǒng)就會(huì)推送消息到選擇的機(jī)頂盒,切換機(jī)頂盒播放頻道:
并將頁(yè)面自動(dòng)轉(zhuǎn)到查看機(jī)頂盒回復(fù)的頁(yè)面:
而PC上的Spark登錄用戶,則會(huì)收到一條消息通知:
在消息歷史頁(yè)面,可以查詢某段時(shí)間內(nèi)推送到某些機(jī)頂盒上的消息:
輸入機(jī)頂盒部分或全部ID,選擇起止時(shí)間:
點(diǎn)擊搜索,可看到如下搜索結(jié)果,包括消息推送時(shí)保存在數(shù)據(jù)庫(kù)的所有欄位:
而在機(jī)頂盒管理頁(yè)面,可以和FHWTVBoxManager中一樣,完成TVBox群組的增加修改和刪除,完成機(jī)頂盒加入群組,從群組移除及刪除機(jī)頂盒功能,界面如下所示:
機(jī)頂盒管理的功能和操作步驟和FHWTVBoxManager及FHWTVBox Admin都是基本一致的,同樣可以把TVBox加入群組,從群組中移除,對(duì)群組增刪改查及刪除TVBox:
上圖同樣顯示的是openfire server是定義的群組。
要進(jìn)行Openfire plugin的開發(fā),首先必須下載openfire 的源碼,并進(jìn)行部署編譯,然后安裝官方的文檔,增加自己的plugin的配置文件及代碼,最后編譯成jar檔,放在openfire的安裝目錄下的plugins目錄。
可以從http://www.igniterealtime.org/downloads/source.jsp下載打包的openfire 的最新source code ,解壓到本地目錄,例如:F:\Openfire Software\openfire_src_3_7_1;
或者使用svn http://svn.igniterealtime.org/svn/repos/openfire/trunk checkout整個(gè)openfire 項(xiàng)目到同樣的本地目錄。
然后在Eclipse中,新建Java Project,根據(jù)Eclipse版本不同,操作有點(diǎn)差別。
如Eclpse Europa 3.3.2版早期版本,如下圖勾選' create project from existing source' 瀏覽到解壓的openfire 源碼目錄下的'openfire_src' ,完成創(chuàng)建(網(wǎng)上很多openfire源碼部署都是這樣寫的,可是我在我的Eclpse上怎么也找不到那個(gè)' create project from existing source'的勾選框,后來(lái)才知道是Eclipse的版本升級(jí)啦):
而對(duì)于我正在使用的Eclipse indigo 3.7.0版,則界面如下:不要選擇use default location,然后點(diǎn)擊瀏覽,選擇Openfire source code本地目錄,完成工程創(chuàng)建。
然后右鍵項(xiàng)目 --> BuildPath -->Configure BuildPath-->library --add jars 把openfire下所有的lib 及其子目錄中的jar包都添加到進(jìn)來(lái),把插件中的lib目錄下的jar 包添加進(jìn)來(lái)。
接下來(lái)配置運(yùn)行參數(shù),Run::Open Run Dialog... menu.
或者 Run -- Run configuration
選擇Java Application 右鍵新建一個(gè)Java application 重新命名為openfire
選擇剛才建的項(xiàng)目openfire371
設(shè)定Main class:(search) org.jivesoftware.openfire.starter.ServerStarter
單擊 Arguments 選框
在 VM-Arguments 鍵入 -DopenfireHome="${workspace_loc:openfire}/target/openfire"
此處實(shí)際上是告訴 openfire ,openfireHome 在什么地方,用于eclipse執(zhí)行
java命令時(shí)傳遞的參數(shù),openfire程序可以通過(guò)System.getProperty("openfireHome")得到 openfire的本地位置。
點(diǎn)擊classpath 選項(xiàng) User entries --->Advanced-->Add Folder---> OK添加以下三目錄
Openfire371::src::i18n
Openfire371::src::resources::jar
Openfire371::build::lib::dist
點(diǎn)擊Common tab
勾選 Debug和Run復(fù)選框
現(xiàn)在可以使用源碼中自帶的ant腳本,對(duì)Openfire的source code進(jìn)行編譯了。
選擇openfire371 project, 選擇Eclipse的Tools菜單window –> Show View -> Ant,
就會(huì)在右邊ant頁(yè)面列出所有openfire中預(yù)定義的編譯任務(wù):
在ant view里面雙擊openfire(default),開始編譯,編譯成功,界面如下:
如果編譯不成功,請(qǐng)檢查相關(guān)配置及jar文件,確保編譯成功,才可以進(jìn)行下一步plugin的開發(fā)。
根據(jù)openfire官方的plugin develop guide:
http://www.igniterealtime.org/builds/openfire/docs/latest/documentation/plugin-dev-guide.html
openfire plugin 的代碼結(jié)構(gòu)如下:
其中,plugin.xml是必須的plugin的定義文件,其中定義了plugin的配置信息以及包含的頁(yè)面信息。如下圖是TVBox plugin的定義文件:
其中class定義了plugin的包含包名在內(nèi)的主類(似乎必須是定義在org.jivesoftware.plugin 包內(nèi));
databaseKey定義了plugin要執(zhí)行的數(shù)據(jù)庫(kù)SQL腳本文件,如果在plugin中需要建立table的話,就要準(zhǔn)備這份文件,按數(shù)據(jù)庫(kù)類型命名放在如下的目錄:
然后下面的<adminconsole>部分指定了plugin從openfire 服務(wù)器控制面板訪問(wèn)時(shí)的路徑。<tab id="tab-users">表示放在”用戶/組”的tab下:
如果是<tab id="tab-server">則表示放在”服務(wù)器” 的tab下。要放入其他tab,相應(yīng)的id可以在openfire源碼自帶的plugin的定義文件中查找。
<sidebar id="sidebar-tvboxes" name="${users.sidebar.tvboxes.name}" 表示plugin在”用
戶/組” tab下子tab的id和名字,其中${users.sidebar.tvboxes.name}是采用多語(yǔ)言資源文件的方式,取資源文件中key users.sidebar.tvboxes.name的相應(yīng)值:
中文的資源文件如下圖,文字都被變成了unicode編碼。開始我還以為要去一個(gè)字一個(gè)字查找其unicode編碼,后來(lái)才發(fā)現(xiàn)在英文資源文件(編碼設(shè)定為ISO-8859-1)中,直接輸入中文,就會(huì)自動(dòng)轉(zhuǎn)換為unicode編碼,然后直接copy到中文資源文件(編碼UTF-8)就可以了。
<sidebar id="sidebar-tvboxes" 下面的每一個(gè)item,都定義了一個(gè)jsp頁(yè)面,這里才是plugin的核心內(nèi)容。
例如發(fā)送消息頁(yè)面定義如下:
<item id="push-message" name="${tvbox.item.pushmessage.name}"
url="push_message.jsp" description="${tvbox.item.pushmessage.description}" />
id是頁(yè)面的標(biāo)識(shí),url是頁(yè)面的名稱,name和description是資源文件里面的key。
這里定義有多少個(gè)item,這個(gè)plugin里面就包含有多少個(gè)jsp頁(yè)面。
Plugin里面的jsp等web資源,被放在在web目錄中:
其中,WEB-INF下的web-custom.xml,是用來(lái)定義plugin中用到的Servlet的。在TVBox插件中并沒(méi)有用到。
web目錄下的其他活頁(yè)夾,images,scripts和style和一般的web應(yīng)用是一樣的,用來(lái)存放圖片,javascript腳本以及css文件。
到這里,已經(jīng)基本上把一個(gè)插件源代碼的主要結(jié)構(gòu)講解完畢了,現(xiàn)在總結(jié)一下:
readme.xml是plugin的自述說(shuō)明文件
changelog.html是plugin的發(fā)布日志文件。
logo_large.png和logo_small.png是plugin顯示在openfire 插件列表中的圖標(biāo)文件。
plugin.xml是plugin的定義文件,前面已經(jīng)詳細(xì)講過(guò)。
database目錄是用來(lái)存放plugin中用到的數(shù)據(jù)庫(kù)創(chuàng)建table的sql腳本文件。
I18n目錄是用來(lái)存放plugin中用到的多語(yǔ)言資源文件。
lib目錄是用來(lái)存放plugin中引用的的第三方library。
web 目錄是用來(lái)存放plugin中用到的jsp頁(yè)面,servlet定義及其他web資源。
Java目錄中存放的是plugin中用到的所有java code。(openfire source code中的plugin源碼中都是java,但是在官方的plugin結(jié)構(gòu)中改為了classes,可能以后會(huì)有變化)。
下面我們來(lái)看如何進(jìn)行Openfire plugin Class的開發(fā)。一共包含如下8個(gè)Class:
public class TVBoxPlugin implements Plugin
public class EchoInterceptor implements PacketInterceptor
public class EchoMessage extends Message
public class LinkItem
public class ServletUtil
public class TVBoxBean
public class TVBoxMessage
public class TVBoxServlet extends HttpServlet
首先是創(chuàng)建在plugin.xml文件中定義的plug的class并實(shí)現(xiàn)plugin接口,保持包名和類名一致。在這里就是org.jivesoftware.openfire.plugin.tvbox.TVBoxPlugin,如下圖所示:
其實(shí),在這個(gè)plugin的主類TVBoxPlugin中,能做的事并不多。
首先,定義了一個(gè)回報(bào)消息的攔截器,并在plugin初始化時(shí)將其加入統(tǒng)一的攔截器管理器中。代碼如下:
public class TVBoxPlugin implements Plugin{
private EchoInterceptor echoInter = null;
@Override
public void initializePlugin(PluginManager manager, File pluginDirectory) {
System.out.println("hello,TVBox Plugin!");
echoInter = new EchoInterceptor();
InterceptorManager.getInstance().addInterceptor(echoInter);
}
plugin初始化時(shí)做的另一件事就是在控制臺(tái)輸出TVBox Plugin的初始化信息,在前面openfire server console啟動(dòng)界面截圖中可以看到。
然后是重載的一個(gè)plugin銷毀時(shí)的方法:
@Override
public void destroyPlugin() {
System.out.println("TVBox Plugin Destroyed!");
if(echoInter != null){
echoInter.closeDB();
InterceptorManager.getInstance().removeInterceptor(echoInter);
}
}
輸出控制臺(tái)信息,并關(guān)閉DB,移除回報(bào)消息攔截器。
其他方法都是對(duì)攔截器的方法做了一次封裝,讓用戶可以通過(guò)plugin對(duì)象直接去操作消息攔截器里面定義的保存消息,查詢消息的方法。代碼如下:
public void setSendID(long value){
echoInter.setSendID(value);
}
public List<EchoMessage> getEchoMessage(){
return echoInter.getEchoMessage();
}
public List<TVBoxMessage> searchTVBoxMessage(String sendTo, long sendTimeFrom, long sendTimeTo){
return echoInter.searchTVBoxMessage(sendTo, sendTimeFrom, sendTimeTo);
}
public void saveTVBoxMessage(TVBoxMessage msg){
echoInter.saveTVBoxMessage(msg);
}
第二個(gè)Class是EchoInterceptor,實(shí)現(xiàn)了openfire預(yù)定義的PacketInterceptor接口,負(fù)責(zé)在plugin里對(duì)接收到的回報(bào)消息進(jìn)行處理。
首先是定義消息列表和sendID以及數(shù)據(jù)庫(kù)操作的SQL語(yǔ)句。
private List<EchoMessage> echoMessage = new ArrayList<EchoMessage>();
private long sendID = -1;
private static final String CREATE_TVBOXMESSAGE =
"INSERT INTO ofTVBoxMessage (sendID, sendFrom, sendTo, message, sendTime) " +
"VALUES (?, ?, ?, ?, ?); ";
private static final String RECIEVE_ECHOMESSAGE =
"UPDATE ofTVBoxMessage SET echoTime = ? WHERE sendID = ? AND sendTo = ? ;";
private static final String SEARCH_TVBOXMESSAGE =
"SELECT * FROM ofTVBoxMessage WHERE sendTo LIKE ? AND sendTime >= ? AND sendTime <= ? ORDER BY sendTime DESC;";
private static Connection con = null;
主要方法是重載的interceptPacket方法,對(duì)接收到的chat message進(jìn)行保存。
@Override
public void interceptPacket(Packet packet, Session session,
boolean incoming, boolean processed) throws PacketRejectedException {
// TODO Auto-generated method stub
if(!processed && packet instanceof Message){
if(incoming && Type.chat == ((Message)packet).getType()){
//System.out.println("Chat echo " + packet.toString());
EchoMessage curMsg = new EchoMessage((Message)packet);
echoMessage.add(curMsg);
recieveEchoMessage(curMsg);
}
}
}
這個(gè)類里的其他方法還包括保存和查詢回報(bào)消息。在plugin主類中已有調(diào)用,這里是其具體實(shí)現(xiàn)的地方。代碼比較簡(jiǎn)單,不再贅述。
第三個(gè)Class是EchoMessage,對(duì)openfire預(yù)定義的message類進(jìn)行擴(kuò)展,增加了sendID和recieveDate兩個(gè)屬性。
第四個(gè)Class是LinkItem,封裝了從HTML 內(nèi)容中解析出來(lái)的超鏈接對(duì)象。
第五個(gè)Class是ServletUtil,定義了一些公用方法。
第六個(gè)Class是TVBoxBean,定義了對(duì)openfire用戶和群組數(shù)據(jù)的操作。
第七個(gè)Class是TVBoxMessage,定義了對(duì)應(yīng)數(shù)據(jù)庫(kù)保存的EchoMessage的實(shí)體類。
第八個(gè)Class是TVBoxServlet,擴(kuò)展子HttpServlet,定義了Plugin中用到的需要用Servlet完成的功能。
TVBox plugin里主要包括以下四個(gè)JSP頁(yè)面,以后可能還會(huì)增加。每個(gè)jsp頁(yè)面首先在plugin.xml中有定義,sidebar中的每一個(gè)Item都對(duì)應(yīng)一個(gè)JSP頁(yè)面:
<sidebar id="sidebar-tvboxes" name="${users.sidebar.tvboxes.name}"
description="${users.sidebar.tvboxes.description}">
<item id="push-message" name="${tvbox.item.pushmessage.name}" url="push_message.jsp" description="${tvbox.item.pushmessage.description}" />
<item id="client-echo" name="${tvbox.item.clientecho.name}" url="client_echo.jsp"
description="${tvbox.item.clientecho.description}" />
<item id="message-history" name="${tvbox.item.messagehistory.name}" url="message_history.jsp"
description="${tvbox.item.messagehistory.description}" />
<item id="tvbox-manager" name="${tvbox.item.tvboxmanage.name}" url="tvbox_manage.jsp"
description="${tvbox.item.tvboxmanage.description}" />
</sidebar>
下面以PushMessaget.jsp為例,講解一下如何進(jìn)行JSP的開發(fā)。(其實(shí)可以首先從source code所帶插件中復(fù)制一個(gè)功能相近或相關(guān)的JSP過(guò)來(lái),修改成自己需要的就好了。)
JSP最開始的<%@ page import 部分無(wú)需多說(shuō),上一步class開發(fā)中寫的class用到的都需要在此導(dǎo)入。
下來(lái)的兩行引用了標(biāo)準(zhǔn)的Taglib,用于多語(yǔ)言處理,從前面定義的資源文件中提取字符串。
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt_rt" prefix="fmt" %>
接下來(lái)三行是:
<jsp:useBean id="webManager" class="org.jivesoftware.util.WebManager" />
<jsp:useBean id="boxBean" class="org.jivesoftware.openfire.plugin.tvbox.TVBoxBean" />
<% webManager.init(request, response, session, application, out );
定義了兩個(gè)JavaBean,一個(gè)是Openfire預(yù)定義的,一個(gè)是自己寫的,并照例對(duì)webManager bean進(jìn)行了初始化。
然后的java代碼主要是獲取所有的頻道數(shù)據(jù),openfire用戶組和在線用戶數(shù)據(jù)。
List<LinkItem> tvChannels = new ArrayList<LinkItem>();
tvChannels = boxBean.getFHWTVChannels("http://tv.lh.efoxconn.com/channel.htm");
List<String> groups = boxBean.getAllGroups();
List<String> onlineUsers = boxBean.getOnlineUsers();
隨后是處理頁(yè)面提交的代碼,獲取用戶選擇的TVBox以及輸入的信息,或者選擇的頻道,發(fā)送消息到TVBox,并在頁(yè)面顯示結(jié)果消息。其中調(diào)用plugin中的saveTVBoxMessage方法對(duì)消息進(jìn)行了保存:
String strMessage = request.getParameter("message");
String strSendTo = request.getParameter("tvboxes");
List<String> selectedBoxes = new ArrayList<String>();
String strSendResult = "消息發(fā)送結(jié)果:";
boolean isMessageSent = false;
if ((strMessage != null) && (strSendTo != null)){
SessionManager sessionManager = webManager.getSessionManager();
PresenceManager presenceManager = webManager.getPresenceManager();
UserManager userManager = webManager.getUserManager();
String[] users = strSendTo.split(",");
for(int i = 0;i < users.length;i ++){
selectedBoxes.add(users[i]);
}
String serverDomainName = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
// Get handle on the TVBox plugin
TVBoxPlugin plugin = (TVBoxPlugin)XMPPServer.getInstance().getPluginManager().getPlugin(
"tvbox");
long sendID = ServletUtil.getCurrentID();
plugin.setSendID(sendID);
for(int i = 0;i < users.length;i ++){
//由于選擇TVBox會(huì)自動(dòng)選擇器群組,所以發(fā)送消息時(shí)需將群組剔除
if (groups.contains(users[i]) == false){
try{
sessionManager.sendServerMessage(new JID(users[i] + "@" + serverDomainName), null, strMessage);
long sendTime = ServletUtil.date2long(new Date());
TVBoxMessage msg = new TVBoxMessage();
msg.setSendID(sendID);
msg.setSendFrom("admin@" + serverDomainName);
msg.setSendTo(users[i] + "@" + serverDomainName);
msg.setMessage(strMessage);
msg.setSendTime(sendTime);
plugin.saveTVBoxMessage(msg);
strSendResult += "<br />發(fā)送到 " + users[i] + " 成功!";
}
catch(Exception e){
strSendResult += "<br />發(fā)送到 " + users[i] + " 失敗 for !" + e.toString();
}
}
}
isMessageSent = true;
}
else{
strMessage = "";
}
strSendResult = new String(strSendResult.getBytes("ISO-8859-1"),"utf-8");
下面都是HTML的內(nèi)容了。
首先是HTML的head,其中包含的pageID就是在plugin.xml中定義過(guò)的,<fmt: 是調(diào)用標(biāo)
Taglib引用資源文件,其中的相對(duì)目錄都是位于plugin 代碼目錄的Web活頁(yè)夾下:
<html>
<head>
<title><fmt:message key="tvbox.pushmessage.title"/></title>
<meta name="pageID" content="push-message"/>
<link rel="stylesheet" type="text/css" href="style/treelistcontrol.css">
<script language="JavaScript" type="text/javascript" src="scripts/treelistcontrol.js"></script>
隨后的JavaScript代碼中,構(gòu)造了顯示TVBox群組用戶的樹型結(jié)構(gòu)以及頁(yè)面交互的響應(yīng)函數(shù),并且通過(guò)javascript定時(shí)函數(shù),實(shí)現(xiàn)在消息發(fā)送成功之后,延時(shí)三秒,自動(dòng)轉(zhuǎn)向回報(bào)信息頁(yè)面client_echo.jsp,可以查看到Client端接收消息后的回饋信息。
其他幾個(gè)JSP頁(yè)面大體相同,唯一值得注意的是引用的images及javascript等等web資源,都要用相對(duì)路徑,放在web下的相應(yīng)目錄中。然后就是多參照source code中附帶的其他plugin里面的類似功能的代碼,依葫蘆畫瓢就可以啦。
到這里,tvbox插件的所有東東都齊備了,整個(gè)源碼位于openfire371項(xiàng)目下的src目錄下的plugins里面,目錄結(jié)構(gòu)如下:
開發(fā)工作至此大功告成,接下來(lái)就是編譯plugin,生成jar檔,然后按前述方法部署就可以了。
Openfire 的源碼工程里,Ant view中預(yù)先定義了許多build任務(wù),如前面build源碼是運(yùn)行的openfire(default)。Build plugin可以運(yùn)行其中的plugins,build所有plugin;或者plugin,僅build某一個(gè)指定的plugin,比如TVBox,專門用來(lái)build自己開發(fā)的這一個(gè)TVBox plugin。
從Eclipse的Window 菜單,選擇Show View -> Ant,就會(huì)在右邊列出openfire source code源碼工程中預(yù)定義的Openfire XMPP Server build task列表。
然后向下拖動(dòng),右鍵選中其中的plugin,-> Run As -> External Tools Configurations…
在Arguments中輸入:-Dplugin=tvbox,其中tvbox就是你的plugin在src目錄下的源碼目錄名稱。
Run,運(yùn)行build task,如果Eclipse Console輸出有編譯錯(cuò)誤,請(qǐng)修改java class或jsp page,保證最終build successful。
成功編譯后,生成的plugin插件可以在target目錄下的plugins中看到:
前面實(shí)現(xiàn)的FHW TVBox的管理功能,使用一個(gè)admin賬號(hào)去管理所有的TVBox。當(dāng)TVBox很多時(shí),可能會(huì)出現(xiàn)不同的組需要由不同的管理員來(lái)管理這中需求。
為實(shí)現(xiàn)這一需求,首先,要在openfire server上注冊(cè)管理員用戶(新增用戶,勾選最下面的Is Administrator 選擇框):
這樣創(chuàng)建的用戶都具有管理員權(quán)限,登錄到openfire server上,能夠查看到所有群組中的TVBox。如果沒(méi)有勾選這個(gè)選項(xiàng),將不能看到其它的群組。
然后,在編輯用戶組 Tab的編輯組界面:
將這個(gè)管理員用戶加入被它管理的群組:
這樣,在FHWTVBoxManager中,admin登錄后,就只能看到它能夠管理的用戶組了:
(如果一個(gè)管理員需要管理多個(gè)組,可以把他加入多個(gè)組中。)
FHWTVBox 的client 程序已經(jīng)修改,client在登錄openfire server后,除了與缺省admin建立連接外,還會(huì)與同組內(nèi)的所有具有管理員權(quán)限的賬號(hào)建立連接(在前面的代碼示例中可以看到)。這樣,除了admin賬號(hào)仍然可以管理外,所有與client賬號(hào)同在一組的具有管理員權(quán)限的賬號(hào),都可以使用push message的方式對(duì)這個(gè)TVBox進(jìn)行管理了。
本博客中相關(guān)工程源碼可到CSDN下載:
http://download.csdn.net/detail/yangdanbo1975/5825753
聯(lián)系客服