`
duanfei
  • 浏览: 732372 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

(转)关于将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这个类中做简要的处理即可修改。源码如下:

/*
 * 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();


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

  • 大小: 39.6 KB
分享到:
评论
1 楼 cvb21354 2013-03-28  
公司用的项目竟然和这个分享的差不多

相关推荐

    XMPP(Android客户端的实现__Tomcat版的Androidpn_)

    在Tomcat中部署XMPP服务,可以使用Openfire或者Ignite Real Time等支持XMPP的服务器软件。 接着,你需要设置MySQL数据库。确保MySQL服务已经启动,并创建一个用于AndroidPN的数据库。数据库中需要包含用于存储用户...

    androidpn_tomcat整合(发布直接使用)

    然后,在服务器的“发布”目录下,将AndroidPN-server的构建结果(WAR文件或已编译的类文件)部署到适当的位置,通常是`webapps`目录。 4. **配置XML文件**:AndroidPN-server可能需要一些特定的XML配置文件,例如`...

    androidpn-tomcat版本

    8. **故障排查**:在部署和运行AndroidPN-tomcat时,可能会遇到各种问题,如网络连接问题、服务器配置错误或客户端兼容性问题。掌握基本的故障排查技巧非常重要。 9. **性能优化**:为了提供良好的用户体验,开发者...

    Androidpn tomcat版本

    将此WAR文件放入Tomcat的webapps目录下,Tomcat会自动部署该应用。 4. **配置服务器**:根据项目文档,配置AndroidPN服务器的XML配置文件,如server.xml和context.xml,设置数据库连接、端口等参数。 5. **创建...

    androidpn-tomcat-0.5.0

    另一方面,"androidpn-server-tomcat.zip"则包含了AndroidPN服务端的部署包,这部分是基于Tomcat服务器运行的。Tomcat是Apache软件基金会的Jakarta项目下的一个开源产品,它实现了Java Servlet和JavaServer Pages...

    Openfire下实现WebServer

    2. **独立WebServer与Openfire通信**:另一种方法是将WebServer(如Apache Tomcat、Nginx或Lighttpd)与Openfire分开部署,通过HTTP或者XMPP协议进行通信。这种方式下,WebServer负责处理HTTP请求,而Openfire专注于...

    openfire3.9.1 源码部署及运行

    一旦构建成功,Openfire会产生一个可运行的WAR文件,可以部署到任何支持Java Servlet容器的应用服务器上,如Tomcat或Jetty。如果你选择Tomcat,将WAR文件复制到Tomcat的webapps目录下,Tomcat会自动解压并启动...

    openfire webchat源码部署相关jar

    在部署Openfire WebChat时,你需要将这些JAR文件添加到你的应用服务器(如Tomcat)的类路径中,确保服务器能够正确处理JSP并编译它们为Servlet。通常,这可以通过将这些文件放入服务器的lib目录来实现。一旦完成,...

    JMS教程+activemq以及activemq和tomcat的整合

    要将ActiveMQ与Tomcat整合,你需要完成以下步骤: 1. **下载和安装ActiveMQ**: 获取ActiveMQ的最新版本,并解压到合适的位置。 2. **配置ActiveMQ**: 修改`conf.activemq.xml`配置文件,设置所需的参数,如端口、...

    androidpn服务端,客户端.rar

    - `XMPPServer`: 负责处理客户端的连接和断开,接收推送消息。 - `NotificationManager`: 存储和管理设备注册信息,负责消息的广播。 - `DatabaseAccess`: 使用Hibernate操作数据库,存储用户和设备信息。 二、...

    fastpath_webchat_war

    它将所有静态和动态资源集成在一起,便于在支持Java EE的服务器上进行部署,如Tomcat、Jetty或Glassfish等。 2. **fastpath-webchat**:Fastpath-Webchat是一个互动式聊天平台,旨在增强openfire即时通讯系统的功能...

    Androidpn源代码分析

    - **服务端**:接收到推送请求后,服务端会验证请求的有效性,然后通过网络协议(如HTTP或XMPP)将消息发送给指定的客户端。服务端通常维护一个客户端在线状态的数据库,以便在需要时向特定客户端推送消息。 - **...

    apache-activemq-5.11.3

    集成过程通常包括配置ActiveMQ为Tomcat的独立服务或者作为Tomcat内部模块,通过修改Tomcat的服务器配置文件(如`server.xml`)来设置ActiveMQ的相关参数。 **使用Apache ActiveMQ-5.11.3需要注意:** 1. **硬件和...

    Android平台消息推送技术的应用研究.pdf

    在服务器端部署中,AndroidPn-server-0.5.0需要在Tomcat上运行,配合MySQL数据库。客户端则接收即时推送信息,提供用户友好的交互界面。 总结来说,Android平台的消息推送技术通过优化的推送机制和协议,如XMPP,...

    about androidPN

    AndroidPN的源代码结构包含了服务器端和客户端的相关模块,例如,`org.androidpn.server.xmpp.net.Connection`类代表服务器的XMPP连接,而`org.androidpn.server.xmpp.session.SessionManager`负责会话管理。...

    openfire3.10.2 基于Eclipse的二次开发环境单间缺失部分包

    - **jersey-servlet-1.19.jar**:提供了Servlet容器的适配器,使得Jersey可以部署在Web应用服务器上,如Tomcat。 - **javax.ws.rs-api-2.0.jar**:定义了JAX-RS 2.0标准的接口,是RESTful服务开发的基础。 4. **...

    openfire-4.5.0源码

    例如,`xmpp-server`模块包含了处理连接、认证、会话管理和消息路由的类。通过解析XML流,Openfire可以理解客户端的需求,如登录、发送消息或建立多用户聊天室。 三、源码关键模块分析 1. 用户管理:`user`模块是...

    java版仿QQ即时通讯系统

    10. **负载均衡与分布式**:随着用户量的增加,系统可能需要部署在多台服务器上,这就涉及到负载均衡和分布式计算的概念。Java的NIO(New I/O)库和Netty框架可以帮助实现高性能的网络通信,而像Apache Tomcat这样的...

    IMS.rar_IMS_JAVA 即时通讯_jsp IMS_即时 通讯_即时通讯

    3. **部署**:将应用程序部署到生产环境,如AWS、Azure或阿里云等云服务平台,保证服务的稳定性和可扩展性。 4. **监控**:通过日志分析、性能监控工具(如Prometheus、Grafana)对系统运行状态进行监控,及时发现...

    Openfire_spark安装手册.pdf

    - 将RED5应用部署到Web服务器中,例如Apache Tomcat。 - 配置RED5应用的基本设置,确保其能够与Openfire和Spark客户端协同工作。 ##### 5.2 Spark客户端配置视频插件 - 在Spark客户端中启用视频插件,以便支持视频...

Global site tag (gtag.js) - Google Analytics