openfire插件开发(plugin sevlet)入门
近来几天为了写一个openfire的小插件可谓脑袋都大了。。。自己又是初学小菜鸟一个,学习过程算是非常的磕磕绊绊。这里记录下来仅供后来的同学参考,也少走些弯路。勤能补拙,期望大家多多支持,共同学习,共同进步。
1.plugin插件开发
1.1简单插件入门(简单plugin结构)
在结构方面我最初也是很头疼,给那么多图反而更容易把人搞混淆。我们还是用文字加配图说明吧。
新建一个 java project ,这里命名为 test_plugin。(new -> java project -> finish)然后把src删除或重命名为 src/plugins/test/src/java (即与openfire原生插件保持一致)。若是删除src的话,就新建 Source Folder命名为 src/plugins/test/src/java (new -> Source Folder )。然后项目目录下就会自动生成对应的 src文件夹及层级结构。
同样,在工程项目中新建文件夹(new ->folder)命名 build ,lib .其中build下边新建 build.properties 和 build.xml文件,用以ant编译生成插件。llib文件存放openfire等jar库。这里将openfir.jar + sevlet.jar拷贝至lib目录并add to buid path.
(servlet其实可以不用,仅导入openfire.jar即可)
1.2 build文件
build文件用于编译和打包,很多像我这样的小白基本不会用代码打包编译。网上有推荐利用源码包里边的build来编译打包,这也是一种方法,这里我使用的是jooho大大写好的build文件。我稍微做了些修改。贴上两个文件源码
uild.properties
build.xml
![](https://i-blog.csdnimg.cn/blog_migrate/dff80e79a440b032211e3d3044f2ed19.png)
1.3 其他文件结构
在source folder下新建包(例 com.qiao.test.plugin),此包即为上边要修改的地方。
新建TestPlugin 类并实现Plugin接口 中的 init 和 destroy方法:
TestPlugin .java
changelog.htmllogo_small.giflogo_large.gif readme.html 和plugin.xml文件,其中前面是个可以从openfire源码中拷贝
changelog.html是修改日志;logo_small.gif是插件图标;plugin.xml是我们配置插件的文件,这个很重要。
配置 plugin.xml
注意上面的class的配置,那个配置是最为重要的,配置的是插件的全路径;name是插件的名称,安装后的插件名称;author是插件作者;lincenseType是协议;adminconsole是配置插件关联的页面的,这里用不上
至此,整个框架大概就是这样:
编写ant命令,打可部署jar包。如果你不懂ant命令也没关系,你总知道java的基本常用的dos命令。只不过ant就是将dos转换成一个可重复多次调用的命令行。我们这里就是用上边的build.xml利用ant来编译和打包。
以下是jooho大大的话,小白可以略过。
(注意:这里我没有编写编译java代码到class的步骤,我是直接使用MyEclipse自动编译的bin/class的。如果你没有用MyEclipse或Eclipse,那么你需要将src中的Java代码编译class。
这里需要配置tomcat的目录,我这里是5.0.28的版本。我用tomcat6有些问题,这里主要是用tomcat中的lib库,帮助我们编译jsp。还需要配置你当前工程的所在目录,也就是工程在Eclipse中的目录位置。最后你需要配置插件的名称和插件在工程中的所在目录,这个是在打包的时候,需要将其他的html、image、xml等资源导入的jar内。
因为这里的插件是不带jsp的,所以我们执行clean、java-jar、java-plugin-jar。也就是openfire-plugin-java这个命令即可。执行命令后,你可以看到工作空间的工程目录下多了目录和文件。)
1.4 插件打包部署
我们右键点击build.xml选择 run as -> ant build..
选择openfire-plugin-java
等待打包完成
打包完成后刷新工程项目,会看到多出来个bin文件夹
打包成功的test.jar部署到openfire服务器,部署有两种方法:
1.直接拷贝至openfire安装目录下的plugins的文件夹内,自启动后它会自动解压
2.登录openfire管理界面,选择插件,上传
在openfire启动的情况下,访问http://localhost:9090/plugin-admin.jsp页面,点击页面下方的upload plugin完成插件上传操作。
插件按照成功后,访问http://localhost:9090/plugin-admin.jsp页面你就可以看到安装好的插件了。
![](https://i-blog.csdnimg.cn/blog_migrate/500d7de9ced6a55b84b975d42b2be1dd.png)
测试插件。启动openfire,就可以看到插件的运行结果如下:
2.含有servlet和JSP的插件开发
2.1 serlet开发及配置
新建一个TestServlet.java 继承HttpServlet
TestServlet.java
其中 AuthCheckFilter.addExclude(SERVICE_NAME); 是为了避免登录验证,如果没有这一段代码。每次访问TestServelt都会出现跳转到登录界面的情况。
![](https://i-blog.csdnimg.cn/blog_migrate/b5d79aa7c8b009b8b7fac0dbd1ab167a.png)
web-custom.xml
2.2 Jsp开发及配置
test-demo.jsp
其中最重要的一点就是:<meta name="pageID" content="sample-service"/>这个pageID。这里的是固定的,后面的content对应我们plugin.xml的内容(等下看看plguin.xml的配置)。然后可以适当的看下里面table的 属性和样式,因为很多时候会在jsp中显示内容,且用table布局的。
plugin.xml
注意,这里是jsp里边的 pageID
![](https://i-blog.csdnimg.cn/blog_migrate/04510e3b4dcbb3447fea59f1d76640b5.png)
2.3 运行ant,发布插件
![](https://i-blog.csdnimg.cn/blog_migrate/970d0dd1d5a90ff0ffad25b4fe0276bf.png)
![](https://i-blog.csdnimg.cn/blog_migrate/14a47976b48a0af0d086130540e12a8c.png)
3.Servlet拓展问题及解决
3.1.servlet登录验证问题
那怎么单独对自己插件的url进行排除呢?接着往下看!
有两种方法:修改openfire的web.xml或使用AuthCheckFilter.addExclude方法其中一种即可
1)修改openfire的web.xml
这个web.xml出现在openfire的源码很多地方,了解正确的配置方法很重要。下面展示不同路径下的web.xml修改说明,取其一即可。
a. 修改/openfire_src/src/web/WEB-INF/web.xml,修改完后,记得还要用ant编译一次并重启才能生效。
b. 修改/openfire_src/target/openfire/plugins/admin/webapp/WEB-INF/web.xml
修改完后,直接重启即可生效,本方法同样适用于安装版的openfire
- <filter>
- <filter-name>AuthCheck</filter-name>
- <filter-class>org.jivesoftware.admin.AuthCheckFilter</filter-class>
- <init-param>
- <param-name>excludes</param-name>
- <param-value>
- popplugin/*,login.jsp,index.jsp?logout=true,setup/index.jsp,setup/setup-*,.gif,.png,error-serverdown.jsp,setup/clearspace-integration-prelogin.jsp
- </param-value>
- </init-param>
- </filter>
url的匹配规则比较复杂,有兴趣的同学可以去看AuthCheckFilter类源码,如果要测试自己的pattern对不对,可以使用org.jivesoftware.admin.AuthCheckFilterTest类执行JUnitTest试试,如自己加上一段:
- assertTrue(AuthCheckFilter.testURLPassesExclude("popplugin/sendmessage", "popplugin*"));
- assertTrue(AuthCheckFilter.testURLPassesExclude("popplugin/sendmessage", "popplugin/sendmessage"));
2)使用AuthCheckFilter.addExclude方法
调用该方法和配置web.xml的效果是完全一样的,个人建议用此方法,因为本方法绿色安全、无污染,不修改和破坏原生openfire代码。
这个方法可以在servlet的init方法或plugin的initializePlugin方法中调用。
个人建议在servlet的init方法中调用,因为servlet的init方法比plugin的initializePlugin方法更早初始化,这个问题在“插件初始化顺序”会讲到。
使用完后记得removeExclude一下,有始有终嘛。
- @Override
- public void init() throws ServletException
- {
- System.out.println("UserMgrServlet init....");
- AuthCheckFilter.addExclude("popplugin/usermgr*");
- AuthCheckFilter.addExclude("popplugin/usermgr/*");
- }
- @Override
- public void destroy()
- {
- System.out.println("UserMgrServlet destroy....");
- // Release the excluded URL
- AuthCheckFilter.removeExclude("popplugin/usermgr*");
- AuthCheckFilter.removeExclude("popplugin/usermgr/*");
- }
注意:这种方式仅限于servlet的访问,如果访问插件的jsp/html页面,页面会报NullPointException异常,解决办法见下面的说明
3.2.servlet仍然需要登录验证或者报NullPointException空指针异常
java.lang.NullPointerException at org.jivesoftware.openfire.admin.decorators.main_jsp._jspService(main_jsp.java:195) at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:97) at javax.servlet.http.HttpServlet.service(HttpServlet.java:820) at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:547) at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:480) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:119) at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:520) at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:227) at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:941) at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:409) at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:186) at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:875) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:117) at org.eclipse.jetty.server.Dispatcher.include(Dispatcher.java:195) at com.opensymphony.module.sitemesh.filter.PageFilter.applyDecorator(PageFilter.java:156) at com.opensymphony.module.sitemesh.filter.PageFilter.doFilter(PageFilter.java:59) at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1330) at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:478) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:119) at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:520) at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:227) at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:941) at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:409) at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:186) at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:875) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:117) at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:250) at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:149) at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:110) at org.eclipse.jetty.server.Server.handle(Server.java:349) at org.eclipse.jetty.server.HttpConnection.handleRequest(HttpConnection.java:441) at org.eclipse.jetty.server.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:919) at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:582) at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:218) at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:51) at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:586) at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:44) at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:598) at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:533) at java.lang.Thread.run(Unknown Source)
插件的页面不使用openfire控制台的页面框架
安装版的openfire的话,修改/openfire/plugins/admin/webapp/WEB-INF/decorators.xml文件
源代码版的,要修改/openfire_src/src/web/WEB-INF/decorators.xml文件
示例:
- <excludes>
- <pattern>/plugins/test/*</pattern>
- <pattern>/setup/setup-completed.jsp*</pattern>
- <pattern>/setup/setup-ldap-server_test.jsp*</pattern>
- <pattern>/setup/setup-ldap-user_test.jsp*</pattern>
- <pattern>/setup/setup-ldap-group_test.jsp*</pattern>
- <pattern>/setup/setup-clearspace-integration_test.jsp*</pattern>
- <pattern>/setup/setup-admin-settings_test.jsp*</pattern>
- <pattern>/login.jsp*</pattern>
- <pattern>/plugin-icon.jsp*</pattern>
- <pattern>/js/jscalendar/i18n.jsp*</pattern>
- </excludes>
这样,插件popplugin目录下的所有路径的jsp页面及js,css等,都能被自由的访问了。
至此,海阔天空任鸟飞了。。