关于将XMPP server部署到Tomcat上的一些问题
在XMPP消息推送这个问题上,网上已经有很多资料了,本人觉得很好的一篇资料是: http://HdhCmsTestiteye测试数据/topic/1117043 提供了一个连接下载源码: http://115测试数据/file/bhkfse3i#%20Androidpn.rar 很感谢前辈们的研究结果。 在源码的使用过程中要注意的地方有两点,网上的那篇资料好像忽略了一个重要的地方,就是要改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://HdhCmsTestapache.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测试数据)
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测试数据mit();
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测试数据mit();
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
该源码里面有用户是否在线的判断,我们只要将用户的列表取出来,如果用户在线就将消息进行推送,如果有不在线的用户,我们就把该消息放到缓存中(也可以放到数据库中更加保险),当然为了防止用户过长没有登陆系统,导致下次登录时出现过多托送过来的消息,我们还可以在服务端进行设置时间,比如服务端只缓存近N天的消息,利用
session.getCreationDate(); session.getLastActiveDate();
这两句代码应该可以完成,本人没尝试过,还不知道。效果如如下:
绿色通道: 好文要顶 关注我 收藏该文 与我联系
作者: Leo_wl
出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息查看更多关于关于将XMPP server部署到Tomcat上的一些问题的详细内容...