zoukankan      html  css  js  c++  java
  • OpenFire源码学习之四:openfire的启动流程

    openfire启动

    ServerStarter

    启动流程图:


    启动的总入口在ServerStarter的main方法中。通过上图首先它会先加载它所需要的jar文件。最后通过java反射机制将XMPPServer加入当前线程。

    Thread.currentThread().setContextClassLoader(loader);
                Class containerClass = loader.loadClass(
                        "org.jivesoftware.openfire.XMPPServer");
                containerClass.newInstance();
    

    当它获取当前执行的线程对象Thread.currentThread()然后设置为这个线程上下文类加载器,将loader加载进去。而loader是什么呢?再向上看它的源码:

    ClassLoader loader = new JiveClassLoader(parent, libDir);

    libDir就没什么好说的了。Parent就是:ClassLoader parent = Thread.currentThread().getContextClassLoader();

    Parent就是AppClassLoader加载器。

    XMPPServer

    ——核心启动类

    XmppServer中的启动顺序机制,如图:

    初始化

    ——初始化操作initialize()

    initialize方法执行的时候会根据配置文件的<setup>节点属性值来判断系统是否第一次启动在openfire_src argetopenfireconf目录下的openfire.xml文件中也配置着系统的基本信息。这些基本的信息包括:DB连接供应商,数据库连接的基本信息,端口,语言环境等。

    如果<setup>节点值默认为false。那么在第一次打开系统的时候会出现系统配置信息的界面。如图所示:


    在这里的每次设置都会把值传递给connectionManager连接管理对象。继续下一部也就是到安装数据库信息配置的时候会跳转到这个页面:

    setup-datasource-standard.jsp

    在这里OpenFire会把数据库配置信息写入到openfire.xml配置文件当中。

    以下为代码清单

    JiveGlobals.setXMLProperty("connectionProvider.className",
                        "org.jivesoftware.database.DefaultConnectionProvider");
    DefaultConnectionProvider conProvider = new DefaultConnectionProvider();
         try {
                //设置驱动
                conProvider.setDriver(driver);
                //连接超时
                conProvider.setConnectionTimeout(connectionTimeout);
                //最小连接
                conProvider.setMinConnections(minConnections);
                conProvider.setMaxConnections(maxConnections);
                conProvider.setServerURL(serverURL);
                conProvider.setUsername(username);
                conProvider.setPassword(password);
            conProvider.setTestSQL(DbConnectionManager.getTestSQL(driver));
            //设置系统属性
                JiveGlobals.setXMLProperty("database.defaultProvider.driver", driver);
                JiveGlobals.setXMLProperty("database.defaultProvider.serverURL", serverURL);
                JiveGlobals.setXMLProperty("database.defaultProvider.username", username);
                JiveGlobals.setXMLProperty("database.defaultProvider.password", password);
            JiveGlobals.setXMLProperty("database.defaultProvider.testSQL",  
                                    DbConnectionManager.getTestSQL(driver));
                                ......
    

    读写openfire文件属性之后,系统会测试数据库连接并生成数据表结构。通过一下方法完成操作

    DbConnectionManager.setConnectionProvider(conProvider);

    然后检查数据库是否要更新

    加载插件模块

    of中所有的插件都以jar或war文件存在

    pluginManager = new PluginManager(pluginDir);

    XMPPServer中 插件管理器(PluginManager)会添加插件的基本信息如:插件的实体、插件所在的目录、插件文件、插件监控等等

    启动插件模块分为分为以下几个步骤:

    1、数据库验证

    private void verifyDataSource() {
            Connection con = null;
            PreparedStatement pstmt = null;
            ResultSet rs = null;
            try {
                con = DbConnectionManager.getConnection();
                pstmt = con.prepareStatement("SELECT count(*) FROM ofID");
                rs = pstmt.executeQuery();
                rs.next();
            }
            catch (Exception e) {
                System.err.println("Database setup or configuration error: " +
                        "Please verify your database settings and check the " +
                        "logs/error.log file for detailed error messages.");
                Log.error("Database could not be accessed", e);
                throw new IllegalArgumentException(e);
            }
            finally {
                DbConnectionManager.closeConnection(rs, pstmt, con);
            }
        }
    
    2、加载启动模块
     private void loadModules() {
            // 加载启动模块
            loadModule(RoutingTableImpl.class.getName());
            loadModule(AuditManagerImpl.class.getName());
            loadModule(RosterManager.class.getName());
            loadModule(PrivateStorage.class.getName());
            // 加载核心模块
            loadModule(PresenceManagerImpl.class.getName());
            loadModule(SessionManager.class.getName());
            loadModule(PacketRouterImpl.class.getName());
            loadModule(IQRouter.class.getName());
            loadModule(MessageRouter.class.getName());
            loadModule(PresenceRouter.class.getName());
            loadModule(MulticastRouter.class.getName());
            。。。。。。
            // 加载标准handler载模块
            loadModule(IQBindHandler.class.getName());
            loadModule(IQSessionEstablishmentHandler.class.getName());
            loadModule(IQAuthHandler.class.getName());
            loadModule(IQPingHandler.class.getName());
            loadModule(IQPrivateHandler.class.getName());
            loadModule(IQRegisterHandler.class.getName());
            。。。。。。
            // 连接管理
            loadModule(ConnectionManagerImpl.class.getName());
            // Keep a reference to the internal component manager
            componentManager = getComponentManager();
        }
    

    3.初始化模块

    of中的所有插件都继承BasicModule,并实现initialize()方法

    统一初始化所有的插件代码清代:

    private void initModules() {
            for (Module module : modules.values()) {
                boolean isInitialized = false;
                try {
                    module.initialize(this);
                    isInitialized = true;
                }
                catch (Exception e) {
                    e.printStackTrace();
                    // Remove the failed initialized module
                    this.modules.remove(module.getClass());
                    if (isInitialized) {
                        module.stop();
                        module.destroy();
                    }
                    Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
                }
            }
        }
    

    4.启动插件模块

    of加载和初始化后的所有模块后的调用startModules()这个方法来遍历已知的模块和启动它们。

    private void startModules() {
            for (Module module : modules.values()) {
                boolean started = false;
                try {
                    module.start();
                }
                catch (Exception e) {
                    if (started && module != null) {
                        module.stop();
                        module.destroy();
                    }
                    Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
                }
            }
        }
    


    启动插件监控管理

    插件监控管理——PluginMonitor。这个监控器会定时检查插件目录是否有新的插件添加。

    public void start() {
            executor = new ScheduledThreadPoolExecutor(1);
            // See if we're in development mode. If so, check for new plugins once every 5 seconds.
            // Otherwise, default to every 20 seconds.
            if (Boolean.getBoolean("developmentMode")) {
                executor.scheduleWithFixedDelay(pluginMonitor, 0, 5, TimeUnit.SECONDS);
            }
            else {
                executor.scheduleWithFixedDelay(pluginMonitor, 0, 20, TimeUnit.SECONDS);
            }
        }
    

    通知监听

    当of完成所有插件的初始化和启动后会通知监听器:XMPPServerListener。

    for (XMPPServerListener listener : listeners) {
                    listener.serverStarted();
                }
    

    这个通知消息说明服务器所有模块已经启动完成。这个时候就是监听消息的发送和接收了。但也有可能有些插件也会等待被装载。














  • 相关阅读:
    POJ 1681 Painter's Problem(高斯消元法)
    HDU 3530 Subsequence(单调队列)
    HDU 4302 Holedox Eating(优先队列或者线段树)
    POJ 2947 Widget Factory(高斯消元法,解模线性方程组)
    HDU 3635 Dragon Balls(并查集)
    HDU 4301 Divide Chocolate(找规律,DP)
    POJ 1753 Flip Game(高斯消元)
    POJ 3185 The Water Bowls(高斯消元)
    克琳:http://liyu.eu5.org
    WinDbg使用
  • 原文地址:https://www.cnblogs.com/huwf/p/4273364.html
Copyright © 2011-2022 走看看