(转)OpenFire源码学习之四:openfire的启动流程
2017-07-11 17:47
471 查看
转:http://blog.csdn.net/huwenfeng_2011/article/details/43413233
启动的总入口在ServerStarter的main方法中。通过上图首先它会先加载它所需要的jar文件。最后通过Java反射机制将XMPPServer加入当前线程。
[java] view plain copy
Thread.currentThread().setContextClassLoader(loader);
Class containerClass = loader.loadClass(
"org.jivesoftware.openfire.XMPPServer");
containerClass.newInstance();
当它获取当前执行的线程对象Thread.currentThread()然后设置为这个线程上下文类加载器,将loader加载进去。而loader是什么呢?再向上看它的源码:
[java] view plain copy
ClassLoader loader = new JiveClassLoader(parent, libDir);
libDir就没什么好说的了。Parent就是:ClassLoader parent = Thread.currentThread().getContextClassLoader();
Parent就是AppClassLoader加载器。
XmppServer中的启动顺序机制,如图:
initialize方法执行的时候会根据配置文件的<setup>节点属性值来判断系统是否第一次启动在openfire_src\target\openfire\conf目录下的openfire.xml文件中也配置着系统的基本信息。这些基本的信息包括:DB连接供应商,数据库连接的基本信息,端口,语言环境等。
如果<setup>节点值默认为false。那么在第一次打开系统的时候会出现系统配置信息的界面。如图所示:
在这里的每次设置都会把值传递给connectionManager连接管理对象。继续下一部也就是到安装数据库信息配置的时候会跳转到这个页面:
setup-datasource-standard.jsp
在这里OpenFire会把数据库配置信息写入到openfire.xml配置文件当中。
以下为代码清单
[java] view plain copy
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文件属性之后,系统会测试数据库连接并生成数据表结构。通过一下方法完成操作
[java] view plain copy
DbConnectionManager.setConnectionProvider(conProvider);
然后检查数据库是否要更新
[java] view plain copy
pluginManager = new PluginManager(pluginDir);
XMPPServer中 插件管理器(PluginManager)会添加插件的基本信息如:插件的实体、插件所在的目录、插件文件、插件监控等等
启动插件模块分为分为以下几个步骤:
1、数据库验证
[java] view plain copy
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、加载启动模块
[java] view plain copy
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()方法
统一初始化所有的插件代码清代:
[java] view plain copy
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()这个方法来遍历已知的模块和启动它们。
[java] view plain copy
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);
}
}
}
[java] view plain copy
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);
}
}
[java] view plain copy
for (XMPPServerListener listener : listeners) {
listener.serverStarted();
}
这个通知消息说明服务器所有模块已经启动完成。这个时候就是监听消息的发送和接收了。但也有可能有些插件也会等待被装载。
openfire启动
ServerStarter
启动流程图:启动的总入口在ServerStarter的main方法中。通过上图首先它会先加载它所需要的jar文件。最后通过Java反射机制将XMPPServer加入当前线程。
[java] view plain copy
Thread.currentThread().setContextClassLoader(loader);
Class containerClass = loader.loadClass(
"org.jivesoftware.openfire.XMPPServer");
containerClass.newInstance();
当它获取当前执行的线程对象Thread.currentThread()然后设置为这个线程上下文类加载器,将loader加载进去。而loader是什么呢?再向上看它的源码:
[java] view plain copy
ClassLoader loader = new JiveClassLoader(parent, libDir);
libDir就没什么好说的了。Parent就是:ClassLoader parent = Thread.currentThread().getContextClassLoader();
Parent就是AppClassLoader加载器。
XMPPServer
——核心启动类XmppServer中的启动顺序机制,如图:
初始化
——初始化操作initialize()initialize方法执行的时候会根据配置文件的<setup>节点属性值来判断系统是否第一次启动在openfire_src\target\openfire\conf目录下的openfire.xml文件中也配置着系统的基本信息。这些基本的信息包括:DB连接供应商,数据库连接的基本信息,端口,语言环境等。
如果<setup>节点值默认为false。那么在第一次打开系统的时候会出现系统配置信息的界面。如图所示:
在这里的每次设置都会把值传递给connectionManager连接管理对象。继续下一部也就是到安装数据库信息配置的时候会跳转到这个页面:
setup-datasource-standard.jsp
在这里OpenFire会把数据库配置信息写入到openfire.xml配置文件当中。
以下为代码清单
[java] view plain copy
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文件属性之后,系统会测试数据库连接并生成数据表结构。通过一下方法完成操作
[java] view plain copy
DbConnectionManager.setConnectionProvider(conProvider);
然后检查数据库是否要更新
加载插件模块
of中所有的插件都以jar或war文件存在[java] view plain copy
pluginManager = new PluginManager(pluginDir);
XMPPServer中 插件管理器(PluginManager)会添加插件的基本信息如:插件的实体、插件所在的目录、插件文件、插件监控等等
启动插件模块分为分为以下几个步骤:
1、数据库验证
[java] view plain copy
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、加载启动模块
[java] view plain copy
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()方法
统一初始化所有的插件代码清代:
[java] view plain copy
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()这个方法来遍历已知的模块和启动它们。
[java] view plain copy
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。这个监控器会定时检查插件目录是否有新的插件添加。[java] view plain copy
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。[java] view plain copy
for (XMPPServerListener listener : listeners) {
listener.serverStarted();
}
这个通知消息说明服务器所有模块已经启动完成。这个时候就是监听消息的发送和接收了。但也有可能有些插件也会等待被装载。
相关文章推荐
- OpenFire源码学习之四:openfire的启动流程
- OpenFire源码学习之四:openfire的启动流程
- chrome源码学习之启动流程简介
- Ejabberd源码学习——启动流程
- ASP.NET Core MVC源码学习:MVC启动流程详解
- chrome源码学习之启动流程简介
- Spark源码学习(二)---Master和Worker的启动以及Actor通信流程
- PHP SAPI CLI启动流程结合mysql扩展源码学习
- mysql 源码学习笔记:mysqld启动流程
- wordpress学习二:源码目录结构和启动流程
- ASP.NET Core MVC 源码学习:MVC 启动流程详解
- chrome源码学习之启动流程简介
- hdfs2.6.2源码学习:Day1-NameNode启动流程分析
- hdfs2.6.2源码学习:Day2-DataNode启动流程分析
- ASP.NET Core MVC 源码学习:MVC 启动流程详解
- chrome源码学习之启动流程简介
- Python源码学习:启动流程简析
- [ZZ]chrome源码学习之启动流程简介
- ASP.NET Core MVC 源码学习:MVC 启动流程详解
- 内核:Linux2.6内核启动流程学习