Keep Running

导航

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


  1. 在XMPP消息推送这个问题上,网上已经有很多资料了,本人觉得很好的一篇资料是:http://www.iteye.com/topic/1117043
  2. 提供了一个连接下载源码:http://115.com/file/bhkfse3i#%20Androidpn.rar
  3. 很感谢前辈们的研究结果。
  4. 在源码的使用过程中要注意的地方有两点,网上的那篇资料好像忽略了一个重要的地方,就是要改resources文件夹下面的jdbc.properties,将里面关于数据库的配置改为自己的,另一个需要注意的地方就是改android端的ip了。

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

对于这个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<Runnable> 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<Runnable>();
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<Runnable> 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<String, String> attributes = new HashMap<String, String>();
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这个类,源码如下:

View 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<User> 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();

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

posted on 2012-04-28 19:01  Keep Running  阅读(11447)  评论(6编辑  收藏  举报