插件結(jié)構(gòu)
myplugin /
|-pom.xml <-Maven項(xiàng)目的插件描述/配置文件(允許您定義Maven使用的POM(項(xiàng)目對象模型)。
|-plugin.xml <-插件定義文件
|-readme.html <-插件的可選自述文件,它將顯示給最終用戶
|-changelog.html <-插件的可選changelog文件,它將顯示給最終用戶
|-logo_small.gif <-與插件關(guān)聯(lián)的可選小(16x16)圖標(biāo)(也可以是.png文件)
|-logo_large.gif <-與插件關(guān)聯(lián)的可選大(32x32)圖標(biāo)(也可以是.png文件)
|-src
|-classes / <-插件所需的資源(即屬性文件)
|-database/ <-插件所需的可選數(shù)據(jù)庫架構(gòu)文件
|-i18n / <-可選的i18n文件晾匠,以允許插件國際化。
|-lib / <-插件需要的庫(JAR文件)
|-java / <-這是包含插件應(yīng)用程序(.java文件)源的目錄梯刚,位于軟件包中
|-web <-管理控制臺集成的資源(如果有)
|-WEB-INF /
|-web.xml <-包含編譯的JSP條目的生成的web.xml
|-web-custom.xml <-自定義servlet的可選用戶定義的web.xml
|-images/
plugin.xml
<?xml version="1.0" encoding="UTF-8"?>
<plugin>
<!-- 插件主類 -->
<class>org.example.ExamplePlugin</class>
<!--插件信息-->
<name>Example Plugin</name>
<description>This is an example plugin.</description>
<author>阿倫</author>
<version>1.0</version>
<date>07/01/2006</date>
<url>http://www.baidu.com</url>
<minServerVersion>3.0.0</minServerVersion>
<licenseType>gpl</licenseType>
<!-- 管理控制臺條目 -->
<adminconsole>
<!-- 控制臺內(nèi)容 -->
</adminconsole>
</plugin>
- name -- 插件的名稱凉馆。
- description -- 插件的描述。
- author -- 插件的作者亡资。
- version -- 插件的版本澜共。
- date -- 插件發(fā)布的日期。日期必須采用MM / dd / yyyy格式锥腻,例如07/01/2006嗦董。
- url -- 其中提供了有關(guān)插件的其他信息。
- minServerVersion -- 運(yùn)行插件所需的Openfire的最低版本(受Openfire 2.1.2和更高版本支持)瘦黑。如果服務(wù)器版本小于要求的值京革,則不會啟動(dòng)插件。
- beforeToServerVersion -- 可以運(yùn)行此插件的服務(wù)器版本幸斥,但不包括此版本匹摇。
- minJavaVersion --插件需要運(yùn)行的最低Java規(guī)范版本。
- databaseKey -- 如果插件需要它自己的數(shù)據(jù)庫表甲葬,則應(yīng)使用架構(gòu)鍵名稱(通常與插件名稱相同)來設(shè)置databaseKey元素廊勃。然后,應(yīng)將每個(gè)受支持?jǐn)?shù)據(jù)庫的數(shù)據(jù)庫模式文件放置在 插件的數(shù)據(jù)庫目錄中经窖。例如坡垫,給定鍵“ foo”,模式文件將被稱為“ foo_mysql.sql”钠至,“ foo_oracle.sql”等葛虐。我們建議您為表加上“ of”(openfire)前綴胎源,以避免與其他應(yīng)用程序發(fā)生沖突安裝在同一數(shù)據(jù)庫中棉钧。腳本應(yīng)使用鍵在ofVersion表中進(jìn)行輸入,以便可以跟蹤架構(gòu)版本信息涕蚤,例如:
INSERT INTO ofVersion (name, version) VALUES ('foo', 0);
- databaseVersion -- 數(shù)據(jù)庫模式版本(如果定義了數(shù)據(jù)庫模式)宪卿。具有數(shù)據(jù)庫模式的新插件應(yīng)從版本0開始。如果插件的未來版本需要對該模式進(jìn)行更新万栅,則可以通過在數(shù)據(jù)庫/升級目錄中為每個(gè)版本號創(chuàng)建子目錄來定義這些更新佑钾。例如,目錄 database / upgrade / 1 和database / upgrade / 2將包含諸如“ foo_mysql.sql”和“ foo_oracle.sql”之類的腳本烦粒,這些腳本包含每個(gè)版本的相關(guān)數(shù)據(jù)庫更改休溶。每個(gè)腳本應(yīng)更新ofVersion表中的版本信息代赁,例如:
UPDATE ofVersion set version=1 where name='foo';
- parentPlugin -- 父插件的名稱(對于“ foo.jar”插件,命名為“ foo”)兽掰。當(dāng)插件具有父插件時(shí)芭碍,將使用父插件的類加載器,而不是創(chuàng)建新的類加載器孽尽。這使插件可以更緊密地協(xié)同工作窖壕。沒有父插件,子插件將無法運(yùn)行杉女。
- licenseType -- 指示插件受其管轄的許可協(xié)議瞻讽。有效值為:
“commercial”:該插件是根據(jù)商業(yè)許可協(xié)議發(fā)布的。
“gpl”:該插件是根據(jù)GNU公共許可證(GPL)發(fā)布的熏挎。
“apache”:該插件根據(jù)Apache許可發(fā)布速勇。
“internal”:該插件僅供組織內(nèi)部使用,不會重新分發(fā)坎拐。
“other”:該插件是根據(jù)許可協(xié)議發(fā)行的快集,該許可協(xié)議不屬于其他類別之一。許可協(xié)議應(yīng)該是插件自述文件中的詳細(xì)信息廉白。
如果未設(shè)置許可證類型个初,則假定為其他。
插件中可以存在幾個(gè)其他文件猴蹂,以向最終用戶提供其他信息(所有信息都放置在主插件目錄中):
- readme.html -- 插件的可選自述文件院溺,將顯示給最終用戶。
- changelog.html -- 插件的可選changelog文件磅轻,將顯示給最終用戶珍逸。
- logo_small.png -- 與插件關(guān)聯(lián)的可選小(16x16)圖標(biāo)聋溜。它也可以是.gif文件谆膳。
- logo_large.png -- 與插件關(guān)聯(lián)的可選大(32x32)圖標(biāo)。它也可以是.gif文件撮躁。
實(shí)現(xiàn) Plugin接口
插件實(shí)現(xiàn)Openfire API的 Plugin 接口漱病,并且必須具有默認(rèn)(無參數(shù))構(gòu)造函數(shù)。Plugin接口具有用于初始化和銷毀??插件的方法把曼。
package org.example;
import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager;
import java.io.File;
/**
*實(shí)例
*/
public class ExamplePlugin implements Plugin {
//初始插件回調(diào)
public void initializePlugin(PluginManager manager, File pluginDirectory) {
// 這里寫你的代碼
}
//銷毀插件回調(diào)
public void destroyPlugin() {
// 這里寫你的代碼
}
}
修改管理控制臺
插件可以將標(biāo)簽杨帽,部分和頁面添加到管理控制臺。步驟:
- 必須將<adminconsole />部分添加到 plugin.xml文件中嗤军。
- 必須編譯JSP文件并將其放入插件的類路徑中注盈。一個(gè)web.xml中含有編譯JSP的servlet項(xiàng)文件必須放到插件目錄 web/。注意: Openfire構(gòu)建腳本可以幫助編譯JSP和創(chuàng)建web.xml叙赚。這在下面詳細(xì)說明老客。
- JSP頁面所需的任何圖片都必須位于web / images / 目錄中僚饭。僅支持GIF和PNG圖像。
plugin.xml的<adminconsole />部分定義了管理控制臺框架中的其他選項(xiàng)卡胧砰,部分和條目浪慌。一個(gè)示例 plugin.xml文件可能如下所示:
plugin.xml
<?xml version="1.0" encoding="UTF-8"?>
<plugin>
<!-- Main plugin class -->
<class>org.example.ExamplePlugin</class>
<!-- Admin console entries -->
<adminconsole>
<tab id="mytab" name="Example" url="my-plugin-admin.jsp" description="Click to manage...">
<sidebar id="mysidebar" name="My Plugin">
<item id="my-plugin" name="My Plugin Admin"
url="my-plugin-admin.jsp"
description="Click to administer settings for my plugin"
order="4" />
<item id="my-plugin" name="My Plugin Overview"
url="my-plugin-overview.jsp"
description="Click to have an Overview of Plugin usage"
order="2" />
</sidebar>
</tab>
</adminconsole>
</plugin>
在此示例中,我們定義了一個(gè)新選項(xiàng)卡“ Example”朴则,一個(gè)側(cè)欄部分“ My Plugin”和兩個(gè)頁面:“ My Plugin Admin”和“ My Plugin Overview”权纤。我們已經(jīng)分別將my-plugin-admin.jsp和my-plugin-overview.jsp注冊 為頁面。
默認(rèn)情況下乌妒,選項(xiàng)卡汹想,側(cè)邊欄和頁面將按照定義順序顯示。但是撤蚊,您可以通過向每個(gè)元素添加“ order”屬性來定義顯式排序古掏。它的數(shù)值定義順序。如果未指定任何順序侦啸,則默認(rèn)值為0槽唾。在上面的示例中,使用此構(gòu)造對項(xiàng)目??進(jìn)行排序光涂。在管理控制臺中庞萍,“My Plugin Overview”頁面將在“My Plugin Admin”頁面之前顯示,因?yàn)槠洹皁rder”值較低忘闻。如果兩個(gè)項(xiàng)目都未定義'order'屬性钝计,則兩個(gè)頁面的顯示都將被反轉(zhuǎn)(因?yàn)樗?jīng)用來對以XML定義頁面的順序進(jìn)行排序)。
您可以使用自己的<adminconsole>定義中的現(xiàn)有id屬性值來覆蓋現(xiàn)有的tabs, sections和items 齐佳。
管理控制臺最佳做法
通過插件更改Openfire管理控制臺時(shí)私恬,需要考慮幾種最佳做法。一般主題是插件應(yīng)無縫集成:
- 盡可能將其集成到現(xiàn)有的選項(xiàng)卡和側(cè)邊欄部分中炼吴,而不是自己創(chuàng)建本鸣。僅為非常重要的新功能創(chuàng)建新選項(xiàng)卡。
- 標(biāo)簽硅蹦,側(cè)邊欄和項(xiàng)目名稱中請勿使用“插件”一詞荣德。例如,與其具有名為“ Gateway Plugin”的項(xiàng)目提针,不如將其稱為“ Gateway Settings”命爬。
- 嘗試在您的自定義插件頁面中匹配現(xiàn)有管理控制臺的用戶界面。
- 無需創(chuàng)建管理控制臺條目即可顯示插件元數(shù)據(jù)辐脖。相反,讓Openfire通知用戶有關(guān)已安裝哪些插件的信息并提供插件管理皆愉。
編寫管理控制臺頁面
Openfire使用Sitemesh 框架在管理控制臺中裝飾頁面嗜价。如下圖所示艇抠,將全局定義的裝飾器應(yīng)用于每個(gè)頁面以呈現(xiàn)最終輸出:
創(chuàng)建適用于Sitemesh的頁面很容易。只需創(chuàng)建有效的HTML頁面久锥,然后使用元標(biāo)記將指令發(fā)送到Sitemesh家淤。呈現(xiàn)輸出時(shí),Sitemesh將使用您提供的說明來呈現(xiàn)裝飾器以及HTML頁面正文中的任何內(nèi)容瑟由⌒踔兀可以使用以下元標(biāo)記:
- pageID -- 頁面的ID,必須與上述管理控制臺XML中的條目匹配歹苦。必須指定pageID或subPageID 青伤。
- subPageID -- 子頁面的ID,必須與上述管理控制臺XML中的條目匹配殴瘦。子頁面用于與父頁面ID相關(guān)的管理操作狠角。例如,編輯或刪除特定組蚪腋。必須指定pageID或subPageID 丰歌。
- extraParams(可選)--應(yīng)該傳遞給頁面的額外參數(shù)。例如屉凯,在要?jiǎng)h除組的頁面上立帖,它可能是該組的ID。參數(shù)必須經(jīng)過URL編碼悠砚。
- decorator(可選)-- 覆蓋用于頁面的Sitemesh裝飾器厘惦。可以使用名為none的裝飾器哩簿,該裝飾器將在沒有裝飾器的情況下簡單地呈現(xiàn)頁面宵蕉。
<html>
<head>
<title>My Plugin Page</title>
<meta name="pageID" content="myPluginPage"/>
</head>
<body>
Body here!
</body>
</html>
在插件中使用i18n
可以將您的插件翻譯成多種語言(i18n)。為此节榜,請使用以下過程:
- 在插件的根目錄中創(chuàng)建一個(gè)“ i18n”目錄羡玛。
- 使用%[plugin_name]%_i18n "_" language ".properties"命名約定添加每個(gè)資源文件,其中[plugin_name]是插件目錄的名稱宗苍。有關(guān)資源束的更多信息稼稿,請參見 翻譯指南。
- 將JSP文件中的字符串轉(zhuǎn)換為引用國際化密鑰讳窟。例如:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
...
<fmt:message key="some.key.name" />
- 使用LocaleUtils類將Java文件中的字符串國際化:
org.jivesoftware.util.LocaleUtils.getLocalizedString("some.key.name", "[plugin_name]");
使用$ {var}格式將plugin.xml文件中的字符串國際化:
<sidebar id="gateways" name="${plugin.sidebar.name}" description="${plugin.sidebar.description}">
<description>${plugin.description}</description>
使用Openfire構(gòu)建腳本
Openfire構(gòu)建腳本將幫助您構(gòu)建和開發(fā)插件让歼。它以以下格式查找插件開發(fā)目錄:
插件結(jié)構(gòu)
myplugin/
|- plugin.xml <- Plugin definition file
|- readme.html <- Optional readme file for plugin
|- changelog.html <- Optional changelog file for plugin
|- logo_small.gif <- Optional small (16x16) icon associated with the plugin (can also be a .png file)
|- logo_large.gif <- Optional large (32x32) icon associated with the plugin (can also be a .png file)
|- classes/ <- Resources your plugin needs (i.e., a properties file)
|- lib/ <- Libraries your plugin needs
|- src/
|- database <- Optional database scripts for your plugin
|- java <- Java source code for your plugin
| |- com
| |- mycompany
| |- *.java
|- web
|- *.jsp <- JSPs your plugin uses for the admin console
|- images/ <- Any images your JSP pages need (optional)
|- WEB-INF
|- web.xml <- Optional file where custom servlets can be registered
構(gòu)建腳本將編譯源文件和JSP,并創(chuàng)建有效的插件結(jié)構(gòu)和JAR文件丽啡。將您的插件目錄放在 源發(fā)行版的src / plugins目錄中谋右,然后使用ant插件來構(gòu)建您的插件。
您的插件在編譯過程中需要的所有JAR文件都應(yīng)放在lib目錄中补箍。在構(gòu)建過程中改执,還將這些JAR文件復(fù)制到插件的生成的lib目錄中啸蜜。
如果創(chuàng)建src / web / WEB-INF / web.xml文件,則在插件啟動(dòng)時(shí)將初始化在此注冊的所有servlet辈挂。從web.xml文件中僅接受servlet注冊和servlet映射衬横。注意:此功能是通過將您的自定義web.xml文件合并到JSP編譯過程生成的web.xml文件中來實(shí)現(xiàn)的。
***********=============現(xiàn)在推薦用maven構(gòu)建,下一章介紹===========************
實(shí)施您的插件
插件具有對Openfire API的完全訪問權(quán)限终蒂。這為插件可以完成的工作提供了極大的靈活性蜂林。但是,有幾個(gè)最常見的集成點(diǎn):
將插件注冊為Component拇泣。組件接收所有發(fā)往特定子域的數(shù)據(jù)包噪叙。例如, test_component.example.com挫酿。因此构眯,發(fā)送到 joe_component.example.com 的數(shù)據(jù)包將被傳遞到該組件。請注意早龟,定義為組件的子域與子域的DNS條目無關(guān)惫霸。套接字級別的所有XMPP路由都使用主服務(wù)器域(在上面的示例中為example.com)完成;子域僅用于XMPP服務(wù)器中的路由葱弟。
將插件注冊為IQHandler壹店。IQ處理程序以特定的元素名稱和名稱空間響應(yīng)IQ數(shù)據(jù)包。以下代碼段演示了如何注冊IQHandler:
IQHandler myHandler = new MyIQHander();
IQRouter iqRouter = XMPPServer.getInstance().getIQRouter();
iqRouter.addHandler(myHandler);
將插件注冊為 PacketInterceptor以接收通過系統(tǒng)發(fā)送的所有數(shù)據(jù)包芝加,并有選擇地拒絕它們硅卢。例如,攔截器可以拒絕所有包含敏感詞的郵件或?qū)⑵錁?biāo)記為由管理員查看藏杖。
您可以使用JiveGlobals.getProperty(String)和JiveGlobals.setProperty(String将塑,String)方法將持久性插件設(shè)置存儲為Openfire屬性。通過實(shí)現(xiàn)org.jivesoftware.util.PropertyEventListener方法蝌麸,使插件成為屬性偵聽器点寥,以偵聽對其屬性的更改 。您可以使用PropertyEventDispatcher.addListener(PropertyEventListener)方法將插件注冊為偵聽器来吩。確保在插件的destroyPlugin()方法中將插件注冊為偵聽器敢辩。
Openfire管理員標(biāo)簽
Openfire提供了可以使用的有用的JSP標(biāo)簽。要在JSP頁面上啟用它們弟疆,只需在JSP頁面
<%@ taglib uri="admin" prefix="admin" %>
頂部添加標(biāo)簽,包括:
<admin:ASN1DER value="${ASN.1 DER certificate as a byte[]}"/>
<!-- (自O(shè)penfire 4.0.0起)將在HTML表中顯示ASN.1 DER編碼的證書戚长。-->
<admin:FlashMessage/>
<!--(自O(shè)penfire 4.5.0起)將在呈現(xiàn)的頁面上最多顯示三個(gè)經(jīng)過適當(dāng)裝飾的會話屬性。
這些會話屬性的密鑰是由定義 FlashMessageTag.SUCCESS_MESSAGE_KEY怠苔,
WARNING_MESSAGE_KEY和 ERROR_MESSAGE_KEY同廉。這允許在頁面之間
導(dǎo)航時(shí)向用戶顯示消息。-->
CSRF保護(hù)
管理員頁面容易受到CSRF攻擊。Openfire提供了一些工具來幫助插件作者在其管理頁面上防御這些攻擊恤溶。啟用CSRF保護(hù):
- 將plugin.xml設(shè)置
minServerVersion
為4.5.0或更高版本乓诽,因?yàn)檫@是添加支持時(shí)的狀態(tài)帜羊。 - 將plugin.xml
csrfProtectionEnabled
設(shè)置true
為啟用插件的CSRF保護(hù)咒程。這將;- 防范CSRF攻擊,除了 GET請求外讼育,所有對管理頁面的請求
- 使用鍵“ csrf”設(shè)置servlet請求屬性
- 確保GET請求不會修改任何設(shè)置或更改任何數(shù)據(jù)帐姻,因?yàn)槲礊镚ET請求啟用此保護(hù)
- 確保在管理頁面中提交的任何表單都有一個(gè)名為
csrf
的字段,該字段的值由請求屬性“ csrf”定義-例如:
<input name="csrf" value="<c:out value="${csrf}"/>" type="hidden">
如果檢測到CSRF攻擊奶段,將使用FlashMessageTag.ERROR_MESSAGE_KEY
設(shè)置了session屬性以指示問題的狀態(tài)(使用簡單的HTTP GET請求)重新加載管理頁面-因此建議<admin:FlashMessage/>
在JSP頁面的頂部包含饥瓷。
注意 :使用<c:out>標(biāo)簽或等效標(biāo)簽確保所有輸出正確轉(zhuǎn)義仍然很重要 。
插件常見問題
我可以將插件部署為目錄而不是JAR嗎痹籍?
不呢铆,所有插件都必須部署為JAR或WAR文件。當(dāng)插件不存在JAR或WAR時(shí)蹲缠,Openfire會假定該文件已被刪除棺克,并且用戶希望銷毀該插件,因此它也會刪除該目錄线定。