執迷不悟,那又如何。
openfire是我接觸的第一款通訊開源框架,裏面涉及到mina,插件開發更新等知識,項目完全開源,最近一直在學動態更新,偶爾想起以前讀過openfire的源碼,怕忘記,寫一篇blog來複習一下其中的插件開發內容。與君分享。
openfire開源官網地址:http://www.igniterealtime.org/projects/openfire/index.jsp,
openfire github 地址:https://github.com/igniterealtime/Openfire
代碼入口爲;org.jivesoftware.openfire.starter.ServerStarter
private void start() {
// Setup the classpath using JiveClassLoader
try {
// Load up the bootstrap container
final ClassLoader parent = findParentClassLoader();
String libDirString = System.getProperty("openfire.lib.dir");
File libDir;
if (libDirString != null) {
// If the lib directory property has been specified and it actually
// exists use it, else use the default
libDir = new File(libDirString);
if (!libDir.exists()) {
Log.warn("Lib directory " + libDirString +
" does not exist. Using default " + DEFAULT_LIB_DIR);
libDir = new File(DEFAULT_LIB_DIR);
}
}
else {
libDir = new File(DEFAULT_LIB_DIR);
}
String adminLibDirString = System.getProperty("openfireHome");
if (adminLibDirString == null) {
adminLibDirString = DEFAULT_ADMIN_LIB_DIR;
}
else {
adminLibDirString = adminLibDirString+"/plugins/admin/webapp/WEB-INF/lib";
}
File adminLibDir = new File(adminLibDirString);
if (!adminLibDir.exists()) {
Log.warn("Admin Lib Directory " + adminLibDirString +
" does not exist. Web admin console may not work.");
}
ClassLoader loader = new JiveClassLoader(parent, libDir);
Thread.currentThread().setContextClassLoader(loader);
Class containerClass = loader.loadClass(
"org.jivesoftware.openfire.XMPPServer");
containerClass.newInstance();
}
catch (Exception e) {
e.printStackTrace();
}
}
先找到讀取設置的lib路徑,如果沒有,採用默認的路徑。然後用JiveClassLoader其中的class文件
最後加載org.jivesoftware.openfire.XMPPServer,然後newInstance,執行其中的靜態方法開始運行
接下來看JiveClassLoader代碼,他繼承了urlclassloader
構造方法:
JiveClassLoader(ClassLoader parent, File libDir) throws MalformedURLException {
super(new URL[] { libDir.toURI().toURL() }, parent);
File[] jars = libDir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
boolean accept = false;
String smallName = name.toLowerCase();
if (smallName.endsWith(".jar")) {
accept = true;
}
else if (smallName.endsWith(".zip")) {
accept = true;
}
return accept;
}
});
// Do nothing if no jar or zip files were found
if (jars == null) {
return;
}
// sort jars otherwise order differs between installations (e.g. it's not alphabetical)
// order may matter if trying to patch an install by adding patch jar to lib folder
Arrays.sort(jars);
for (int i = 0; i < jars.length; i++) {
if (jars[i].isFile()) {
addURL(jars[i].toURI().toURL());
}
}
}
吧目錄下的jar和zip加載進目錄中,然後分析urlClassloader的addUrl和構造方法urlClassloader繼承SecureClassLoader。
父類加載器爲也就是先獲取設置的classloader如果沒有就是appclassloader
private ClassLoader findParentClassLoader() {
ClassLoader parent = Thread.currentThread().getContextClassLoader();
if (parent == null) {
parent = this.getClass().getClassLoader();
if (parent == null) {
parent = ClassLoader.getSystemClassLoader();
}
}
return parent;
}
在org.jivesoftware.openfire.XMPPServer中,先加載model
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
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") + " " + xmppServerInfo.getVersion().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();
}
}
也急速在home下的model下,用loader加載model
loader爲剛設置進去的loader,然後loader文件下的所有class
加載進去了 然後得到實例 最後放進一個map裏面。然後初始化model
private void initModules() {
for (Module module : modules.values()) {
try {
module.initialize(this);
}
catch (Exception e) {
e.printStackTrace();
// Remove the failed initialized module
this.modules.remove(module.getClass());
module.stop();
module.destroy();
logger.error(LocaleUtils.getLocalizedString("admin.error"), e);
}
}
// Register modules with service discovery provides where applicable.
for (Module module : modules.values() )
{
// Service discovery info
if (module instanceof ServerFeaturesProvider) {
getIQDiscoInfoHandler().addServerFeaturesProvider( (ServerFeaturesProvider) module );
}
if (module instanceof ServerIdentitiesProvider) {
getIQDiscoInfoHandler().addServerIdentitiesProvider( (ServerIdentitiesProvider) module );
}
if (module instanceof UserIdentitiesProvider) {
getIQDiscoInfoHandler().addUserIdentitiesProvider( (UserIdentitiesProvider) module );
}
// Service discovery items
if (module instanceof ServerItemsProvider) {
getIQDiscoItemsHandler().addServerItemsProvider( (ServerItemsProvider) module );
}
if (module instanceof UserItemsProvider) {
getIQDiscoItemsHandler().addUserItemsProvider( (UserItemsProvider) module);
}
}
}
先初始化model,然後在將model根據接口裝進不同的handler裏面,然後開始執行模塊,最後完成模塊的加載
接下來就是pluginManager了。
有點長,