最近在做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文件了,内容如下:
-
com.helloworld.HelloWorldPlugin -
helloWorld -
First Openfire Custom Plugin. -
xieyuan -
1.0.0 -
14/07/2014 -
3.9.0 -
以及注意上面的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);
}
/**
* send offline msg from this function
*/
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 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 deviceToken = "";
String userName = "allen";
HashMap
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如何推送请看我之前的博客。
附件里给出了完整的插件目录