|  | 
 
| 在项目部署到tomcat下之后,发现了不少的bug,其中一个就是当tomcat重新启动,客户端的连接将断开,不能进行自动重连。
 在XMPP消息推送这个问题上,网上已经有很多资料了,本人觉得很好的一篇资料是:http://www.iteye.com/topic/1117043
提供了一个连接下载源码:http://115.com/file/bhkfse3i#%20Androidpn.rar
很感谢前辈们的研究结果。
在源码的使用过程中要注意的地方有两点,网上的那篇资料好像忽略了一个重要的地方,就是要改resources文件夹下面的jdbc.properties,将里面关于数据库的配置改为自己的,另一个需要注意的地方就是改android端的ip了。
 对于这个BUG,我们可以在Androidpn-clieng下的XmppManager这个类中做简要的处理即可修改。源码如下:
 
 
 
 1 /*
 2  * Copyright (C) 2010 Moduad Co., Ltd.
 3  *
 4  * Licensed under the Apache License, Version 2.0 (the "License");
 5  * you may not use this file except in compliance with the License.
 6  * You may obtain a copy of the License at
 7  *
 8  *      http://www.apache.org/licenses/LICENSE-2.0
 9  *
 10  * Unless required by applicable law or agreed to in writing, software
 11  * distributed under the License is distributed on an "AS IS" BASIS,
 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  * See the License for the specific language governing permissions and
 14  * limitations under the License.
 15  */
 16 package org.androidpn.client;
 17
 18 import java.util.ArrayList;
 19 import java.util.List;
 20 import java.util.UUID;
 21 import java.util.concurrent.Future;
 22
 23 import org.jivesoftware.smack.ConnectionConfiguration;
 24 import org.jivesoftware.smack.ConnectionListener;
 25 import org.jivesoftware.smack.PacketListener;
 26 import org.jivesoftware.smack.XMPPConnection;
 27 import org.jivesoftware.smack.XMPPException;
 28 import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
 29 import org.jivesoftware.smack.filter.AndFilter;
 30 import org.jivesoftware.smack.filter.PacketFilter;
 31 import org.jivesoftware.smack.filter.PacketIDFilter;
 32 import org.jivesoftware.smack.filter.PacketTypeFilter;
 33 import org.jivesoftware.smack.packet.IQ;
 34 import org.jivesoftware.smack.packet.Packet;
 35 import org.jivesoftware.smack.packet.Registration;
 36 import org.jivesoftware.smack.provider.ProviderManager;
 37
 38 import android.content.Context;
 39 import android.content.SharedPreferences;
 40 import android.content.SharedPreferences.Editor;
 41 import android.os.Handler;
 42 import android.util.Log;
 43
 44
 45 /**
 46  * This class is to manage the XMPP connection between client and server.
 47  *
 48  * @author Sehwan Noh (devnoh@gmail.com)
 49  */
 50 public class XmppManager {
 51
 52     private static final String LOGTAG = LogUtil.makeLogTag(XmppManager.class);
 53
 54     private static final String XMPP_RESOURCE_NAME = "AndroidpnClient";
 55
 56     private Context context;
 57
 58     private NotificationService.TaskSubmitter taskSubmitter;
 59
 60     private NotificationService.TaskTracker taskTracker;
 61
 62     private SharedPreferences sharedPrefs;
 63
 64     private String xmppHost;
 65
 66     private int xmppPort;
 67
 68     private XMPPConnection connection;
 69
 70     private String username;
 71
 72     private String password;
 73
 74     private ConnectionListener connectionListener;
 75
 76     private PacketListener notificationPacketListener;
 77
 78     private Handler handler;
 79
 80     private List taskList;
 81
 82     private boolean running = false;
 83
 84     private Future futureTask;
 85
 86     private Thread reconnection;
 87
 88     public XmppManager(NotificationService notificationService) {
 89         context = notificationService;
 90         taskSubmitter = notificationService.getTaskSubmitter();
 91         taskTracker = notificationService.getTaskTracker();
 92         sharedPrefs = notificationService.getSharedPreferences();
 93
 94         xmppHost = sharedPrefs.getString(Constants.XMPP_HOST, "localhost");
 95         xmppPort = sharedPrefs.getInt(Constants.XMPP_PORT, 5222);
 96         username = sharedPrefs.getString(Constants.XMPP_USERNAME, "");
 97         password = sharedPrefs.getString(Constants.XMPP_PASSWORD, "");
 98
 99         connectionListener = new PersistentConnectionListener(this);
 100         notificationPacketListener = new NotificationPacketListener(this);
 101
 102         handler = new Handler();
 103         taskList = new ArrayList();
 104         reconnection = new ReconnectionThread(this);
 105     }
 106
 107     public Context getContext() {
 108         return context;
 109     }
 110
 111     public void connect() {
 112         Log.d(LOGTAG, "connect()...");
 113         submitLoginTask();
 114     }
 115
 116     public void disconnect() {
 117         Log.d(LOGTAG, "disconnect()...");
 118         terminatePersistentConnection();
 119     }
 120
 121     public void terminatePersistentConnection() {
 122         Log.d(LOGTAG, "terminatePersistentConnection()...");
 123         Runnable runnable = new Runnable() {
 124
 125             final XmppManager xmppManager = XmppManager.this;
 126
 127             public void run() {
 128                 if (xmppManager.isConnected()) {
 129                     Log.d(LOGTAG, "terminatePersistentConnection()... run()");
 130                     xmppManager.getConnection().removePacketListener(
 131                             xmppManager.getNotificationPacketListener());
 132                     xmppManager.getConnection().disconnect();
 133                 }
 134                 xmppManager.runTask();
 135             }
 136
 137         };
 138         addTask(runnable);
 139     }
 140
 141     public XMPPConnection getConnection() {
 142         return connection;
 143     }
 144
 145     public void setConnection(XMPPConnection connection) {
 146         this.connection = connection;
 147     }
 148
 149     public String getUsername() {
 150         return username;
 151     }
 152
 153     public void setUsername(String username) {
 154         this.username = username;
 155     }
 156
 157     public String getPassword() {
 158         return password;
 159     }
 160
 161     public void setPassword(String password) {
 162         this.password = password;
 163     }
 164
 165     public ConnectionListener getConnectionListener() {
 166         return connectionListener;
 167     }
 168
 169     public PacketListener getNotificationPacketListener() {
 170         return notificationPacketListener;
 171     }
 172
 173     public void startReconnectionThread() {
 174         synchronized (reconnection) {
 175             if (!reconnection.isAlive()) {
 176                 reconnection.setName("Xmpp Reconnection Thread");
 177                 reconnection.start();
 178             }
 179         }
 180     }
 181
 182     public Handler getHandler() {
 183         return handler;
 184     }
 185
 186     public void reregisterAccount() {
 187         removeAccount();
 188         submitLoginTask();
 189         runTask();
 190     }
 191
 192     public List getTaskList() {
 193         return taskList;
 194     }
 195
 196     public Future getFutureTask() {
 197         return futureTask;
 198     }
 199
 200     public void runTask() {
 201         Log.d(LOGTAG, "runTask()...");
 202         synchronized (taskList) {
 203             running = false;
 204             futureTask = null;
 205             if (!taskList.isEmpty()) {
 206                 Runnable runnable = (Runnable) taskList.get(0);
 207                 taskList.remove(0);
 208                 running = true;
 209                 futureTask = taskSubmitter.submit(runnable);
 210                 if (futureTask == null) {
 211                     taskTracker.decrease();
 212                 }
 213             }
 214         }
 215         taskTracker.decrease();
 216         Log.d(LOGTAG, "runTask()...done");
 217     }
 218
 219     private String newRandomUUID() {
 220         String uuidRaw = UUID.randomUUID().toString();
 221         return uuidRaw.replaceAll("-", "");
 222     }
 223
 224     private boolean isConnected() {
 225         return connection != null && connection.isConnected();
 226     }
 227
 228     private boolean isAuthenticated() {
 229         return connection != null && connection.isConnected()
 230                 && connection.isAuthenticated();
 231     }
 232
 233     private boolean isRegistered() {
 234         return sharedPrefs.contains(Constants.XMPP_USERNAME)
 235                 && sharedPrefs.contains(Constants.XMPP_PASSWORD);
 236     }
 237
 238     private void submitConnectTask() {
 239         Log.d(LOGTAG, "submitConnectTask()...");
 240         addTask(new ConnectTask());
 241     }
 242
 243     private void submitRegisterTask() {
 244         Log.d(LOGTAG, "submitRegisterTask()...");
 245         submitConnectTask();
 246         addTask(new RegisterTask());
 247     }
 248
 249     private void submitLoginTask() {
 250         Log.d(LOGTAG, "submitLoginTask()...");
 251         submitRegisterTask();
 252         addTask(new LoginTask());
 253     }
 254
 255     private void addTask(Runnable runnable) {
 256         Log.d(LOGTAG, "addTask(runnable)...");
 257         taskTracker.increase();
 258         synchronized (taskList) {
 259             if (taskList.isEmpty() && !running) {
 260                 running = true;
 261                 futureTask = taskSubmitter.submit(runnable);
 262                 if (futureTask == null) {
 263                     taskTracker.decrease();
 264                 }
 265             } else {
 266                 taskList.add(runnable);
 267             }
 268         }
 269         Log.d(LOGTAG, "addTask(runnable)... done");
 270     }
 271
 272     private void removeAccount() {
 273         Editor editor = sharedPrefs.edit();
 274         editor.remove(Constants.XMPP_USERNAME);
 275         editor.remove(Constants.XMPP_PASSWORD);
 276         editor.commit();
 277     }
 278
 279     /**
 280      * A runnable task to connect the server.
 281      */
 282     private class ConnectTask implements Runnable {
 283
 284         final XmppManager xmppManager;
 285
 286         private ConnectTask() {
 287             this.xmppManager = XmppManager.this;
 288         }
 289
 290         public void run() {
 291             Log.i(LOGTAG, "ConnectTask.run()...");
 292
 293             if (!xmppManager.isConnected()) {
 294                 // Create the configuration for this new connection
 295                 ConnectionConfiguration connConfig = new ConnectionConfiguration(
 296                         xmppHost, xmppPort);
 297                 // connConfig.setSecurityMode(SecurityMode.disabled);
 298                 connConfig.setSecurityMode(SecurityMode.required);
 299                 connConfig.setSASLAuthenticationEnabled(false);
 300                 connConfig.setCompressionEnabled(false);
 301
 302                 XMPPConnection connection = new XMPPConnection(connConfig);
 303                 xmppManager.setConnection(connection);
 304
 305                 try {
 306                     // Connect to the server
 307                     connection.connect();
 308                     Log.i(LOGTAG, "XMPP connected successfully");
 309
 310                     // packet provider
 311                     ProviderManager.getInstance().addIQProvider("notification",
 312                             "androidpn:iq:notification",
 313                             new NotificationIQProvider());
 314
 315                 } catch (XMPPException e) {
 316                     Log.e(LOGTAG, "XMPP connection failed", e);
 317                 }
 318
 319                 xmppManager.runTask();
 320
 321             } else {
 322                 Log.i(LOGTAG, "XMPP connected already");
 323                 xmppManager.runTask();
 324             }
 325         }
 326     }
 327
 328     /**
 329      * A runnable task to register a new user onto the server.
 330      */
 331     private class RegisterTask implements Runnable {
 332
 333         final XmppManager xmppManager;
 334
 335         private RegisterTask() {
 336             xmppManager = XmppManager.this;
 337         }
 338
 339         public void run() {
 340             Log.i(LOGTAG, "RegisterTask.run()...");
 341
 342             //如果账号不存在的话,随机生成一个uuid的用户名和mima
 343             if (!xmppManager.isRegistered()) {
 344                 final String newUsername = newRandomUUID();
 345                 final String newPassword = newRandomUUID();
 346                 // final String newUsername = "af100042487d4b06a49adda8c3a82d41";
 347                 // final String newPassword = "af100042487d4b06a49adda8c3a82d41";
 348
 349                 Registration registration = new Registration();
 350
 351                 PacketFilter packetFilter = new AndFilter(new PacketIDFilter(
 352                         registration.getPacketID()), new PacketTypeFilter(
 353                         IQ.class));
 354
 355                 PacketListener packetListener = new PacketListener() {
 356
 357                     public void processPacket(Packet packet) {
 358                         Log.d("RegisterTask.PacketListener",
 359                                 "processPacket().....");
 360                         Log.d("RegisterTask.PacketListener", "packet="
 361                                 + packet.toXML());
 362
 363                         if (packet instanceof IQ) {
 364                             IQ response = (IQ) packet;
 365                             if (response.getType() == IQ.Type.ERROR) {
 366                                 if (!response.getError().toString().contains(
 367                                         "409")) {
 368                                     Log.e(LOGTAG,
 369                                             "Unknown error while registering XMPP account! "
 370                                                     + response.getError()
 371                                                             .getCondition());
 372                                 }
 373                             } else if (response.getType() == IQ.Type.RESULT) {
 374                                 xmppManager.setUsername(newUsername);
 375                                 xmppManager.setPassword(newPassword);
 376                                 Log.d(LOGTAG, "username=" + newUsername);
 377                                 Log.d(LOGTAG, "password=" + newPassword);
 378
 379                                 Editor editor = sharedPrefs.edit();
 380                                 editor.putString(Constants.XMPP_USERNAME,
 381                                         newUsername);
 382                                 editor.putString(Constants.XMPP_PASSWORD,
 383                                         newPassword);
 384                                 editor.commit();
 385                                 Log
 386                                         .i(LOGTAG,
 387                                                 "Account registered successfully");
 388                                 xmppManager.runTask();
 389                             }
 390                         }
 391                     }
 392                 };
 393
 394                 connection.addPacketListener(packetListener, packetFilter);
 395
 396                 registration.setType(IQ.Type.SET);
 397                 // registration.setTo(xmppHost);
 398                 // Map attributes = new HashMap();
 399                 // attributes.put("username", rUsername);
 400                 // attributes.put("password", rPassword);
 401                 // registration.setAttributes(attributes);
 402                 registration.addAttribute("username", newUsername);
 403                 registration.addAttribute("password", newPassword);
 404                 connection.sendPacket(registration);
 405
 406             } else {
 407                 Log.i(LOGTAG, "Account registered already");
 408                 xmppManager.runTask();
 409             }
 410         }
 411     }
 412
 413     /**
 414      * A runnable task to log into the server.
 415      */
 416     private class LoginTask implements Runnable {
 417
 418         final XmppManager xmppManager;
 419
 420         private LoginTask() {
 421             this.xmppManager = XmppManager.this;
 422         }
 423
 424         public void run() {
 425             Log.i(LOGTAG, "LoginTask.run()...");
 426
 427             if (!xmppManager.isAuthenticated()) {
 428                 Log.d(LOGTAG, "username=" + username);
 429                 Log.d(LOGTAG, "password=" + password);
 430
 431                 try {
 432                     xmppManager.getConnection().login(
 433                             xmppManager.getUsername(),
 434                             xmppManager.getPassword(), XMPP_RESOURCE_NAME);
 435                     Log.d(LOGTAG, "Loggedn in successfully");
 436
 437                     // connection listener
 438                     if (xmppManager.getConnectionListener() != null) {
 439                         xmppManager.getConnection().addConnectionListener(
 440                                 xmppManager.getConnectionListener());
 441                     }
 442
 443                     // packet filter
 444                     PacketFilter packetFilter = new PacketTypeFilter(
 445                             NotificationIQ.class);
 446                     // packet listener
 447                     PacketListener packetListener = xmppManager
 448                             .getNotificationPacketListener();
 449                     connection.addPacketListener(packetListener, packetFilter);
 450                   //判断是否处于连接状态(添加)
 451                     if(!getConnection().isConnected())
 452                     {
 453                          xmppManager.runTask();
 454                     }
 455                     xmppManager.runTask();
 456                 } catch (XMPPException e) {
 457                     Log.e(LOGTAG, "LoginTask.run()... xmpp error");
 458                     Log.e(LOGTAG, "Failed to login to xmpp server. Caused by: "
 459                             + e.getMessage());
 460                     String INVALID_CREDENTIALS_ERROR_CODE = "401";
 461                     String errorMessage = e.getMessage();
 462                     if (errorMessage != null
 463                             && errorMessage
 464                                     .contains(INVALID_CREDENTIALS_ERROR_CODE)) {
 465                         xmppManager.reregisterAccount();
 466                         return;
 467                     }
 468                     xmppManager.startReconnectionThread();
 469
 470                 } catch (Exception e) {
 471                     Log.e(LOGTAG, "LoginTask.run()... other error");
 472                     Log.e(LOGTAG, "Failed to login to xmpp server. Caused by: "
 473                             + e.getMessage());
 474                     xmppManager.startReconnectionThread();
 475                 }
 476                 //添加
 477                 xmppManager.runTask();
 478             } else {
 479                 Log.i(LOGTAG, "Logged in already");
 480                 xmppManager.runTask();
 481             }
 482
 483         }
 484     }
 485
 486 }
 新添加代码450-454行和477行
 还有一个问题是:当客户端的用户有不在线的时候,消息应怎么进行推送,是直接忽略呢还是下次登录的时候在进行推送,想qq那样,很显然对已一个具体的实用项目来说是不能忽略的,那么怎么进行消息的离线推送呢,下次告诉大家,因为我现在还没解决,不过快了。和大家说下我的思路吧:在androidpn服务端有一个UserController这个类,源码如下:
 
 
 http://images.iyunv.com/OutliningIndicators/ContractedBlock.gifhttp://images.iyunv.com/OutliningIndicators/ExpandedBlockStart.gifView Code
 
 
 1 /*
 2  * Copyright (C) 2010 Moduad Co., Ltd.
 3  *
 4  * This program is free software; you can redistribute it and/or modify
 5  * it under the terms of the GNU General Public License as published by
 6  * the Free Software Foundation; either version 2 of the License, or
 7  * (at your option) any later version.
 8  *
 9  * This program is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 12  * GNU General Public License for more details.
 13  *
 14  * You should have received a copy of the GNU General Public License along
 15  * with this program; if not, write to the Free Software Foundation, Inc.,
 16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 17  */
 18 package org.androidpn.server.console.controller;
 19
 20 import java.util.List;
 21
 22 import javax.servlet.http.HttpServletRequest;
 23 import javax.servlet.http.HttpServletResponse;
 24
 25 import org.androidpn.server.model.User;
 26 import org.androidpn.server.service.ServiceLocator;
 27 import org.androidpn.server.service.UserService;
 28 import org.androidpn.server.xmpp.presence.PresenceManager;
 29 import org.springframework.web.servlet.ModelAndView;
 30 import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
 31
 32 /**
 33  * A controller class to process the user related requests.
 34  *
 35  * @author Sehwan Noh (devnoh@gmail.com)
 36  */
 37 public class UserController extends MultiActionController {
 38
 39     private UserService userService;
 40
 41     public UserController() {
 42         userService = ServiceLocator.getUserService();
 43     }
 44
 45     //用户列表
 46     public ModelAndView list(HttpServletRequest request,
 47             HttpServletResponse response) throws Exception {
 48         PresenceManager presenceManager = new PresenceManager();
 49         List userList = userService.getUsers();
 50         for (User user : userList) {
 51             if (presenceManager.isAvailable(user)) {
 52                 // Presence presence = presenceManager.getPresence(user);
 53                 user.setOnline(true);
 54             } else {
 55                 user.setOnline(false);
 56             }
 57             // logger.debug("user.online=" + user.isOnline());
 58         }
 59         ModelAndView mav = new ModelAndView();
 60         mav.addObject("userList", userList);
 61         mav.setViewName("user/list");
 62         return mav;
 63     }
 64
 65 }
 该源码里面有用户是否在线的判断,我们只要将用户的列表取出来,如果用户在线就将消息进行推送,如果有不在线的用户,我们就把该消息放到缓存中(也可以放到数据库中更加保险),当然为了防止用户过长没有登陆系统,导致下次登录时出现过多托送过来的消息,我们还可以在服务端进行设置时间,比如服务端只缓存近N天的消息,利用
 
 
 
 session.getCreationDate();
 session.getLastActiveDate();
 这两句代码应该可以完成,本人没尝试过,还不知道。效果如如下:
 http://pic002.iyunv.com/images/2012/379107/2012042818595916.png
 | 
 |