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();
                }
    

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














  • 相关阅读:
    Oracle Words Spelling Error
    原创 分布式锁与应用场景
    原创 分布式事务简单实现思路
    原创 使用redis位图实现布隆过滤器
    原创 redis实现接口限流
    原创 jwt-security简单实现
    原创 抢购秒杀之redis高性能实现
    原创 springcloud feign优化配置
    原创 派单、抢单业务简单实现
    原创 微信公众号推送图片实现
  • 原文地址:https://www.cnblogs.com/huwf/p/4273364.html
Copyright © 2011-2022 走看看