(转)关于将XMPP server部署到Tomcat上的一些问题

[list=1]
[*]在XMPP消息推送这个问题上,网上已经有很多资料了,本人觉得很好的一篇资料是:[url=http://www.iteye.com/topic/1117043]http://www.iteye.com/topic/1117043[/url]

[*]提供了一个连接下载源码:[url=http://115.com/file/bhkfse3i#%20Androidpn.rar]http://115.com/file/bhkfse3i#%20Androidpn.rar[/url]

[*]很感谢前辈们的研究结果。

[*]在源码的使用过程中要注意的地方有两点,网上的那篇资料好像忽略了一个重要的地方,就是要改resources文件夹下面的jdbc.properties,将里面关于数据库的配置改为自己的,另一个需要注意的地方就是改android端的ip了。

[/list]

在项目部署到tomcat下之后,发现了不少的bug,其中一个就是当tomcat重新启动,客户端的连接将断开,不能进行自动重连。

对于这个BUG,我们可以在Androidpn-clieng下的XmppManager这个类中做简要的处理即可修改。源码如下:


/*
* Copyright (C) 2010 Moduad Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.androidpn.client;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Future;

import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketIDFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Registration;
import org.jivesoftware.smack.provider.ProviderManager;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Handler;
import android.util.Log;


/**
* This class is to manage the XMPP connection between client and server.
*
* @author Sehwan Noh (devnoh@gmail.com)
*/
public class XmppManager {

private static final String LOGTAG = LogUtil.makeLogTag(XmppManager.class);

private static final String XMPP_RESOURCE_NAME = "AndroidpnClient";

private Context context;

private NotificationService.TaskSubmitter taskSubmitter;

private NotificationService.TaskTracker taskTracker;

private SharedPreferences sharedPrefs;

private String xmppHost;

private int xmppPort;

private XMPPConnection connection;

private String username;

private String password;

private ConnectionListener connectionListener;

private PacketListener notificationPacketListener;

private Handler handler;

private List<Runnable> taskList;

private boolean running = false;

private Future<?> futureTask;

private Thread reconnection;

public XmppManager(NotificationService notificationService) {
context = notificationService;
taskSubmitter = notificationService.getTaskSubmitter();
taskTracker = notificationService.getTaskTracker();
sharedPrefs = notificationService.getSharedPreferences();

xmppHost = sharedPrefs.getString(Constants.XMPP_HOST, "localhost");
xmppPort = sharedPrefs.getInt(Constants.XMPP_PORT, 5222);
username = sharedPrefs.getString(Constants.XMPP_USERNAME, "");
password = sharedPrefs.getString(Constants.XMPP_PASSWORD, "");

connectionListener = new PersistentConnectionListener(this);
notificationPacketListener = new NotificationPacketListener(this);

handler = new Handler();
taskList = new ArrayList<Runnable>();
reconnection = new ReconnectionThread(this);
}

public Context getContext() {
return context;
}

public void connect() {
Log.d(LOGTAG, "connect()...");
submitLoginTask();
}

public void disconnect() {
Log.d(LOGTAG, "disconnect()...");
terminatePersistentConnection();
}

public void terminatePersistentConnection() {
Log.d(LOGTAG, "terminatePersistentConnection()...");
Runnable runnable = new Runnable() {

final XmppManager xmppManager = XmppManager.this;

public void run() {
if (xmppManager.isConnected()) {
Log.d(LOGTAG, "terminatePersistentConnection()... run()");
xmppManager.getConnection().removePacketListener(
xmppManager.getNotificationPacketListener());
xmppManager.getConnection().disconnect();
}
xmppManager.runTask();
}

};
addTask(runnable);
}

public XMPPConnection getConnection() {
return connection;
}

public void setConnection(XMPPConnection connection) {
this.connection = connection;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public ConnectionListener getConnectionListener() {
return connectionListener;
}

public PacketListener getNotificationPacketListener() {
return notificationPacketListener;
}

public void startReconnectionThread() {
synchronized (reconnection) {
if (!reconnection.isAlive()) {
reconnection.setName("Xmpp Reconnection Thread");
reconnection.start();
}
}
}

public Handler getHandler() {
return handler;
}

public void reregisterAccount() {
removeAccount();
submitLoginTask();
runTask();
}

public List<Runnable> getTaskList() {
return taskList;
}

public Future<?> getFutureTask() {
return futureTask;
}

public void runTask() {
Log.d(LOGTAG, "runTask()...");
synchronized (taskList) {
running = false;
futureTask = null;
if (!taskList.isEmpty()) {
Runnable runnable = (Runnable) taskList.get(0);
taskList.remove(0);
running = true;
futureTask = taskSubmitter.submit(runnable);
if (futureTask == null) {
taskTracker.decrease();
}
}
}
taskTracker.decrease();
Log.d(LOGTAG, "runTask()...done");
}

private String newRandomUUID() {
String uuidRaw = UUID.randomUUID().toString();
return uuidRaw.replaceAll("-", "");
}

private boolean isConnected() {
return connection != null && connection.isConnected();
}

private boolean isAuthenticated() {
return connection != null && connection.isConnected()
&& connection.isAuthenticated();
}

private boolean isRegistered() {
return sharedPrefs.contains(Constants.XMPP_USERNAME)
&& sharedPrefs.contains(Constants.XMPP_PASSWORD);
}

private void submitConnectTask() {
Log.d(LOGTAG, "submitConnectTask()...");
addTask(new ConnectTask());
}

private void submitRegisterTask() {
Log.d(LOGTAG, "submitRegisterTask()...");
submitConnectTask();
addTask(new RegisterTask());
}

private void submitLoginTask() {
Log.d(LOGTAG, "submitLoginTask()...");
submitRegisterTask();
addTask(new LoginTask());
}

private void addTask(Runnable runnable) {
Log.d(LOGTAG, "addTask(runnable)...");
taskTracker.increase();
synchronized (taskList) {
if (taskList.isEmpty() && !running) {
running = true;
futureTask = taskSubmitter.submit(runnable);
if (futureTask == null) {
taskTracker.decrease();
}
} else {
taskList.add(runnable);
}
}
Log.d(LOGTAG, "addTask(runnable)... done");
}

private void removeAccount() {
Editor editor = sharedPrefs.edit();
editor.remove(Constants.XMPP_USERNAME);
editor.remove(Constants.XMPP_PASSWORD);
editor.commit();
}

/**
* A runnable task to connect the server.
*/
private class ConnectTask implements Runnable {

final XmppManager xmppManager;

private ConnectTask() {
this.xmppManager = XmppManager.this;
}

public void run() {
Log.i(LOGTAG, "ConnectTask.run()...");

if (!xmppManager.isConnected()) {
// Create the configuration for this new connection
ConnectionConfiguration connConfig = new ConnectionConfiguration(
xmppHost, xmppPort);
// connConfig.setSecurityMode(SecurityMode.disabled);
connConfig.setSecurityMode(SecurityMode.required);
connConfig.setSASLAuthenticationEnabled(false);
connConfig.setCompressionEnabled(false);

XMPPConnection connection = new XMPPConnection(connConfig);
xmppManager.setConnection(connection);

try {
// Connect to the server
connection.connect();
Log.i(LOGTAG, "XMPP connected successfully");

// packet provider
ProviderManager.getInstance().addIQProvider("notification",
"androidpn:iq:notification",
new NotificationIQProvider());

} catch (XMPPException e) {
Log.e(LOGTAG, "XMPP connection failed", e);
}

xmppManager.runTask();

} else {
Log.i(LOGTAG, "XMPP connected already");
xmppManager.runTask();
}
}
}

/**
* A runnable task to register a new user onto the server.
*/
private class RegisterTask implements Runnable {

final XmppManager xmppManager;

private RegisterTask() {
xmppManager = XmppManager.this;
}

public void run() {
Log.i(LOGTAG, "RegisterTask.run()...");

//如果账号不存在的话,随机生成一个uuid的用户名和mima
if (!xmppManager.isRegistered()) {
final String newUsername = newRandomUUID();
final String newPassword = newRandomUUID();
// final String newUsername = "af100042487d4b06a49adda8c3a82d41";
// final String newPassword = "af100042487d4b06a49adda8c3a82d41";

Registration registration = new Registration();

PacketFilter packetFilter = new AndFilter(new PacketIDFilter(
registration.getPacketID()), new PacketTypeFilter(
IQ.class));

PacketListener packetListener = new PacketListener() {

public void processPacket(Packet packet) {
Log.d("RegisterTask.PacketListener",
"processPacket().....");
Log.d("RegisterTask.PacketListener", "packet="
+ packet.toXML());

if (packet instanceof IQ) {
IQ response = (IQ) packet;
if (response.getType() == IQ.Type.ERROR) {
if (!response.getError().toString().contains(
"409")) {
Log.e(LOGTAG,
"Unknown error while registering XMPP account! "
+ response.getError()
.getCondition());
}
} else if (response.getType() == IQ.Type.RESULT) {
xmppManager.setUsername(newUsername);
xmppManager.setPassword(newPassword);
Log.d(LOGTAG, "username=" + newUsername);
Log.d(LOGTAG, "password=" + newPassword);

Editor editor = sharedPrefs.edit();
editor.putString(Constants.XMPP_USERNAME,
newUsername);
editor.putString(Constants.XMPP_PASSWORD,
newPassword);
editor.commit();
Log
.i(LOGTAG,
"Account registered successfully");
xmppManager.runTask();
}
}
}
};

connection.addPacketListener(packetListener, packetFilter);

registration.setType(IQ.Type.SET);
// registration.setTo(xmppHost);
// Map<String, String> attributes = new HashMap<String, String>();
// attributes.put("username", rUsername);
// attributes.put("password", rPassword);
// registration.setAttributes(attributes);
registration.addAttribute("username", newUsername);
registration.addAttribute("password", newPassword);
connection.sendPacket(registration);

} else {
Log.i(LOGTAG, "Account registered already");
xmppManager.runTask();
}
}
}

/**
* A runnable task to log into the server.
*/
private class LoginTask implements Runnable {

final XmppManager xmppManager;

private LoginTask() {
this.xmppManager = XmppManager.this;
}

public void run() {
Log.i(LOGTAG, "LoginTask.run()...");

if (!xmppManager.isAuthenticated()) {
Log.d(LOGTAG, "username=" + username);
Log.d(LOGTAG, "password=" + password);

try {
xmppManager.getConnection().login(
xmppManager.getUsername(),
xmppManager.getPassword(), XMPP_RESOURCE_NAME);
Log.d(LOGTAG, "Loggedn in successfully");

// connection listener
if (xmppManager.getConnectionListener() != null) {
xmppManager.getConnection().addConnectionListener(
xmppManager.getConnectionListener());
}

// packet filter
PacketFilter packetFilter = new PacketTypeFilter(
NotificationIQ.class);
// packet listener
PacketListener packetListener = xmppManager
.getNotificationPacketListener();
connection.addPacketListener(packetListener, packetFilter);
//判断是否处于连接状态(添加)
if(!getConnection().isConnected())
{
xmppManager.runTask();
}
xmppManager.runTask();
} catch (XMPPException e) {
Log.e(LOGTAG, "LoginTask.run()... xmpp error");
Log.e(LOGTAG, "Failed to login to xmpp server. Caused by: "
+ e.getMessage());
String INVALID_CREDENTIALS_ERROR_CODE = "401";
String errorMessage = e.getMessage();
if (errorMessage != null
&& errorMessage
.contains(INVALID_CREDENTIALS_ERROR_CODE)) {
xmppManager.reregisterAccount();
return;
}
xmppManager.startReconnectionThread();

} catch (Exception e) {
Log.e(LOGTAG, "LoginTask.run()... other error");
Log.e(LOGTAG, "Failed to login to xmpp server. Caused by: "
+ e.getMessage());
xmppManager.startReconnectionThread();
}
//添加
xmppManager.runTask();
} else {
Log.i(LOGTAG, "Logged in already");
xmppManager.runTask();
}

}
}

}


新添加代码450-454行和477行

还有一个问题是:当客户端的用户有不在线的时候,消息应怎么进行推送,是直接忽略呢还是下次登录的时候在进行推送,想qq那样,很显然对已一个具体的实用项目来说是不能忽略的,那么怎么进行消息的离线推送呢,下次告诉大家,因为我现在还没解决,不过快了。和大家说下我的思路吧:在androidpn服务端有一个UserController这个类,源码如下:


View Code
/*
* Copyright (C) 2010 Moduad Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.androidpn.server.console.controller;

import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.androidpn.server.model.User;
import org.androidpn.server.service.ServiceLocator;
import org.androidpn.server.service.UserService;
import org.androidpn.server.xmpp.presence.PresenceManager;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;

/**
* A controller class to process the user related requests.
*
* @author Sehwan Noh (devnoh@gmail.com)
*/
public class UserController extends MultiActionController {

private UserService userService;

public UserController() {
userService = ServiceLocator.getUserService();
}

//用户列表
public ModelAndView list(HttpServletRequest request,
HttpServletResponse response) throws Exception {
PresenceManager presenceManager = new PresenceManager();
List<User> userList = userService.getUsers();
for (User user : userList) {
if (presenceManager.isAvailable(user)) {
// Presence presence = presenceManager.getPresence(user);
user.setOnline(true);
} else {
user.setOnline(false);
}
// logger.debug("user.online=" + user.isOnline());
}
ModelAndView mav = new ModelAndView();
mav.addObject("userList", userList);
mav.setViewName("user/list");
return mav;
}

}


该源码里面有用户是否在线的判断,我们只要将用户的列表取出来,如果用户在线就将消息进行推送,如果有不在线的用户,我们就把该消息放到缓存中(也可以放到数据库中更加保险),当然为了防止用户过长没有登陆系统,导致下次登录时出现过多托送过来的消息,我们还可以在服务端进行设置时间,比如服务端只缓存近N天的消息,利用


session.getCreationDate();
session.getLastActiveDate();


这两句代码应该可以完成,本人没尝试过,还不知道。效果如如下:

[img]http://dl.iteye.com/upload/attachment/0076/0276/48f4ee51-3b44-31fe-bd92-8c3ed670d7b4.bmp[/img]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值