一、Openfire启动控制台监听分析
Openfire启动时序图
在Eclipse或者Intellij IDEA中运行openfire源码时,需要先指定openfire启动类:ServerStarter(该类里面有main()方法),Intellij IDEA中配置如下图所示:
ServerStarter类中关键代码如下:
ClassLoader loader = new JiveClassLoader(parent, libDir);
Thread.currentThread().setContextClassLoader(loader);
Class containerClass = loader.loadClass(
"org.jivesoftware.openfire.XMPPServer");
containerClass.newInstance();
从代码中我们可以看出,Openfire通过反射加载XmppServer.class,并调用newInstance方法,实例化XmppServer对象
XmppServer类是一个很重要的类,很多对象实例都可以通过XmppServer.getInstance().getXxx()方法得到。
在XmppServer对象被初始化时,会调用start()方法,关键代码如下:
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();
intialize()方法主要做了两件事:1. 读取openfireHome属性,2. 初始化Cache,openfire cache实现后面再分析。2.初始化PluginManager对象,PluginManager这个类是用来管理各种插件的。3.判断是否为setupmode,即openfire是否已做过初始化安装,这里先假设setupmode为false。4.初始化统计类,A ServerTrafficCounter counts the number of bytes read and written by the server. 5. PluginManager start,扫描plugins文件夹插件并加载插件,通过插件加载流程机制,会实例化AdminConsolePlugin类。
AdminConsolePlugin类分析
先来看一下InitialPlugin()方法:
public void initializePlugin(PluginManager manager, File pluginDir) {
this.pluginDir = pluginDir;
createWebAppContext();
startup();
}
private void createWebAppContext() {
WebAppContext context;
// Add web-app. Check to see if we're in development mode. If so, we don't
// add the normal web-app location, but the web-app in the project directory.
boolean developmentMode = Boolean.getBoolean("developmentMode");
if( developmentMode )
{ System.out.println(LocaleUtils.getLocalizedString("admin.console.devmode"));
context = new WebAppContext(contexts,
pluginDir.getParentFile().getParentFile().getParentFile().getParent() + File.separator + "src" + File.separator + "web", "/");
}else {
context = new WebAppContext(contexts, pluginDir.getAbsoluteFile() + File.separator + "webapp", "/");
}
}
public void startup() {
restartNeeded = false;
// Add listener for certificate events
certificateListener = new CertificateListener();
CertificateManager.addListener(certificateListener);
// the number of threads allocated to each connector/port
int serverThreads = JiveGlobals.getXMLProperty("adminConsole.serverThreads", 2);
adminPort = JiveGlobals.getXMLProperty("adminConsole.port", 9090);
adminSecurePort = JiveGlobals.getXMLProperty("adminConsole.securePort", 9091);
final QueuedThreadPool tp = new QueuedThreadPool();
tp.setName("Jetty-QTP-AdminConsole");
adminServer = new Server(tp);
if (JMXManager.isEnabled()) {
JMXManager jmx = JMXManager.getInstance();
adminServer.addBean(jmx.getContainer());
}
// Create connector for http traffic if it's enabled.
if (adminPort > 0) {
final HttpConfiguration httpConfig = new HttpConfiguration();
// Do not send Jetty info in HTTP headers
httpConfig.setSendServerVersion( false );
final ServerConnector httpConnector = new ServerConnector(adminServer, null, null, null, -1, serverThreads, new HttpConnectionFactory(httpConfig));
// Listen on a specific network interface if it has been set.
String bindInterface = getBindInterface();
httpConnector.setHost(bindInterface);
httpConnector.setPort(adminPort);
adminServer.addConnector(httpConnector);
}
// Create a connector for https traffic if it's enabled.
sslEnabled = false;
try {
final ConnectionManagerImpl connectionManager = ( (ConnectionManagerImpl) XMPPServer.getInstance().getConnectionManager() );
final ConnectionConfiguration configuration = connectionManager.getListener( ConnectionType.WEBADMIN, true ).generateConnectionConfiguration();
final SslContextFactory sslContextFactory = new EncryptionArtifactFactory( configuration ).getSslContextFactory();
final ServerConnector httpsConnector;
if ( "npn".equals( JiveGlobals.getXMLProperty( "spdy.protocol", "" ) ) )
{
httpsConnector = new HTTPSPDYServerConnector( adminServer, sslContextFactory );
}
else
{
final HttpConfiguration httpsConfig = new HttpConfiguration();
httpsConfig.setSendServerVersion( false );
httpsConfig.setSecureScheme( "https" );
httpsConfig.setSecurePort( adminSecurePort );
httpsConfig.addCustomizer( new SecureRequestCustomizer() );
final HttpConnectionFactory httpConnectionFactory = new HttpConnectionFactory( httpsConfig );
final SslConnectionFactory sslConnectionFactory = new SslConnectionFactory( sslContextFactory, org.eclipse.jetty.http.HttpVersion.HTTP_1_1.toString() );
httpsConnector = new ServerConnector( adminServer, null, null, null, -1, serverThreads,
sslConnectionFactory, httpConnectionFactory );
}
final String bindInterface = getBindInterface();
httpsConnector.setHost(bindInterface);
httpsConnector.setPort(adminSecurePort);
adminServer.addConnector(httpsConnector);
sslEnabled = true;
}
}
}
catch ( Exception e )
{
Log.error( "An exception occurred while trying to make available the admin console via HTTPS.", e );
}
HandlerCollection collection = new HandlerCollection();
adminServer.setHandler(collection);
collection.setHandlers(new Handler[] { contexts, new DefaultHandler() });
try {
adminServer.start();
logAdminConsolePorts();
}
catch (Exception e) {
Log.error("Could not start admin console server", e);
}
}
createWebAppContext()法里面new 了一个新的WebAppContext对象,做过javaweb开发的童鞋应该知道,每一个web应用都会有一个这样的对象,在构造方法里面指明了web.xml的加载路径。再看startUp()方法,初始化配置这些有兴趣的童鞋以自己看下,看最关键代码:
try {
adminServer.start();
// Log the ports that the admin server is listening on.
logAdminConsolePorts();
}
这里开启了对9090端口的监听。同时本地控制台中会输出日志:
2148 INFO 管理平台开始监听:
http://localhost:9090
https://localhost:9091
此时大家打开浏览器访问http://localhost:9090, 便可看到openfire控制台页面了。