Openfire支持插件开发,使得它的用处大大增加。前阵子,做一个匿名聊天软件,名字叫做“子曰”,再做这个项目之前,了解了许多关于Openfire的知识,得知他支持插件开发,并且能实现所需要的功能,所以最后就决定用它。这个匿名聊天软件最核心的功能就是陌生人配对了。服务器端实现也很简单,用户配对插件思路是这样的:
1. 如果客户端是发来请求配对用户的包并且用户队列中没有用户,将请求的用户的用户名放入用户队列中并向客户端发送等待的包。
2. 如果客户端是发来请求配对用户的包并且用户队列中有其他用户,将其用户名从队列中取出,并给客户端发送包含其他用户名的包。
3. 如果客户端是发来请求配对用户的包并且用户队列中有用户本身,给客户端发送继续等待的包。
4. 如果客户端是发来请求退出配对的包,将其用户名从用户队列中移除。
我们写两个Java源文件
1. MatchUserPlugin.java,插件的入口和插件的所有逻辑判断。
public class MatchUserPlugin implements Plugin {
private IQRouter iqRouter;
private final Logger Log = LoggerFactory.getLogger(MatchUserPlugin.class);
private SimpleDateFormat simpleDateFormat=new SimpleDateFormat("HH:mm:ss.SSS");
SessionManager sessionManager=new SessionManager();
@Override
public void destroyPlugin() {
// TODO Auto-generated method stub
Log.info("------------------->MatchUserPlugin destroy!");
}
/**
* 插件初始化时被调用
*/
@Override
public void initializePlugin(PluginManager pluginManager, File file) {
// TODO Auto-generated method stub
Log.info("------------------->MatchUserPlugin initialize!");
try {
iqRouter = XMPPServer.getInstance().getIQRouter();
iqRouter.addHandler(new MyIQHandler("MyIQHandler"));
} catch (Exception e) {
// TODO: handle exception
Log.info("------------------->Bind Exception:"+e.toString());
}
}
/**
* 处理数据包
* @author Administrator
*
*/
private class MyIQHandler extends IQHandler{
//客户端可能发来的消息
private static final String QUIT_MATCH="quit_match";//退出用户配对
private static final String MATCH_USER="match_user";//请求用户配对
//服务端要发送的消息
private static final String MATCH_WAIT="match_wait";//等待状态
private static final String MATCH_SUCCESS="match_success_server";//匹配成功
private static final String QUIT_MATCH_SUCCESS="quit_match_success";//退出配对成功
private static final String NAMESPACE_INFO="match:iq:info";//iq包的命名控件
private static final String QUIT_CHAT="quit_chat";
private Dao redisDAO;
private IQHandlerInfo info;
//private final Logger log = LoggerFactory.getLogger(MyIQHandler.class);
@Override
public void initialize(XMPPServer server) {
// TODO Auto-generated method stub
super.initialize(server);
}
public MyIQHandler(String moduleName) {
super(moduleName);
redisDAO=Dao.getInstance();
// TODO Auto-generated constructor stub
info=new IQHandlerInfo("info", NAMESPACE_INFO);
}
@Override
public IQHandlerInfo getInfo() {
// TODO Auto-generated method stub
return info;
}
@Override
public IQ handleIQ(IQ packet) throws UnauthorizedException {
// TODO Auto-generated method stub
IQ iq=new IQ();
String childName=packet.getChildElement().getName();
if(childName==null)
return packet;
if(!childName.equals("info"))
{
return packet;
}
IQ replyIQ=IQ.createResultIQ(packet);
ClientSession clientSession=sessionManager.getSession(packet.getFrom());
if(clientSession==null)
{
log("Error user info.Session not found"
+sessionManager.getPreAuthenticatedKeys()
+" from "+packet.getFrom());
replyIQ.setChildElement(packet.getChildElement().createCopy());
replyIQ.setError(PacketError.Condition.forbidden);
return replyIQ;
}
org.dom4j.Element child= packet.getChildElement().createCopy();
String type=child.attributeValue("type");
String ns=child.getNamespaceURI();
if(!ns.equals(NAMESPACE_INFO))
{
log("This namespace is valid !"
+sessionManager.getPreAuthenticatedKeys()
+" from "+packet.getFrom());
replyIQ.setChildElement(packet.getChildElement().createCopy());
replyIQ.setError(PacketError.Condition.bad_request);
return replyIQ;
}
Element infoElement = DocumentHelper.createElement("info");
infoElement.addNamespace("", NAMESPACE_INFO);
switch (type) {
case MATCH_USER:
//log("------------------->MATCH_USER");
String queueJID=redisDAO.lget();
//队列中没有人
if(queueJID==null)
{
//将自己的jid放入队列,并返回等待
try {
redisDAO.push(packet.getFrom().toFullJID());
infoElement.addAttribute("type", MATCH_WAIT);
infoElement.addAttribute("data","nil");
replyIQ.setChildElement(infoElement);
} catch (Exception e) {
// TODO: handle exception
}
}
//队列中有人
else
{
try {
//如果队列只有自己,等待
if(queueJID.equals(packet.getFrom().toFullJID()))
{
infoElement.addAttribute("type", MATCH_WAIT);
infoElement.addAttribute("data","nil");
replyIQ.setChildElement(infoElement);
}
else
{
//从队列中弹出一个元素
queueJID=redisDAO.lpop();
infoElement.addAttribute("type", MATCH_SUCCESS);
infoElement.addAttribute("data",queueJID);
replyIQ.setChildElement(infoElement);
}
} catch (Exception e) {
// TODO: handle exception
}
}
break;
case QUIT_MATCH:
long idx=redisDAO.remove(packet.getFrom().toFullJID());
if(idx!=-1){
infoElement.addAttribute("type", QUIT_MATCH_SUCCESS);
infoElement.addAttribute("data","nil");
replyIQ.setChildElement(infoElement);
}
break;
case QUIT_CHAT:
return packet;
default:
return packet;
}//switch
return replyIQ;
}
}//MyIQHandler
public void log(String text)
{
Log.info(text);
}
}
2. Dao.java,Redis数据库操作类,并模拟一个用户队列,依赖Jedis.jar.
public class Dao {
private static Dao dao;
private static Jedis jRedis;
private final static String KEY="UserQueue";//Redis数据库中的键名
private final static String LOCAL_HOST="127.0.0.1";//Redis数据库ip
private final Logger Log = LoggerFactory.getLogger(Dao.class);
public static void main(String[] args) {
// TODO Auto-generated method stub
Dao dao=Dao.getInstance();
}
/**
*
* @return
*/
public static Dao getInstance()
{
if(dao==null)
{
dao = new Dao();
}
return dao;
}
/**
* 移除指定元素
*/
public Long remove(String value)
{
return jRedis.lrem(KEY, 1, value);
}
/**
* 弹出最左边的数据
* @return
*/
public String lpop() {
// TODO Auto-generated method stub
return jRedis.lpop(KEY);
}
/**
* 获取最左边的数据
* @return
*/
public String lget() {
// TODO Auto-generated method stub
return jRedis.lindex(KEY, jRedis.llen(KEY)-1);
}
/**
* 将元素放入队列尾
* @param jid
*/
public void push(String jid) {
// TODO Auto-generated method stub
jRedis.rpush(KEY, jid);
}
private Dao(){
if(jRedis==null)
{
jRedis=new Jedis(LOCAL_HOST);
}
String ping=jRedis.ping();
Log.info("---------->"+ping);
}
}
完整代码:https://github.com/luoyesiqiu/MatchUserPlugin