OpenFire源码学习之十五:插件开发
Plugin接口规范
插件是openfire功能的增强表现,它的主要任务:
l 在XMPP协议中作为附加功能实现
l 动态修改控制管理台
l 使用openfire api作为新功能添加到服务器
Openfire里面的插件都会存放在plugins(工程目录为:src/plugins)的住目录下。使用ant工具编译后插件会打成jar包生成在target/openfire/plugins目录下。一个完整的插件应该包含以下的结构:
Yourplugin/
| -plugin.xml 插件定义文件
| -readme.html 任择自述文件的插件,它会显示给最终用户
| -changelog.html 任择修改文件的插件,它会显示给最终用户
| -icon_small.gif 可选小( 16x16 )图标与插件(也可以是 PNG文件)
| -icon_large.gif 可选大( 32x32 )图标与插件(也可以是 PNG文件)
|classes/ 资源的插件需要(即属性文件)
|-database/ 可选数据库架构文件,你需要插件
| -i18n/ 插件国际化的语言配置。
|-lib/ 您的插件的jar包
|-web 资源的管理控制台集成,如果有的话
| - WEB-INF
|-web-custom.xml 可选用户自定义的web.xml中的自定义servlets
|-images/ 图片文件存放的目录
|-test-plugin.jsp jsp文件
其中类和lib目录是可选的,类目录下的所有文件和以及在lib目录的所有JAR文件中都会被添加到classpath路径下。 plugin.xml 这个文件也必须要添加到您的插件当中,这个文件描述了改插件的的基本信息和一些需要在控制台上生成目录的信息。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>Jive Software</author> <version>1.0</version> <date>07/01/2006</date> <url>http://www.igniterealtime.org/projects/openfire/plugins.jsp</url> <!-- 要求openfire服务器的最低版本 --> <minServerVersion>3.0.0</minServerVersion> <licenseType>gpl</licenseType> <!-- 管理控制台的条目 --> <adminconsole> <!-- More on this below --> </adminconsole> </plugin>
该元素的领域设置介绍:
l Name 该插件的名称
l Description 该插件说明
l Author 该插件作者
l Version 该插件的版本标识
l Date 该插件的发布日期
l Url 该插件说明网址
l minServerVersion 该插件需要最低版本Opfenfire的支持
l licenseType 显示许可协议,常用的显示值如下:
“commercial”:commercial “商业” :插件是下发布的商业许可协议。
“gpl”: 通用公共许可证,插件发布使用GNU公共授权
“apache”:Apache许可证internal
“internal”:插件是提供内部组织使用
“other”: 如果许可未设置就会假定为其他
l databaseKey 如果插件需要它自己的数据表,该databaseKey内容应设立一个架构 主要名称(通常是相同名称的插件)。数据库架构文件为每个支持的数据库,然后放置在数据库目录下的插件。例如, “foo”,架构文件将被称为“ foo_mysql.sql ” , “ foo_oracle.sql ”等等,我们建议您,您的表前缀of ,以避免可能的冲突与其他应用程序安装在同一数据库。脚本应该进入ofVersion表使用的关键,这样的架构版本信息可跟踪,例如:
INSERT INTO ofVersion (name, version) VALUES ('foo', 0); databaseVersion -数据库版本号(如果数据库模式的定义)。新的插件与数据库架构应该开始在版本。如果将来插件版本的需要更新,这些更新可以定义创建子目录中的升级数据库目录为每个版本。例如,目录database/upgrade/1和database/upgrade/2将包含脚本,如“ foo_mysql.sql
”和“ foo_oracle.sql ”中包含相关的数据库,为每一个版本的变化。每个脚本应该更新版本中的信息ofVersion表,例如:
UPDATE ofVersion set version=1 where name='foo';
l parentPlugin 父层插件(作为“foo”的“ foo.jar ”插件)。当一个插件有一个父插件,插件的类加载器将被使用来而不是建立一个新的类加载器。这可让插件更加紧密地协同工作。子插件将不会影响其父插件。
l Adminconsole 该插件在管理控制台的条目
一个完整plugin.xml文件内容应该如下:
<?xml version="1.0" encoding="UTF-8"?> <plugin> <class>org.jivesoftware.openfire.plugin.RegistrationPlugin</class> <name>Registration</name> <description>Performs various actions whenever a new user account is created.</description> <author>Ryan Graham</author> <version>1.5.0</version> <date>12/2/2009</date> <minServerVersion>3.7.0</minServerVersion> <adminconsole> <tab id="tab-users"> <sidebar id="sidebar-users"> <item id="registration-props-form" name="Registration Properties" url="registration-props-form.jsp" description="User Registration" /> </sidebar> </tab> </adminconsole> </plugin>
插件开发
注意:启动of的开发模式需要添加tools.jar包 添加到target/lib目录下
设置参数-DdevelopmentMode="true"
一、添加如下结构插件包
二、编写TestIQHandle类
public class TestIQHandle extends IQHandler { private static final String MODULE_NAME = "test plugin"; private static final String NAME_SPACE = "com:test:testplug"; private IQHandlerInfo info; public TestIQHandle(){ super(MODULE_NAME); info = new IQHandlerInfo("query", NAME_SPACE); } public TestIQHandle(String moduleName) { super(moduleName); info = new IQHandlerInfo("query", NAME_SPACE); } @Override public IQ handleIQ(IQ packet) throws UnauthorizedException { IQ reply = IQ.createResultIQ(packet); Element groups = packet.getChildElement(); if(true){ System.out.println("=======请求非法========"); } if(!IQ.Type.get.equals(packet.getType())){ reply.setChildElement(groups.createCopy()); reply.setError(PacketError.Condition.bad_request); return reply; } //StringUtils.substringBefore(packet.getFrom().toString(), "@"); return reply; } @Override public IQHandlerInfo getInfo() { // TODO Auto-generated method stub return info; } }
三、编写TestPlugin类
public class TestPlugin implements Plugin ,PropertyEventListener{ private XMPPServer server; @Override public void initializePlugin(PluginManager manager, File pluginDirectory) { server = XMPPServer.getInstance(); server.getIQRouter().addHandler(new TestIQHandle()); PropertyEventDispatcher.addListener(this); System.out.println("==========插件初始化============="); } @Override public void destroyPlugin() { PropertyEventDispatcher.removeListener(this); System.out.println("==========插件销毁动作============="); } @Override public void propertySet(String property, Map<String, Object> params) { // TODO Auto-generated method stub } @Override public void propertyDeleted(String property, Map<String, Object> params) { // TODO Auto-generated method stub } @Override public void xmlPropertySet(String property, Map<String, Object> params) { // TODO Auto-generated method stub } @Override public void xmlPropertyDeleted(String property, Map<String, Object> params) { // TODO Auto-generated method stub } }
四、编写TestServlet
public class TestServlet extends HttpServlet{ public TestServlet() { super(); } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("============调用servlet============="); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } public void init() throws ServletException { AuthCheckFilter.addExclude("test/testservlet"); System.out.println("==========init()============"); } public void destroy() { System.out.println("==========destroy()========="); AuthCheckFilter.addExclude("test/testservlet"); } }
五、配置web-custom.xml
<?xml version='1.0' encoding='ISO-8859-1'?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <servlet> <servlet-name>TestServlet</servlet-name> <servlet-class>com.test.plugin.test.TestServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>TestServlet</servlet-name> <url-pattern>/servlet</url-pattern> </servlet-mapping> </web-app>
六、test-plugin.jsp
<%@ page import="java.util.*, org.jivesoftware.openfire.XMPPServer, org.jivesoftware.util.*, com.test.plugin.TestPlugin" errorPage="error.jsp" %> <%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %> <%@ taglib uri="http://java.sun.com/jstl/fmt_rt" prefix="fmt" %> <%-- Define Administration Bean --%> <jsp:useBean id="admin" class="org.jivesoftware.util.WebManager" /> <c:set var="admin" value="${admin.manager}" /> <% admin.init(request, response, session, application, out ); %> <%String path = request.getContextPath(); System.out.println("path= "+path); %> <html> <head> <title>User Service Properties</title> <meta name="pageID" content="test_plugin"/> </head> <body> <p>==============================================</p> <form action="<%=path %>/plugins/testplug/servlet" method="post"> <fieldset> <legend>test plugin</legend> <div> <input type="text" size="15" /><br> <input type="text" size="15" /><br> </div> </fieldset> <br><br> <input type="submit" value="Save Settings"> </form> </body> </html>
七、Plugin.xml
<?xml version="1.0" encoding="UTF-8"?> <plugin> <class>com.test.plugin.server.TestPlugin</class> <name>TestPlugin</name> <description>test plugin For myTestPlugin</description> <author>huwf</author> <version>1.0.0</version> <date>5/10/2013</date> <url>http://www.baidu.com</url> <minServerVersion>3.5.1</minServerVersion> <licenseType>gpl</licenseType> <adminconsole> <tab id="tab-server"> <sidebar id="sidebar-server-settings"> <item id="test_plugin" name="testPlugin" url="test-plugin.jsp" description="Edit subscription plugin properties" /> </sidebar> </tab> </adminconsole> </plugin>
八、发布/编译插件