openfire的启动流程

来源:互联网 发布:白金数据 编辑:程序博客网 时间:2024/05/22 10:51
http://blog.csdn.net/huwenfeng_2011/article/details/43413233

1、org.jivesoftware.openfire.starter.ServerStarter
XmppServer中的启动顺序机制,如图:

           图1 openfire启动流程

public class ServerStarter {

/**
* Default to this location if one has not been specified
*/
private static final String DEFAULT_LIB_DIR = "../lib";
private static final String DEFAULT_ADMIN_LIB_DIR = "../plugins/admin/webapp/WEB-INF/lib";

public static void main(String [] args) {
new ServerStarter().start();
}

/**
* Starts the server by loading and instantiating the bootstrap
* container. Once the start method is called, the server is
* started and the server starter should not be used again.
*/
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();
}
}

2、org.jivesoftware.openfire.XMPPServer——核心启动类

图2 核心启动流程
public void start() {
try {
/*初始化initialize方法执行的时候会根据配置文件的<setup>节点属性值来判断系统是否第一次启动在openfire_src\target\openfire\conf目录下的openfire.xml文件中也配置着系统的基本信息。这些基本的信息包括:DB连接供应商,数据库连接的基本信息,端口,语言环境等。
*//
   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") + " " + 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();
}
}

1、初始化

——初始化操作initialize()


                          图 3初始化


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(4, 0, 2, Version.ReleaseStatus.Release, -1);
//如果<setup>节点值默认为false。那么在第一次打开系统的时候会出现系统配置信息的界面,此时setupMode为true。//
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;
}

2、验证数据源
/**
* Verify that the database is accessible.
*/
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.");
logger.error("Database could not be accessed", e);
throw new IllegalArgumentException(e);
}
finally {
DbConnectionManager.closeConnection(rs, pstmt, con);
}
}
3、加载启动模块
private void loadModules() {
// Load boot modules
loadModule(RoutingTableImpl.class.getName());
loadModule(AuditManagerImpl.class.getName());//implemention of the AuditManage Interface
loadModule(RosterManager.class.getName());
loadModule(PrivateStorage.class.getName());
// Load core modules
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());
loadModule(PacketTransporterImpl.class.getName());
loadModule(PacketDelivererImpl.class.getName());
loadModule(TransportHandler.class.getName());
loadModule(OfflineMessageStrategy.class.getName());
loadModule(OfflineMessageStore.class.getName());
loadModule(VCardManager.class.getName());
// Load standard modules
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(IQRosterHandler.class.getName());
loadModule(IQEntityTimeHandler.class.getName());
loadModule(IQvCardHandler.class.getName());
loadModule(IQVersionHandler.class.getName());
loadModule(IQLastActivityHandler.class.getName());
loadModule(PresenceSubscribeHandler.class.getName());
loadModule(PresenceUpdateHandler.class.getName());
loadModule(IQOfflineMessagesHandler.class.getName());
loadModule(IQPEPHandler.class.getName());
loadModule(IQPEPOwnerHandler.class.getName());
loadModule(MulticastDNSService.class.getName());
loadModule(IQSharedGroupHandler.class.getName());
loadModule(AdHocCommandHandler.class.getName());
loadModule(IQPrivacyHandler.class.getName());
loadModule(DefaultFileTransferManager.class.getName());
loadModule(FileTransferProxy.class.getName());
loadModule(MediaProxyService.class.getName());
loadModule(PubSubModule.class.getName());
loadModule(IQDiscoInfoHandler.class.getName());
loadModule(IQDiscoItemsHandler.class.getName());
loadModule(UpdateManager.class.getName());
loadModule(FlashCrossDomainHandler.class.getName());
loadModule(InternalComponentManager.class.getName());
loadModule(MultiUserChatManager.class.getName());
loadModule(IQMessageCarbonsHandler.class.getName());
loadModule(CertificateStoreManager.class.getName());

// Load this module always last since we don't want to start listening for clients
// before the rest of the modules have been started
loadModule(ConnectionManagerImpl.class.getName());
// Keep a reference to the internal component manager
componentManager = getComponentManager();
}
4.初始化模块
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();
}
logger.error(LocaleUtils.getLocalizedString("admin.error"), e);
}
}
}
5、启动模块
/**
* <p>Following the loading and initialization of all the modules
* this method is called to iterate through the known modules and
* start them.</p>
*/
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();
}
logger.error(LocaleUtils.getLocalizedString("admin.error"), e);
}
}
}

注意,在启动ConnectionManagerImpl模块中,该类配置了所有连接参数,并且最终启动了所有连接监听
/**
* Starts all listeners. This ensures that all those that are enabled will start accept connections.
*/
private synchronized void startListeners()
{
// Check if plugins have been loaded
PluginManager pluginManager = XMPPServer.getInstance().getPluginManager();
if (!pluginManager.isExecuted()) {
pluginManager.addPluginManagerListener(new PluginManagerListener() {
public void pluginsMonitored() {
// Stop listening for plugin events
XMPPServer.getInstance().getPluginManager().removePluginManagerListener(this);
// Start listeners
startListeners();
}
});
return;
}

for ( final ConnectionListener listener : getListeners() )
{
try
{
listener.start();
}
catch ( RuntimeException ex )
{
Log.error( "An exception occurred while starting listener " + listener, ex );
}
}

// Start the HTTP client listener.
try
{
HttpBindManager.getInstance().start();
}
catch ( RuntimeException ex )
{
Log.error( "An exception occurred while starting HTTP Bind listener ", ex );
}
}


通知监听

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

启动后:
加载一些plugins,注意,中间有个线程开启了监听,看日志:

[pool-3-thread-1] INFO org.jivesoftware.openfire.spi.ConnectionListener[socket_c2s] - Started.
[pool-3-thread-1] INFO org.jivesoftware.openfire.spi.ConnectionListener[socket_c2s-legacyMode] - Started.
[pool-3-thread-1] WARN org.jivesoftware.openfire.spi.LegacyConnectionAcceptor - Configuration allows for up to 16 threads, although implementation is limited to exactly one.
[pool-3-thread-1] INFO org.jivesoftware.openfire.spi.ConnectionListener[socket_s2s] - Started.
[pool-3-thread-1] INFO org.jivesoftware.openfire.spi.ConnectionListener[component] - Started.
[pool-3-thread-1] INFO org.jivesoftware.openfire.spi.ConnectionListener[component-legacyMode] - Started.
[pool-3-thread-1] INFO org.jivesoftware.openfire.spi.ConnectionListener[connection_manager] - Started.
[pool-3-thread-1] INFO org.jivesoftware.openfire.spi.ConnectionListener[connection_manager-legacyMode] - Started.
[pool-3-thread-1] INFO org.jivesoftware.openfire.spi.EncryptionArtifactFactory - Creating new SslContextFactory instance


下面看org.jivesoftware.openfire.spi.ConnectionListener代码:
/**
* Attempts to start the connection acceptor, creating a new instance when needed.
*
* An invocation of this method does not change the configuration for this connection. As a result, an acceptor will
* <em>not</em> be started when the listener is not enabled (in such cases, an invocation of this method has no
* effect).
*
* In order to start this listener and persist this as the desired state for this connection, use #enable(true).
*
* This method should not be called when an acceptor has already been started (instead, {@link #restart()} should be
* used to explicitly define the need to stop a previous connection). The current implementation of this method will
* stop a pre-existing acceptor, but only when it is currently not serving connections. When the acceptor is not
* idle, this method has no effect. This behavior might change in the future.
*/
public synchronized void start()
{
// TODO Start all connection types here, by supplying more connection acceptors other than a MINA-based one.
switch ( getType() )
{
case BOSH_C2S:
case WEBADMIN:
Log.debug( "Not starting a (MINA-based) connection acceptor, as connections of type " + getType() + " depend on another IO technology.");
return;

default:
}

if ( !isEnabled() )
{
Log.debug( "Not starting: disabled by configuration." );
return;
}

if ( connectionAcceptor != null )
{
// This might indicate an illegal state. Legacy code allows for this, so we won't throw a runtime exception (for now).
if ( !connectionAcceptor.isIdle() )
{
Log.warn( "Unable to start: it appears to have already been started (and it is currently serving connections)! To restart, first stop this listener explicitly." );
return;
}
else
{
Log.warn( "Stopping (in order to restart) an instance that has already been started, but is idle. This start would have failed if the listener was not idle. The implementation should have called stop() or restart() first, to ensure a clean restart!" );
connectionAcceptor.stop();
}
}

Log.debug( "Starting..." );
if ( getType() == ConnectionType.SOCKET_S2S )
{
connectionAcceptor = new LegacyConnectionAcceptor( generateConnectionConfiguration() );
}
else
{//这个地方相当重要,它创建了一个MINAConnectionAcceptor,这个connectionAcceptor的作用就相当于NioSocketAcceptor服务器的作用
connectionAcceptor = new MINAConnectionAcceptor( generateConnectionConfiguration() );
}

connectionAcceptor.start();
Log.info( "mina server listening is start." );
Log.info( "Started." );
}

2016/06/08 15:35

http://blog.csdn.net/hwxia/article/details/6043429



0 0
原创粉丝点击