openfire源码分析---2

openfire源码分析—2

XMPPServer构造函数

    public XMPPServer() {
        // We may only have one instance of the server running on the JVM
        if (instance != null) {
            throw new IllegalStateException("A server is already running");
        }
        instance = this;
        start();
    }

这里主要就是调用了start函数。

start()

    public void start() {
        try {
            initialize();

            // Create PluginManager now (but don't start it) so that modules may use it
            File pluginDir = new File(openfireHome, "plugins");
            pluginManager = new PluginManager(pluginDir);

            // If the server has already been setup then we can start all the server's modules

            //***************************************
            // setupMode = false;
            //*****************************************
            if (!setupMode) {
                verifyDataSource();
                // First load all the modules so that modules may access other modules while
                // being initialized
                loadModules();
                // Initize all the modules
                initModules();
                // Start all the modules
                startModules();
            }
            // Initialize statistics
            ServerTrafficCounter.initStatistics();

            // Load plugins (when in setup mode only the admin console will be loaded)
            pluginManager.start();

            // Log that the server has been started
            String startupBanner = LocaleUtils.getLocalizedString("short.title") + " " + version.getVersionString() +
                    " [" + JiveGlobals.formatDateTime(new Date()) + "]";
            logger.info(startupBanner);
            System.out.println(startupBanner);

            started = true;

            // Notify server listeners that the server has been started
            for (XMPPServerListener listener : listeners) {
                listener.serverStarted();
            }
        }
        catch (Exception e) {
            e.printStackTrace();
            logger.error(e.getMessage(), e);
            System.out.println(LocaleUtils.getLocalizedString("startup.error"));
            shutdownServer();
        }
    }

initialize()主要做了一些初始化工作。
pluginManager = new PluginManager(pluginDir)根据插件目录pluginDir初始化PluginManager实例,该构造函数如下所示

    public PluginManager(File pluginDir) {
        this.pluginDirectory = pluginDir;
        plugins = new ConcurrentHashMap<String, Plugin>();
        pluginDirs = new HashMap<Plugin, File>();
        pluginFiles = new HashMap<String, File>();
        classloaders = new HashMap<Plugin, PluginClassLoader>();
        pluginDevelopment = new HashMap<Plugin, PluginDevEnvironment>();
        parentPluginMap = new HashMap<Plugin, List<String>>();
        childPluginMap = new HashMap<Plugin, String>();
        devPlugins = new HashSet<String>();
        pluginMonitor = new PluginMonitor();
    }

重点关注一下PluginMonitor函数,该函数后面还会提到。
接着,verifyDataSource()、loadModules()、initModules()和startModules()四个函数用来装载一些模块,这几个函数留在下一章分析。
ServerTrafficCounter.initStatistics()用来初始化一些统计信息,例如openfire进程输入输出的字节数等等。
pluginManager.start()启动插件管理,后面再来看这个函数。
最后调用了一些监听函数。

initialize()

    private void initialize() throws FileNotFoundException {

        locateOpenfire();

        startDate = new Date();

        try {
            host = InetAddress.getLocalHost().getHostName();
        }
        catch (UnknownHostException ex) {
            logger.warn("Unable to determine local hostname.", ex);
        }
        if (host == null) {
            host = "127.0.0.1";         
        }

        version = new Version(3, 10, 2, Version.ReleaseStatus.Release, -1);

        if ("true".equals(JiveGlobals.getXMLProperty("setup"))) {
            setupMode = false;
        }

        if (isStandAlone()) {
            logger.info("Registering shutdown hook (standalone mode)");
            Runtime.getRuntime().addShutdownHook(new ShutdownHookThread());
            TaskEngine.getInstance().schedule(new Terminator(), 1000, 1000);
        }

        loader = Thread.currentThread().getContextClassLoader();

        try {
            CacheFactory.initialize();
        } catch (InitializationException e) {
            e.printStackTrace();
            logger.error(e.getMessage(), e);
        }

        JiveGlobals.migrateProperty("xmpp.domain");
        name = JiveGlobals.getProperty("xmpp.domain", host).toLowerCase();

        JiveGlobals.migrateProperty(Log.LOG_DEBUG_ENABLED);
        Log.setDebugEnabled(JiveGlobals.getBooleanProperty(Log.LOG_DEBUG_ENABLED, false));

        // Update server info
        xmppServerInfo = new XMPPServerInfoImpl(name, host, version, startDate);

        initialized = true;
    }

locateOpenfire()用于确定openfire的工作目录,以及配置文件,并构造相应的File实例,我们后面看这个函数。
InetAddress.getLocalHost().getHostName()用于获取计算机名。
Version用于存放本次openfire的版本信息,因此本次openfire的版本为3.10.2-release。
isStandAlone用于判断服务器模式,standalone的服务器表是没有被第三方软件管理(例如服务的启动、停止、安全等)。
Runtime.getRuntime().addShutdownHook(new ShutdownHookThread())用于设置服务器异常关机时执行的函数。ShutdownHookThread如下所示

    private class ShutdownHookThread extends Thread {

        /**
         * <p>Logs the server shutdown.</p>
         */
        @Override
        public void run() {
            shutdownServer();
            logger.info("Server halted");
            System.err.println("Server halted");
        }
    }

因此当服务器异常关机时,必然会执行shutdownServer函数,关闭一些模块、执行一些监听函数等等。这里就不往下看了。
TaskEngine.getInstance().schedule(new Terminator(), 1000, 1000)用来启动一个定时线程,如下所示

    private class Terminator extends TimerTask {
        private BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
        public void run() {
            try { 
                if (stdin.ready()) {
                    if (EXIT.equalsIgnoreCase(stdin.readLine())) {
                        System.exit(0); // invokes shutdown hook(s)
                    }
                }
            } catch (IOException ioe) {
                logger.error("Error reading console input", ioe);
            }
        }
    }

该线程监听控制台的输入,如果为“exit”,则调用System.exit退出openfire进程。
继续往下看,CacheFactory.initialize()的代码如下

    public static synchronized void initialize() throws InitializationException {
        try {
            localCacheFactoryStrategy = (CacheFactoryStrategy) Class.forName(localCacheFactoryClass).newInstance();
            cacheFactoryStrategy = localCacheFactoryStrategy;
        } catch (Exception e) {
            log.error("Failed to instantiate local cache factory strategy: " + localCacheFactoryClass, e);
             throw new InitializationException(e);
        }
    }

该函数初始化了org.jivesoftware.util.cache.DefaultLocalCacheStrategy实例,该实例和缓存有关,因为该构造函数为空函数,所以我们不往下分析了。
再往下看,migrateProperty用于将数据从xml搬入数据库,该函数如下所示

    public static void migrateProperty(String name) {
        if (isSetupMode()) {
            return;
        }
        if (openfireProperties == null) {
            loadOpenfireProperties();
        }
        openfireProperties.migrateProperty(name);
    }

isSetupMode用于启动数据库,该函数如下所示

    private static boolean isSetupMode() {
        if (Boolean.valueOf(JiveGlobals.getXMLProperty("setup"))) {
            return false;
        }
        // Check if the DB configuration is done
        if (DbConnectionManager.getConnectionProvider() == null) {
            // DB setup is still not completed so setup is needed
            return true;
        }
        Connection con = null;
        PreparedStatement pstmt = null;
        try {
            con = DbConnectionManager.getConnection();
            // Properties can now be loaded from DB so consider setup done
        }
        catch (SQLException e) {
            // Properties cannot be loaded from DB so do not consider setup done
            return true;
        }
        finally {
            DbConnectionManager.closeConnection(pstmt, con);
        }
        return false;
    }

继续看migrateProperty,loadOpenfireProperties用于将openfire的配置文件转化为XMLProperties实例,该函数如下所示

    private synchronized static void loadOpenfireProperties() {
        if (openfireProperties == null) {
            // If home is null then log that the application will not work correctly
            if (home == null && !failedLoading) {
                failedLoading = true;
                StringBuilder msg = new StringBuilder();
                msg.append("Critical Error! The home directory has not been configured, \n");
                msg.append("which will prevent the application from working correctly.\n\n");
                System.err.println(msg.toString());
            }
            // Create a manager with the full path to the Openfire config file.
            else {
                try {
                    openfireProperties = new XMLProperties(home + File.separator + getConfigName());
                }
                catch (IOException ioe) {
                    Log.error(ioe.getMessage());
                    failedLoading = true;
                }
            }
            // create a default/empty XML properties set (helpful for unit testing)
            if (openfireProperties == null) {
                try { 
                    openfireProperties = new XMLProperties();
                } catch (IOException e) {
                    Log.error("Failed to setup default openfire properties", e);
                }               
            }
        }
    }

可以看到,如果找不到配置文件,这里就实例化一个空的XMLProperties实例。继续看migrateProperty中最后的openfireProperties.migrateProperty(name),该函数用来检查openfire.xml文件中的配置是否与数据库中一致,如果不一致以数据库中的配置为准,并且删除openfire.xml的配置。由于该函数涉及到太多的XML操作和数据库操作,因此这里就不看了。
initialize函数最后初始化了XMPPServerInfoImpl实例,如下所示

    public XMPPServerInfoImpl(String xmppDomain, String hostname, Version version, Date startDate) {
        this.xmppDomain = xmppDomain;
        this.hostname = hostname;
        this.ver = version;
        this.startDate = startDate;
    }

locateOpenfire()

    private void locateOpenfire() throws FileNotFoundException {
        String jiveConfigName = "conf" + File.separator + "openfire.xml";
        // First, try to load it openfireHome as a system property.
        if (openfireHome == null) {
            String homeProperty = System.getProperty("openfireHome");
            try {
                if (homeProperty != null) {
                    openfireHome = verifyHome(homeProperty, jiveConfigName);
                }
            }
            catch (FileNotFoundException fe) {
                // Ignore.
            }
        }

        // If we still don't have home, let's assume this is standalone
        // and just look for home in a standard sub-dir location and verify
        // by looking for the config file
        if (openfireHome == null) {
            try {
                openfireHome = verifyHome("..", jiveConfigName).getCanonicalFile();
            }
            catch (FileNotFoundException fe) {
                // Ignore.
            }
            catch (IOException ie) {
                // Ignore.
            }
        }

        // If home is still null, no outside process has set it and
        // we have to attempt to load the value from openfire_init.xml,
        // which must be in the classpath.
        if (openfireHome == null) {
            InputStream in = null;
            try {
                in = getClass().getResourceAsStream("/openfire_init.xml");
                if (in != null) {
                    SAXReader reader = new SAXReader();
                    Document doc = reader.read(in);
                    String path = doc.getRootElement().getText();
                    try {
                        if (path != null) {
                            openfireHome = verifyHome(path, jiveConfigName);
                        }
                    }
                    catch (FileNotFoundException fe) {
                        fe.printStackTrace();
                    }
                }
            }
            catch (Exception e) {
                System.err.println("Error loading openfire_init.xml to find home.");
                e.printStackTrace();
            }
            finally {
                try {
                    if (in != null) {
                        in.close();
                    }
                }
                catch (Exception e) {
                    System.err.println("Could not close open connection");
                    e.printStackTrace();
                }
            }
        }

        if (openfireHome == null) {
            System.err.println("Could not locate home");
            throw new FileNotFoundException();
        }
        else {
            // Set the home directory for the config file
            JiveGlobals.setHomeDirectory(openfireHome.toString());
            // Set the name of the config file
            JiveGlobals.setConfigName(jiveConfigName);
        }
    }

jiveConfigName为openfire配置文件所在的相对路径(conf/openfire.xml)。
String homeProperty = System.getProperty(“openfireHome”)用来获取openfire工作目录的绝对路径。
openfireHome = verifyHome(homeProperty, jiveConfigName)用来获取配置文件的File实例。该函数如下所示

    private File verifyHome(String homeGuess, String jiveConfigName) throws FileNotFoundException {
        File openfireHome = new File(homeGuess);
        File configFile = new File(openfireHome, jiveConfigName);
        if (!configFile.exists()) {
            throw new FileNotFoundException();
        }
        else {
            try {
                return new File(openfireHome.getCanonicalPath());
            }
            catch (Exception ex) {
                throw new FileNotFoundException();
            }
        }
    }

继续locateOpenfire的分析,第二个if表是,如果获取配置文件的File实例失败,就尝试从上级目录(..)重新获取一遍。
如果继续失败,就尝试从openfire_init.xml配置文件中获取openfire工作目录的绝对路径,继续尝试一遍。
如果再找不到,就抛出异常。
如果成功获得了openfire.xml配置文件的File实例,就调用JiveGlobals设置一些全局变量。这里设置了openfire的工作目录的绝对路径以及openfire.xml配置文件的相对路径。

pluginManager.start()

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

因此该函数主要启动了pluginMonitor线程,执行了其中的run函数,如下所示

        public void run() {
            // If the task is already running, return.
            synchronized (this) {
                if (running) {
                    return;
                }
                running = true;
            }
            try {
                running = true;
                // Look for extra plugin directories specified as a system property.
                String pluginDirs = System.getProperty("pluginDirs");
                if (pluginDirs != null) {
                    StringTokenizer st = new StringTokenizer(pluginDirs, ", ");
                    while (st.hasMoreTokens()) {
                        String dir = st.nextToken();
                        if (!devPlugins.contains(dir)) {
                            loadPlugin(new File(dir));
                            devPlugins.add(dir);
                        }
                    }
                }

                File[] jars = pluginDirectory.listFiles(new FileFilter() {
                    public boolean accept(File pathname) {
                        String fileName = pathname.getName().toLowerCase();
                        return (fileName.endsWith(".jar") || fileName.endsWith(".war"));
                    }
                });

                if (jars == null) {
                    return;
                }

                for (File jarFile : jars) {
                    String pluginName = jarFile.getName().substring(0,
                        jarFile.getName().length() - 4).toLowerCase();
                    // See if the JAR has already been exploded.
                    File dir = new File(pluginDirectory, pluginName);
                    // Store the JAR/WAR file that created the plugin folder
                    pluginFiles.put(pluginName, jarFile);
                    // If the JAR hasn't been exploded, do so.
                    if (!dir.exists()) {
                        unzipPlugin(pluginName, jarFile, dir);
                    }
                    // See if the JAR is newer than the directory. If so, the plugin
                    // needs to be unloaded and then reloaded.
                    else if (jarFile.lastModified() > dir.lastModified()) {
                        // If this is the first time that the monitor process is running, then
                        // plugins won't be loaded yet. Therefore, just delete the directory.
                        if (firstRun) {
                            int count = 0;
                            // Attempt to delete the folder for up to 5 seconds.
                            while (!deleteDir(dir) && count < 5) {
                                Thread.sleep(1000);
                            }
                        }
                        else {
                            unloadPlugin(pluginName);
                        }
                        // If the delete operation was a success, unzip the plugin.
                        if (!dir.exists()) {
                            unzipPlugin(pluginName, jarFile, dir);
                        }
                    }
                }

                File[] dirs = pluginDirectory.listFiles(new FileFilter() {
                    public boolean accept(File pathname) {
                        return pathname.isDirectory();
                    }
                });

                // Sort the list of directories so that the "admin" plugin is always
                // first in the list.
                Arrays.sort(dirs, new Comparator<File>() {
                    public int compare(File file1, File file2) {
                        if (file1.getName().equals("admin")) {
                            return -1;
                        }
                        else if (file2.getName().equals("admin")) {
                            return 1;
                        }
                        else {
                            return file1.compareTo(file2);
                        }
                    }
                });

                // Turn the list of JAR/WAR files into a set so that we can do lookups.
                Set<String> jarSet = new HashSet<String>();
                for (File file : jars) {
                    jarSet.add(file.getName().toLowerCase());
                }

                // See if any currently running plugins need to be unloaded
                // due to the JAR file being deleted (ignore admin plugin).
                // Build a list of plugins to delete first so that the plugins
                // keyset isn't modified as we're iterating through it.
                List<String> toDelete = new ArrayList<String>();
                for (File pluginDir : dirs) {
                    String pluginName = pluginDir.getName();
                    if (pluginName.equals("admin")) {
                        continue;
                    }
                    if (!jarSet.contains(pluginName + ".jar")) {
                        if (!jarSet.contains(pluginName + ".war")) {
                            toDelete.add(pluginName);
                        }
                    }
                }
                for (String pluginName : toDelete) {
                    unloadPlugin(pluginName);
                }

                // Load all plugins that need to be loaded.
                for (File dirFile : dirs) {
                    // If the plugin hasn't already been started, start it.
                    if (dirFile.exists() && !plugins.containsKey(dirFile.getName())) {
                        loadPlugin(dirFile);
                    }
                }
                // Set that at least one iteration was done. That means that "all available" plugins
                // have been loaded by now.
                if (!XMPPServer.getInstance().isSetupMode()) {
                    executed = true;
                }

                // Trigger event that plugins have been monitored
                firePluginsMonitored();
            }
            catch (Throwable e) {
                Log.error(e.getMessage(), e);
            }
            // Finished running task.
            synchronized (this) {
                running = false;
            }
            // Process finished, so set firstRun to false (setting it multiple times doesn't hurt).
            firstRun = false;
        }

这里就不详细分析这个函数了,该函数就是解压插件目录下所有拓展名为jar和war的插件,解压后变成一个目录,然后调用loadPlugin装载该插件,最后通过firePluginsMonitored函数调用插件的监听函数。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值