真正解决了服务器重启客户端无法连接的bug
android端修改XmppManager这个类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.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
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 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();
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 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()...");
if (!xmppManager.isRegistered()) {
final String newUsername = newRandomUUID();
final String newPassword = newRandomUUID();
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 attributes = new HashMap();
// 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();
}
}
}
}
新添加代码344-348行和369行
1.运行客户端代码,需要注意的是把androidpn.properties中的xmppHost改为你的服务器ip地址
2.服务端 jdbc.properties修改mysql数据库连接
项目部署(服务器)
首先,我单独启动一个新的tomcat,将server端部署到这下面,修改配置文件config。properties,jdbc.properties。jdbc.properties这个文件是配置你本地数据库的参数的,不能有错。启动后,简单测试成功,在自己项目中使用http协议将数据POST到server端这个org.androidpn.server.console.controller.NotificationController类的send方法中。具体参数名称及获取参数的代码,可以修改server端。基本上,写到这里的话,应该可以满足项目需求了。如果你还需要将哪些用户在线的功能整合到你自己项目中。那你就得自己跟踪下代码了。
apiKey默认1234567890为了安全在config.properties里修改
NotificationController.java代码/*
* 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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.androidpn.server.util.Config;
import org.androidpn.server.xmpp.push.NotificationManager;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
/**
* A controller class to process the notification related requests.
*
* @author Sehwan Noh (devnoh@gmail.com)
*/
public class NotificationController extends MultiActionController {
private NotificationManager notificationManager;
public NotificationController() {
notificationManager = new NotificationManager();
}
public ModelAndView list(HttpServletRequest request,
HttpServletResponse response) throws Exception {
ModelAndView mav = new ModelAndView();
// mav.addObject("list", null);
mav.setViewName("notification/form");
return mav;
}
public ModelAndView send(HttpServletRequest request,
HttpServletResponse response) throws Exception {
String broadcast = ServletRequestUtils.getStringParameter(request,
"broadcast", "Y");
String username = ServletRequestUtils.getStringParameter(request,
"username");
String title = ServletRequestUtils.getStringParameter(request, "title");
String message = ServletRequestUtils.getStringParameter(request,
"message");
String uri = ServletRequestUtils.getStringParameter(request, "uri");
String apiKey = Config.getString("apiKey", "");
logger.debug("apiKey=" + apiKey);
if (broadcast.equalsIgnoreCase("Y")) {
notificationManager.sendBroadcast(apiKey, title, message, uri);
} else {
notificationManager.sendNotifcationToUser(apiKey, username, title,
message, uri);
}
ModelAndView mav = new ModelAndView();
mav.setViewName("redirect:notification.do");
return mav;
}
}
参考博文:
真正解决了服务器重启客户端无法连接的bug
源码分析
都是用myeclipse构建的
只有tomcat
jetty和tomcat
myeclipse发布对应关系