最近在做openfire的ios推送插件,下面介绍下openfire的插件开发
1、 因为在很多使用openfire的过程中,需要更改openfire默认的一些行为,这就涉及到插件的开发。这里我也是写一个HelloWorld的入门案例。
2、案例插件的功能
这个插件很简单,就是在openfire Server启动时,和关闭时,在控制台打印出消息。
3、插件开发的目录结构设计
先来看一下当前openfire在eclipse中的目录结构:
目录太长,我截取一部分,现在,我们开始写插件。在\openfire_src\src\plugins目录下新建一个helloWorld的文件夹,然后在helloWorld目录下新建一个src的文件夹,放页面和源文件,再新建一个lib目录放第三方的jar包,然后在src文件夹下面新建web,java两个文件夹,web下面放置页面,在这个案例没有用到,可以不建,java文件夹下面放置java源文件,现在目录结构如下:
changelog.html、plugin.xml、readme.html这三个文件分别是你的插件修改日志文件,插件文件和自述文件,其中plugin.xml这个文件很重要,后面我还要解释,先空着,logo_large.gif和logo_small.gif是插件的logo文件,我随便拷了两个。各位,做好上面的步骤以后,注意了,跟openfire自带插件的目录结构不一样,细心的朋友可能注意到了,我新建的src下面有个java目录,而openfire自带插件则没有,而是跑到上面去了,如下:
不要着急,做完这一步,我们的插件就跟openfire自带插件一样了,步骤截图如下:
这时,我们看到,src目录下的java目录不见了,而在上面的源码目录有了我们自己插件了,截图如下:
好了,我们先建一个包,如下:
输入包名:
现在包建好了,我们在这个包中建一个java文件,名为:HelloWorldPlugin,我就不截图,这个学过java的人就应该知道,内容如下:
- package com.helloworld;
- import java.io.File;
- import org.jivesoftware.openfire.XMPPServer;
- import org.jivesoftware.openfire.container.Plugin;
- import org.jivesoftware.openfire.container.PluginManager;
- public class HelloWorldPlugin implements Plugin {
- private XMPPServer server;
- public HelloWorldPlugin() {
- }
- @Override
- public void initializePlugin(PluginManager manager, File pluginDirectory) {
- server = XMPPServer.getInstance();
- System.out.println("HelloWorldPlugin----start");
- System.out.println(server.getServerInfo());
- }
- @Override
- public void destroyPlugin() {
- System.out.println("HelloWorldPlugin----destroy");
- }
- }
内容很简单,就是在openfire启动和关闭时,在控制台打印出一条消息。保存好了,我们的java源文件就写好了,现在我们该来写plugin.xml文件了,内容如下:
- <?xml version="1.0" encoding="UTF-8"?>
- <plugin>
- <class>com.helloworld.HelloWorldPlugin</class>
- <name>helloWorld</name>
- <description>First Openfire Custom Plugin.</description>
- <author>xieyuan</author>
- <version>1.0.0</version>
- <date>14/07/2014</date>
- <minServerVersion>3.9.0</minServerVersion>
- <adminconsole>
- </adminconsole>
- </plugin>
以及注意上面的class的配置,那个配置是最为重要的,配置的是插件的全路径;name是插件的名称,安装后的插件名称;author是插件作者;adminconsole是配置插件关联的页面的;这里不需要。
4、编译插件
展开\openfire_src\build目录,我们发现有一个build.properties.template文件,我们将其重命名为:build.properties,在这个build.properties中加上一行:plugin=helloWorld,截图如下:
使用ant编译插件,截图操作如下:
在弹出的target中选择build one plugin,点击Apply,Run:
之后,我们在控制台上看到:
构建成功,我们在相应的目录下,可以看到,生成的插件包:helloWorld.jar
现在我们来运行我们的插件,看在控制台上能不能打印相应的信息,启动openfire,我们看到在控制台上一句:
好了,大功告成,网上有很多人问,怎么调试插件,这不很简单,直接debug openfire不就行了:
以上是开发一个简单的openfire的示例,下面贴出ios推送插件代码,新建一个插件offlinemsg;
package org.jivesoftware.openfire.plugin;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.security.KeyStore;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Pattern;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.openfire.PresenceManager;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.interceptor.InterceptorManager;
import org.jivesoftware.openfire.interceptor.PacketInterceptor;
import org.jivesoftware.openfire.interceptor.PacketRejectedException;
import org.jivesoftware.openfire.session.Session;
import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
import org.xmpp.packet.Packet;
import org.xmpp.packet.Presence;
public class OfflineMsg implements PacketInterceptor, Plugin {
private static final Logger log = LoggerFactory.getLogger(OfflineMsg.class);
//Hook for intercpetorn
private InterceptorManager interceptorManager;
private static PluginManager pluginManager;
private UserManager userManager;
private PresenceManager presenceManager;
public OfflineMsg() {
}
public void debug(String str){
if( true ){
// System.out.println(str);
}
}
public void initializePlugin(PluginManager manager, File pluginDirectory) {
interceptorManager = InterceptorManager.getInstance();
interceptorManager.addInterceptor(this);
XMPPServer server = XMPPServer.getInstance();
userManager = server.getUserManager();
presenceManager = server.getPresenceManager();
pluginManager = manager;
this.debug("start offline 1640");
}
public void destroyPlugin() {
this.debug("start offline 1640");
}
/**
* intercept message
*/
@Override
public void interceptPacket(Packet packet, Session session, boolean incoming, boolean processed) throws PacketRejectedException {
JID recipient = packet.getTo();
if (recipient != null) {
String username = recipient.getNode();
// if broadcast message or user is not exist
if (username == null || !UserManager.getInstance().isRegisteredUser(recipient)) {
return;
} else if (!XMPPServer.getInstance().getServerInfo().getXMPPDomain().equals(recipient.getDomain())) {
//not from the same domain
return;
} else if ("".equals(recipient.getResource())) {
}
}
this.doAction(packet, incoming, processed, session);
}
/**
* <b>send offline msg from this function </b>
*/
private void doAction(Packet packet, boolean incoming, boolean processed, Session session) {
Packet copyPacket = packet.createCopy();
if (packet instanceof Message) {
Message message = (Message) copyPacket;
if (message.getType() == Message.Type.chat) {
if (processed || !incoming) {
return;
}
Message sendmessage = (Message) packet;
String content= sendmessage.getBody();
JID recipient = sendmessage.getTo();
//get message
try
{
if (recipient.getNode() == null ||
!UserManager.getInstance().isRegisteredUser(recipient.getNode())) {
// Sender is requesting presence information of an anonymous user
throw new UserNotFoundException("Username is null");
}
Presence status=presenceManager.getPresence(userManager.getUser(recipient.getNode()));
if( status!=null ){
this.debug(recipient.getNode()+" online111"+",message: "+content);
}else{ //这里是离线状态,在这里加上自己的java推送代码
this.debug(recipient.getNode()+" offline111"+",message: "+content);
//这里是解析openfire发送消息的内容,我经过了json封装
JSONObject dataJson=new JSONObject(content);
int type=dataJson.getInt("type");
String fromUserId=dataJson.getString("fromUserId");
String msg="";
if(type==1)
msg=dataJson.getString("content");
this.sendMsg(recipient.getNode(),msg,type,fromUserId);
/*
* add your code here to send offline msg
* recipient.getNode() : receive's id,for example,if receive's jid is "23@localhost", receive's id is "23"
* content: message content
*/
}//end if
}
catch (UserNotFoundException e) {
this.debug("exceptoin "+recipient.getNode()+" not find"+",full jid: "+recipient.toFullJID());
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else if (message.getType() == Message.Type.groupchat) {
List<?> els = message.getElement().elements("x");
if (els != null && !els.isEmpty()) {
} else {
}
} else {
}
} else if (packet instanceof IQ) {
IQ iq = (IQ) copyPacket;
if (iq.getType() == IQ.Type.set && iq.getChildElement() != null && "session".equals(iq.getChildElement().getName())) {
}
} else if (packet instanceof Presence) {
Presence presence = (Presence) copyPacket;
if (presence.getType() == Presence.Type.unavailable) {
}
}
}
public void sendMsg(String iid,String msg,int type,String fromUserId) {
String keyPath = "/opt/push.p12"; //我是linux服务器,证书存放目录
String ksType = "PKCS12";
String ksPassword = "123456"; 证书密码
String ksAlgorithm = "SunX509";
//数据库查询用户对应的推送标识已经发送者的用户名
HashMap<String,String> map=this.getDeviceToken(iid, fromUserId);
String deviceToken=map.get("deviceToken");
String userName=map.get("userName");
if(deviceToken!=null && !deviceToken.equals("")){
String serverHost = "gateway.sandbox.push.apple.com";
int serverPort = 2195;
try {
InputStream certInput = new FileInputStream(keyPath);
KeyStore keyStore = KeyStore.getInstance(ksType);
keyStore.load(certInput, ksPassword.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(ksAlgorithm);
kmf.init(keyStore, ksPassword.toCharArray());
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), null, null);
SSLSocketFactory socketFactory = sslContext.getSocketFactory();
Socket socket = socketFactory.createSocket(serverHost, serverPort);
StringBuilder content = new StringBuilder();
String text="";
if(type==1){
text = userName+":"+msg;
}else{
text= userName+"发来一段语音";
}
content.append("{\"aps\":");
content.append("{\"alert\":\"").append(text)
.append("\",\"badge\":1,\"sound\":\"")
.append("ping1").append("\"}");
//content.append(",\"cpn\":{\"t0\":")
// .append(System.currentTimeMillis()).append("}");
content.append(",\"fromUserId\":\""+fromUserId+"\"");
content.append(",\"type\":\"chat\"");
content.append("}");
byte[] msgByte = makebyte((byte)1, deviceToken, content.toString(), 10000001);
System.out.println(msgByte);
socket.getOutputStream().write(msgByte);
socket.getOutputStream().flush();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* ◊È◊∞apnsπÊ∂®µƒ◊÷Ω⁄ ˝◊È π”√‘ˆ«ø–Õ
*
* @param command
* @param deviceToken
* @param payload
* @return
* @throws IOException
*/
private static byte[] makebyte(byte command, String deviceToken, String payload, int identifer) {
byte[] deviceTokenb = decodeHex(deviceToken);
byte[] payloadBytes = null;
ByteArrayOutputStream boas = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(boas);
try {
payloadBytes = payload.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
try {
dos.writeByte(command);
dos.writeInt(identifer);//identifer
dos.writeInt(Integer.MAX_VALUE);
dos.writeShort(deviceTokenb.length);
dos.write(deviceTokenb);
dos.writeShort(payloadBytes.length);
dos.write(payloadBytes);
return boas.toByteArray();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private static final Pattern pattern = Pattern.compile("[ -]");
private static byte[] decodeHex(String deviceToken) {
String hex = pattern.matcher(deviceToken).replaceAll("");
byte[] bts = new byte[hex.length() / 2];
for (int i = 0; i < bts.length; i++) {
bts[i] = (byte) (charval(hex.charAt(2*i)) * 16 + charval(hex.charAt(2*i + 1)));
}
return bts;
}
private static int charval(char a) {
if ('0' <= a && a <= '9')
return (a - '0');
else if ('a' <= a && a <= 'f')
return (a - 'a') + 10;
else if ('A' <= a && a <= 'F')
return (a - 'A') + 10;
else{
throw new RuntimeException("Invalid hex character: " + a);
}
}
public HashMap<String, String> getDeviceToken(String userId,String fromUserId) {
String deviceToken = "";
String userName = "allen";
HashMap<String, String> map=new HashMap<String, String>();
Connection con = null;
String url = "jdbc:mysql://localhost:3306/test";
String user = "test";
String password = "123456";
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection(url, user, password);;
pstmt = con.prepareStatement("SELECT sign FROM wood_push where user = ?");
pstmt.setString(1, userId);
rs = pstmt.executeQuery();
if (rs.next()) {
deviceToken = rs.getString(1);
}
pstmt = con.prepareStatement("SELECT username,nickname FROM wood_member where id = ?");
pstmt.setString(1, fromUserId);
rs = pstmt.executeQuery();
if (rs.next()) {
String username=rs.getString(1);
String nickname = rs.getString(2);
userName=(nickname!=null && !nickname.equals(""))?nickname:username;
}
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
DbConnectionManager.closeConnection(rs, pstmt, con);
}
map.put("deviceToken", deviceToken);
map.put("userName", userName);
return map;
}
}
注意,刚开始的时候直接运行plugin会报错,这时可以先运行plugins,把全部的插件编译一遍,然后再执行plugin就不会有错了,编译完成后得到offlinemsg.jar 在openfire后台上传至插件即可,上传后最好重启openfire
:/etc/init.d/openfire restart 这时再发送消息就可以收到推送了。java如何推送请看我之前的博客。
附件里给出了完整的插件目录
相关推荐
标题中的“openfire插件开发环境搭建”是指学习和配置Openfire服务器以开发自定义插件的过程。Openfire是一款开源的即时通讯服务器,基于XMPP(Extensible Messaging and Presence Protocol)协议,允许用户进行实时...
OpenFire插件开发系列的第一部分主要涉及如何搭建OpenFire的二次开发环境,为后续的插件开发工作做好准备。OpenFire是一款开源的即时通讯服务器,它基于Java技术,支持XMPP协议,允许开发者通过编写插件来扩展其功能...
在这个“Openfire插件开发 访问jsp servlet”项目中,我们将探讨如何在Openfire环境中开发一个插件,同时利用JavaServer Pages(JSP)和Servlet技术来处理HTTP请求。 首先,让我们理解Openfire插件开发的基本流程:...
本篇文章将深入探讨OpenFire插件开发,旨在帮助开发者从入门到精通,实现移动互联聊天服务器的高效搭建。 首先,了解OpenFire的基本架构至关重要。OpenFire基于XMPP协议,该协议是互联网工程任务组(IETF)定义的一...
以下是对Openfire插件开发核心概念的详细解释: 1. **插件结构**: 插件的基本结构包括一个包含所有必要组件的文件夹。其中,`plugin.xml`是定义插件的关键文件,`readme.html`和`changelog.html`是供用户查看的...
文档中提到了1.5和1.6版本的JDK,这两个版本都是较早的版本,但足以应对Openfire插件开发的需求。Openfire插件开发时可能要根据使用的JDK版本调整代码,以确保兼容性。 在数据库方面,文档提到了MySQL 5.0版本,这...
总的来说,Openfire插件开发涉及到Java编程、数据库设计、XML配置以及Web服务的构建。开发者需要熟悉XMPP协议、Java编程和Web开发的相关知识,才能有效地利用Openfire的API进行插件开发。而部署源码则涉及到构建流程...
总结来说,Openfire 插件开发的关键在于理解 `plugin.xml` 文件的结构和内容,以及如何组织和编写 Java 类以实现所需功能。通过熟练掌握这些知识,开发者可以轻松地扩展 Openfire 的功能,满足特定的业务需求。从...
总的来说,Openfire插件开发涉及Java编程、XML配置、服务器端API的使用等多个方面,是一个综合性的技术实践。通过不断学习和实践,你将能够创建出满足特定需求的Openfire插件,丰富和拓展Openfire的功能。
综上所述,XMPP协议及其服务器端的Openfire插件开发是构建现代即时通讯系统的重要组成部分,通过XML的灵活性和标准化的优势,它为开发者提供了强大的工具,以创建高度定制化且与各种平台兼容的通信解决方案。
目的:主要是为了监控Openfire各类message,然后对message body做进一步...局限:因为时间有限,本插件并没有对Openfire message协议进行扩展,都是基于原生的xml协议,因此后续开发还需要进行更加深入的二次开发操作。
通过这个简单的教程,初学者可以快速上手Openfire插件开发,理解其核心原理,并逐渐掌握更复杂的插件功能。随着经验的积累,你将能够开发出满足各种需求的Openfire插件,丰富和增强Openfire服务器的功能。
openfire第一个插件开发以及源码说明,QQ群:28588322
JMS(Java Message Service)可能是用于实现异步通信或消息队列的组件,而Spark是一个基于XMPP的桌面聊天客户端,也可能与Openfire插件开发有关。 总之,Openfire插件开发需要开发者具备Java编程基础、XML配置知识...
本示例将带你走进Openfire插件开发的世界,通过一个简单的Servlet插件来介绍开发流程。 首先,我们需要理解Openfire插件的基本结构。一个Openfire插件通常包含以下几个部分: 1. **Plugin.java**:这是插件的核心...
在"openfire_plugin"这个压缩包中,我们很可能会找到一系列与Openfire插件开发相关的源代码文件。这些文件通常包含以下几个部分: 1. **主类(Main Class)**:这是插件的核心部分,负责加载和初始化插件。它继承自...
**一、Openfire插件开发环境搭建** 在开始开发之前,我们需要搭建Openfire源码环境。首先,你需要在Openfire官网(https://www.igniterealtime.org/projects/openfire/)下载源码。解压后,利用Eclipse或IntelliJ ...
Openfire 安装配置和插件开发详解 Openfire 是一个基于 XMPP 协议的即时通信服务器,可以实现实时的聊天服务。在本文中,我们将详细介绍 Openfire 的安装和配置过程,并探讨如何使用 Eclipse 进行插件开发。 一、...