读书人

(转)关于将XMPP server部署到Tomcat

发布时间: 2012-11-18 10:51:21 作者: rapoo

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

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


在项目部署到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();


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

读书人网 >移动开发

热点推荐