当XMPPServer启动的时候,会调用其start()方法,
public void start() {
try {
initialize();
startDate = new Date();
// Store server info
xmppServerInfo = new XMPPServerInfoImpl(name, host, version, startDate, getConnectionManager());
// 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
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()) + "]";
Log.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();
Log.error(e.getMessage(), e);
System.out.println(LocaleUtils.getLocalizedString("startup.error"));
shutdownServer();
}
}
start方法中干的事情太多了,这里主要介绍数据库方面,其他的暂时不做介绍。
initialize();会读取openfire.xml中的setup的值,如果为true,就不会做数据的验证操作。
openfire所有的自动化执行简表脚本都在此方法中verifyDataSource();
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);
}
}
DbConnectionManager.getConnection();:
在获取数据库链接的时候会做如下操作:
public static Connection getConnection() throws SQLException {
if (connectionProvider == null) {
synchronized (providerLock) {
if (connectionProvider == null) {
// Attempt to load the connection provider classname as
// a Jive property.
String className = JiveGlobals.getXMLProperty("connectionProvider.className");
if (className != null) {
// Attempt to load the class.
try {
Class conClass = ClassUtils.forName(className);
setConnectionProvider((ConnectionProvider)conClass.newInstance());
}
catch (Exception e) {
Log.warn("Failed to create the " +
"connection provider specified by connection" +
"Provider.className. Using the default pool.", e);
setConnectionProvider(new DefaultConnectionProvider());
}
}
else {
setConnectionProvider(new DefaultConnectionProvider());
}
}
}
}
// TODO: May want to make these settings configurable
Integer retryCnt = 0;
Integer retryMax = 10;
Integer retryWait = 250; // milliseconds
Connection con = null;
SQLException lastException = null;
do {
try {
con = connectionProvider.getConnection();
if (con != null) {
// Got one, lets hand it off.
// Usually profiling is not enabled. So we return a normal
// connection unless profiling is enabled. If yes, wrap the
// connection with a profiled connection.
if (!profilingEnabled) {
return con;
}
else {
return new ProfiledConnection(con);
}
}
} catch (SQLException e) {
// TODO distinguish recoverable from non-recoverable exceptions.
lastException = e;
Log.info("Unable to get a connection from the database pool " +
"(attempt "+retryCnt+" out of "+retryMax+").", e);
}
try {
Thread.sleep(retryWait);
}
catch (Exception e) {
// Ignored
}
retryCnt++;
} while (retryCnt <= retryMax);
throw new SQLException("ConnectionManager.getConnection() " +
"failed to obtain a connection after " + retryCnt +" retries. " +
"The exception from the last attempt is as follows: "+lastException);
}
会在数据库配置文件中读取connectionProvider的配置,如果没有,就采用默认的DefaultConnectionProvider,
public static void setConnectionProvider(ConnectionProvider provider) {
synchronized (providerLock) {
if (connectionProvider != null) {
connectionProvider.destroy();
connectionProvider = null;
}
connectionProvider = provider;
connectionProvider.start();
// Now, get a connection to determine meta data.
Connection con = null;
try {
con = connectionProvider.getConnection();
setMetaData(con);
// Check to see if the database schema needs to be upgraded.
schemaManager.checkOpenfireSchema(con);
}
catch (Exception e) {
Log.error(e.getMessage(), e);
}
finally {
closeConnection(con);
}
}
schemaManager.checkOpenfireSchema(con);是模板管理器,这个才是真的自动建表的管理器。
checkOpenfireSchema方法调用 private boolean checkSchema(Connection con, String schemaKey, int requiredVersion,
ResourceLoader resourceLoader) throws Exception;方法,根据版本号来确定是否需要建表,会读取相关的sql脚本。
public boolean checkPluginSchema(final Plugin plugin) ,这个方法也是根据版本号来执行插件中database中sql脚本,其有一定的命名规则,插件名_数据库类型.sql。这个地方的版本号是从插件的配置文件中读取
<databaseKey>clientcontrol</databaseKey>
<databaseVersion>0</databaseVersion>,
这个配置的作用已在另外一篇文章中做说明了。
此篇文章就到此,稍后会有更多关于openfire的个人解读。
联系方式(qq):851392159