/**
* $RCSfile$
* $Revision: 3144 $
* $Date: 2005-12-01 14:20:11 -0300 (Thu, 01 Dec 2005) $
*
* Copyright (C) 2004-2008 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.openfire;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.KeyStore;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import org.dom4j.Document;
import org.dom4j.io.SAXReader;
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.openfire.admin.AdminManager;
import org.jivesoftware.openfire.audit.AuditManager;
import org.jivesoftware.openfire.audit.spi.AuditManagerImpl;
import org.jivesoftware.openfire.clearspace.ClearspaceManager;
import org.jivesoftware.openfire.cluster.NodeID;
import org.jivesoftware.openfire.commands.AdHocCommandHandler;
import org.jivesoftware.openfire.component.InternalComponentManager;
import org.jivesoftware.openfire.container.AdminConsolePlugin;
import org.jivesoftware.openfire.container.Module;
import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.disco.IQDiscoInfoHandler;
import org.jivesoftware.openfire.disco.IQDiscoItemsHandler;
import org.jivesoftware.openfire.disco.ServerFeaturesProvider;
import org.jivesoftware.openfire.disco.ServerIdentitiesProvider;
import org.jivesoftware.openfire.disco.ServerItemsProvider;
import org.jivesoftware.openfire.disco.UserIdentitiesProvider;
import org.jivesoftware.openfire.disco.UserItemsProvider;
import org.jivesoftware.openfire.filetransfer.DefaultFileTransferManager;
import org.jivesoftware.openfire.filetransfer.FileTransferManager;
import org.jivesoftware.openfire.filetransfer.proxy.FileTransferProxy;
import org.jivesoftware.openfire.handler.IQAuthHandler;
import org.jivesoftware.openfire.handler.IQBindHandler;
import org.jivesoftware.openfire.handler.IQHandler;
import org.jivesoftware.openfire.handler.IQLastActivityHandler;
import org.jivesoftware.openfire.handler.IQOfflineMessagesHandler;
import org.jivesoftware.openfire.handler.IQPingHandler;
import org.jivesoftware.openfire.handler.IQPrivacyHandler;
import org.jivesoftware.openfire.handler.IQPrivateHandler;
import org.jivesoftware.openfire.handler.IQRegisterHandler;
import org.jivesoftware.openfire.handler.IQRosterHandler;
import org.jivesoftware.openfire.handler.IQSessionEstablishmentHandler;
import org.jivesoftware.openfire.handler.IQSharedGroupHandler;
import org.jivesoftware.openfire.handler.IQTimeHandler;
import org.jivesoftware.openfire.handler.IQVersionHandler;
import org.jivesoftware.openfire.handler.IQvCardHandler;
import org.jivesoftware.openfire.handler.PresenceSubscribeHandler;
import org.jivesoftware.openfire.handler.PresenceUpdateHandler;
import org.jivesoftware.openfire.lockout.LockOutManager;
import org.jivesoftware.openfire.mediaproxy.MediaProxyService;
import org.jivesoftware.openfire.muc.MultiUserChatManager;
import org.jivesoftware.openfire.net.MulticastDNSService;
import org.jivesoftware.openfire.net.SSLConfig;
import org.jivesoftware.openfire.net.ServerTrafficCounter;
import org.jivesoftware.openfire.pep.IQPEPHandler;
import org.jivesoftware.openfire.pep.IQPEPOwnerHandler;
import org.jivesoftware.openfire.pubsub.PubSubModule;
import org.jivesoftware.openfire.roster.RosterManager;
import org.jivesoftware.openfire.session.RemoteSessionLocator;
import org.jivesoftware.openfire.spi.ConnectionManagerImpl;
import org.jivesoftware.openfire.spi.PacketDelivererImpl;
import org.jivesoftware.openfire.spi.PacketRouterImpl;
import org.jivesoftware.openfire.spi.PacketTransporterImpl;
import org.jivesoftware.openfire.spi.PresenceManagerImpl;
import org.jivesoftware.openfire.spi.RoutingTableImpl;
import org.jivesoftware.openfire.spi.XMPPServerInfoImpl;
import org.jivesoftware.openfire.stun.STUNService;
import org.jivesoftware.openfire.transport.TransportHandler;
import org.jivesoftware.openfire.update.UpdateManager;
import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.openfire.vcard.VCardManager;
import org.jivesoftware.util.CertificateManager;
import org.jivesoftware.util.InitializationException;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.TaskEngine;
import org.jivesoftware.util.Version;
import org.jivesoftware.util.cache.CacheFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID;
/**
* The main XMPP server that will load, initialize and start all the server's
* modules. The server is unique in the JVM and could be obtained by using the
* {@link #getInstance()} method.<p>
* <p/>
* The loaded modules will be initialized and may access through the server other
* modules. This means that the only way for a module to locate another module is
* through the server. The server maintains a list of loaded modules.<p>
* <p/>
* After starting up all the modules the server will load any available plugin.
* For more information see: {@link org.jivesoftware.openfire.container.PluginManager}.<p>
* <p/>
* A configuration file keeps the server configuration. This information is required for the
* server to work correctly. The server assumes that the configuration file is named
* <b>openfire.xml</b> and is located in the <b>conf</b> folder. The folder that keeps
* the configuration file must be located under the home folder. The server will try different
* methods to locate the home folder.
* <p/>
* <ol>
* <li><b>system property</b> - The server will use the value defined in the <i>openfireHome</i>
* system property.</li>
* <li><b>working folder</b> - The server will check if there is a <i>conf</i> folder in the
* working directory. This is the case when running in standalone mode.</li>
* <li><b>openfire_init.xml file</b> - Attempt to load the value from openfire_init.xml which
* must be in the classpath</li>
* </ol>
*
* @author Gaston Dombiak
*/
public class XMPPServer {
private static final Logger Log = LoggerFactory.getLogger(XMPPServer.class);
private static XMPPServer instance;
private String name;
private String host;
private Version version;
private Date startDate;
private boolean initialized = false;
private boolean started = false;
private NodeID nodeID;
private static final NodeID DEFAULT_NODE_ID = NodeID.getInstance(new byte[0]);
/**
* All modules loaded by this server
*/
private Map<Class, Module> modules = new LinkedHashMap<Class, Module>();
/**
* Listeners that will be notified when the server has started or is about to be stopped.
*/
private List<XMPPServerListener> listeners = new CopyOnWriteArrayList<XMPPServerListener>();
/**
* Location of the home directory. All configuration files should be
* located here.
*/
private File openfireHome;
private ClassLoader loader;
private PluginManager pluginManager;
private InternalComponentManager componentManager;
private RemoteSessionLocator remoteSessionLocator;
/**
* True if in setup mode
*/
private boolean setupMode = true;
private static final String STARTER_CLASSNAME =
"org.jivesoftware.openfire.starter.ServerStarter";
private static final String WRAPPER_CLASSNAME =
"org.tanukisoftware.wrapper.WrapperManager";
private boolean shuttingDown;
private XMPPServerInfoImpl xmppServerInfo;
/**
* Returns a singleton instance of XMPPServer.
*
* @return an instance.
*/
public static XMPPServer getInstance() {
return instance;
}
/**
* Creates a server and starts it.
*/
public XMPPServer() {
// We may only have one instance of the server running on the JVM
if (instance != null) {
throw new IllegalStateException("A server is already running");
}
instance = this;
start();
}
/**
* Returns a snapshot of the server's status.
*
* @return the server information current at the time of the method call.
*/
public XMPPServerInfo getServerInfo() {
if (!initialized) {
throw new IllegalStateException("Not initialized yet");
}
return xmppServerInfo;
}
/**
* Returns true if the given address is local to the server (managed by this
* server domain). Return false even if the jid's domain matches a local component's
* service JID.
*
* @param jid the JID to check.
* @return true if the address is a local address to this server.
*/
public boolean isLocal(JID jid) {
boolean local = false;
if (jid != null && name != null && name.equals(jid.getDomain())) {
local = true;
}
return local;
}
/**
* Returns true if the given address does not match the local server hostname and does not
* match a component service JID.
*
* @param jid the JID to check.
* @return true if the given address does not match the local server hostname and does not
* match a component service JID.
*/
public boolean isRemote(JID jid) {
if (jid != null) {
if (!name.equals(jid.getDomain()) && !componentManager.hasComponent(jid)) {
return true;
}
}
return false;
}
/**
* Returns an ID that uniquely identifies this server in a cluster. When not running in cluster mode
* the returned value is always the same. However, when in cluster mode the value should be set
* when joining the cluster and must be unique even upon restarts of this node.
*
* @return an ID that uniquely identifies this server in a cluster.
*/
public NodeID getNodeID() {
return nodeID == null ? DEFAULT_NODE_ID : nodeID;
}
/**
* Sets an ID that uniquely identifies this server in a cluster. When not running in cluster mode
* the returned value is always the same. However, when in cluster mode the value should be set
* when joining the cluster and must be unique even upon restarts of this node.
*
* @param nodeID an ID that uniquely identifies this server in a cluster or null if not in a cluster.
*/
public void setNodeID(NodeID nodeID) {
this.nodeID = nodeID;
}
/**
* Returns true if the given address matches a component service JID.
*
* @param jid the JID to check.
* @return true if the given address matches a component service JID.
*/
public boolean matchesComponent(JID jid) {
return jid != null && !name.equals(jid.getDomain()) && componentManager.hasComponent(jid);
}
/**
* Creates an XMPPAddress local to this server.
*
* @param username the user name portion of the id or null to indicate none is needed.
* @param resource the resource portion of the id or null to indicate none is needed.
* @return an XMPPAddress for the server.
*/
public JID createJID(String username, String resource) {
return new JID(username, name, resource);
}
/**
* Creates an XMPPAddress local to this server. The construction of the new JID
* can be optimized by skipping stringprep operations.
*
* @param username the user name portion of the id or null to indicate none is needed.
* @param resource the resource portion of the id or null to indicate none is needed.
* @param skipStringprep true if stringprep should not be applied.
* @return an XMPPAddress for the server.
*/
public JID createJID(String username, String resource, boolean skipStringprep) {
return new JID(username, name, resource, skipStringprep);
}
/**
* Returns a collection with the JIDs of the server's admins. The collection may include
* JIDs of local users and users of remote servers.
*
* @return a collection with the JIDs of the server's admins.
*/
public Collection<JID> getAdmins() {
return AdminManager.getInstance().getAdminAccounts();
}
/**
* Adds a new server listener that will be notified when the server has been started
* or is about to be stopped.
*
* @param listener the new server listener to add.
*/
public void addServerListener(XMPPServerListener listener) {
listeners.add(listener);
}
/**
* Removes a server listener that was being notified when the server was being started
* or was about to be stopped.
*
* @param listener the server listener to remove.
*/
public void removeServerListener(XMPPServerListener listener) {
listeners.remove(listener);
}
private void initialize() throws FileNotFoundException {
locateOpenfire();
name = JiveGlobals.getProperty("xmpp.domain", "127.0.0.1").toLowerCase();
try {
host = InetAddress.getLocalHost().getHostName();
}
catch (UnknownHostException ex) {
Log.warn("Unable to determine local hostname.", ex);
}
version = new Version(3, 7, 0, Version.ReleaseStatus.Release, -1);
if ("true".equals(JiveGlobals.getXMLProperty("setup"))) {
setupMode = false;
}
if (isStandAlone()) {
Runtime.getRuntime().addShutdownHook(new ShutdownHookThread());
}
loader = Thread.currentThread().getContextClassLoader();
try {
CacheFactory.initialize();
} catch (InitializationException e) {
e.printStackTrace();
Log.error(e.getMessage(), e);
}
initialized = true;
}
/**
* Finish the setup process. Because this method is meant to be called from inside
* the Admin console plugin, it spawns its own thread to do the work so that the
* class loader is correct.
*/
public void finishSetup() {
if (!setupMode) {
return;
}
// Make sure that setup finished correctly.
if ("true".equals(JiveGlobals.getXMLProperty("setup"))) {
// Set the new server domain assigned during the setup process
name = JiveGlobals.getProperty("xmpp.domain").toLowerCase();
xmppServerInfo.setXMPPDomain(name);
// Update certificates (if required)
try {
// Check if keystore already has certificates for current domain
KeyStore ksKeys = SSLConfig.getKeyStore();
boolean dsaFound = CertificateManager.isDSACertificate(ksKeys, name);
boolean rsaFound = CertificateManager.isRSACertificate(ksKeys, name);
// No certificates were found so create new self-signed certificates
if (!dsaFound) {
CertificateManager.createDSACert(ksKeys, SSLConfig.getKeyPassword(),
name + "_dsa", "cn=" + name, "cn=" + name, "*." + name);
}
if (!rsaFound) {
CertificateManager.createRSACert(ksKeys, SSLConfig.getKeyPassword(),
name + "_rsa", "cn=" + name, "cn=" + name, "*." + name);
}
// Save new certificates into the key store
if (!dsaFound || !rsaFound) {
SSLConfig.saveStores();
}
} catch (Exception e) {
Log.error("Error generating self-signed certificates", e);
}
// Initialize list of admins now (before we restart Jetty)
AdminManager.getInstance().getAdminAccounts();
Thread finishSetup = new Thread() {
@Override
public void run() {
try {
if (isStandAlone()) {
// Always restart the HTTP server manager. This covers the case
// of changing the ports, as well as generating self-signed certificates.
// Wait a short period before shutting down the admin console.
// Otherwise, the page that requested the setup finish won't
// render properly!
Thread.sleep(1000);
((AdminConsolePlugin) pluginManager.getPlugin("admin")).restart();
// ((AdminConsolePlugin) pluginManager.getPlugin("admin")).shutdown();
// ((AdminConsolePlugin) pluginManager.getPlugin("admin")).startup();
}
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();
}
catch (Exception e) {
e.printStackTrace();
Log.error(e.getMessage(), e);
shutdownServer();
}
}
};
// Use the correct class loader.
finishSetup.setContextClassLoader(loader);
finishSetup.start();
// We can now safely indicate that setup has finished
setupMode = false;
// Update server info
xmppServerInfo = new XMPPServerInfoImpl(name, host, version, startDate, getConnectionManager());
}
}
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();
}
}
private void loadModules() {
// Load boot modules
loadModule(RoutingTableImpl.class.getName());
loadModule(AuditManagerImpl.class.getName());
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(IQTimeHandler.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(STUNService.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(ClearspaceManager.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();
}
/**
* Loads a module.
*
* @param module the name of the class that implements the Module interface.
*/
private void loadModule(String module) {
try {
Class modClass = loader.loadClass(module);
Module mod = (Module) modClass.newInstance();
this.modules.put(modClass, mod);
}
catch (Exception e) {
e.printStackTrace();
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
}
}
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);
}
}
}
/**
* <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();
}
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
}
}
}
/**
* Restarts the server and all it's modules only if the server is restartable. Otherwise do
* nothing.
*/
public void restart() {
if (isStandAlone() && isRestartable()) {
try {
Class wrapperClass = Class.forName(WRAPPER_CLASSNAME);
Method restartMethod = wrapperClass.getMethod("restart", (Class []) null);
restartMethod.invoke(null, (Object []) null);
}
catch (Exception e) {
Log.error("Could not restart container", e);
}
}
}
/**
* Restarts the HTTP server only when running in stand alone mode. The restart
* process will be done in another thread that will wait 1 second before doing
* the actual restart. The delay will give time to the page that requested the
* restart to fully render its content.
*/
public void restartHTTPServer() {
Thread restartThread = new Thread() {
@Override
public void run() {
if (isStandAlone()) {
// Restart the HTTP server manager. This covers the case
// of changing the ports, as well as generating self-signed certificates.
// Wait a short period before shutting down the admin console.
// Otherwise, this page won't render properly!
try {
Thread.sleep(1000);
((AdminConsolePlugin) pluginManager.getPlugin("admin")).shutdown();
((AdminConsolePlugin) pluginManager.getPlugin("admin")).startup();
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
restartThread.setContextClassLoader(loader);
restartThread.start();
}
/**
* Stops the server only if running in standalone mode. Do nothing if the server is running
* inside of another server.
*/
public void stop() {
// Only do a system exit if we're running standalone
if (isStandAlone()) {
// if we're in a wrapper, we have to tell the wrapper to shut us down
if (isRestartable()) {
try {
Class wrapperClass = Class.forName(WRAPPER_CLASSNAME);
Method stopMethod = wrapperClass.getMethod("stop", Integer.TYPE);
stopMethod.invoke(null, 0);
}
catch (Exception e) {
Log.error("Could not stop container", e);
}
}
else {
shutdownServer();
Thread shutdownThread = new ShutdownThread();
shutdownThread.setDaemon(true);
shutdownThread.start();
}
}
else {
// Close listening socket no matter what the condition is in order to be able
// to be restartable inside a container.
shutdownServer();
}
}
public boolean isSetupMode() {
return setupMode;
}
public boolean isRestartable() {
boolean restartable;
try {
restartable = Class.forName(WRAPPER_CLASSNAME) != null;
}
catch (ClassNotFoundException e) {
restartable = false;
}
return restartable;
}
/**
* Returns if the server is running in standalone mode. We consider that it's running in
* standalone if the "org.jivesoftware.openfire.starter.ServerStarter" class is present in the
* system.
*
* @return true if the server is running in standalone mode.
*/
public boolean isStandAlone() {
boolean standalone;
try {
standalone = Class.forName(STARTER_CLASSNAME) != null;
}
catch (ClassNotFoundException e) {
standalone = false;
}
return standalone;
}
/**
* 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.");
Log.error("Database could not be accessed", e);
throw new IllegalArgumentException(e);
}
finally {
DbConnectionManager.closeConnection(rs, pstmt, con);
}
}
/**
* Verifies that the given home guess is a real Openfire home directory.
* We do the verification by checking for the Openfire config file in
* the config dir of jiveHome.
*
* @param homeGuess a guess at the path to the home directory.
* @param jiveConfigName the name of the config file to check.
* @return a file pointing to the home directory or null if the
* home directory guess was wrong.
* @throws java.io.FileNotFoundException if there was a problem with the home
* directory provided
*/
private File verifyHome(String homeGuess, String jiveConfigName) throws FileNotFoundException {
File openfireHome = new File(homeGuess);
File configFile = new File(openfireHome, jiveConfigName);
if (!configFile.exists()) {
throw new FileNotFoundException();
}
else {
try {
return new File(openfireHome.getCanonicalPath());
}
catch (Exception ex) {
throw new FileNotFoundException();
}
}
}
/**
* <p>Retrieve the jive home for the container.</p>
*
* @throws FileNotFoundException If jiveHome could not be located
*/
private void locateOpenfire() throws FileNotFoundException {
String jiveConfigName = "conf" + File.separator + "openfire.xml";
// First, try to load it openfireHome as a system property.
if (openfireHome == null) {
String homeProperty = System.getProperty("openfireHome");
try {
if (homeProperty != null) {
openfireHome = verifyHome(homeProperty, jiveConfigName);
}
}
catch (FileNotFoundException fe) {
// Ignore.
}
}
// If we still don't have home, let's assume this is standalone
// and just look for home in a standard sub-dir location and verify
// by looking for the config file
if (openfireHome == null) {
try {
openfireHome = verifyHome("..", jiveConfigName).getCanonicalFile();
}
catch (FileNotFoundException fe) {
// Ignore.
}
catch (IOException ie) {
// Ignore.
}
}
// If home is still null, no outside process has set it and
// we have to attempt to load the value from openfire_init.xml,
// which must be in the classpath.
if (openfireHome == null) {
InputStream in = null;
try {
in = getClass().getResourceAsStream("/openfire_init.xml");
if (in != null) {
SAXReader reader = new SAXReader();
Document doc = reader.read(in);
String path = doc.getRootElement().getText();
try {
if (path != null) {
openfireHome = verifyHome(path, jiveConfigName);
}
}
catch (FileNotFoundException fe) {
fe.printStackTrace();
}
}
}
catch (Exception e) {
System.err.println("Error loading openfire_init.xml to find home.");
e.printStackTrace();
}
finally {
try {
if (in != null) {
in.close();
}
}
catch (Exception e) {
System.err.println("Could not close open connection");
e.printStackTrace();
}
}
}
if (openfireHome == null) {
System.err.println("Could not locate home");
throw new FileNotFoundException();
}
else {
// Set the home directory for the config file
JiveGlobals.setHomeDirectory(openfireHome.toString());
// Set the name of the config file
JiveGlobals.setConfigName(jiveConfigName);
}
}
/**
* <p>A thread to ensure the server shuts down no matter what.</p>
* <p>Spawned when stop() is called in standalone mode, we wait a few
* seconds then call system exit().</p>
*
* @author Iain Shigeoka
*/
private class ShutdownHookThread extends Thread {
/**
* <p>Logs the server shutdown.</p>
*/
@Override
public void run() {
shutdownServer();
Log.info("Server halted");
System.err.println("Server halted");
}
}
/**
* <p>A thread to ensure the server shuts down no matter what.</p>
* <p>Spawned when stop() is called in standalone mode, we wait a few
* seconds then call system exit().</p>
*
* @author Iain Shigeoka
*/
private class ShutdownThread extends Thread {
/**
* <p>Shuts down the JVM after a 5 second delay.</p>
*/
@Override
public void run() {
try {
Thread.sleep(5000);
// No matter what, we make sure it's dead
System.exit(0);
}
catch (InterruptedException e) {
// Ignore.
}
}
}
/**
* Makes a best effort attempt to shutdown the server
*/
private void shutdownServer() {
shuttingDown = true;
// Notify server listeners that the server is about to be stopped
for (XMPPServerListener listener : listeners) {
listener.serverStopping();
}
// Shutdown the task engine.
TaskEngine.getInstance().shutdown();
// If we don't have modules then the server has already been shutdown
if (modules.isEmpty()) {
return;
}
// Get all modules and stop and destroy them
for (Module module : modules.values()) {
module.stop();
module.destroy();
}
// Stop all plugins
if (pluginManager != null) {
pluginManager.shutdown();
}
modules.clear();
// Stop the Db connection manager.
DbConnectionManager.destroyConnectionProvider();
// hack to allow safe stopping
Log.info("Openfire stopped");
}
/**
* Returns true if the server is being shutdown.
*
* @return true if the server is being shutdown.
*/
public boolean isShuttingDown() {
return shuttingDown;
}
/**
* Returns the <code>ConnectionManager</code> registered with this server. The
* <code>ConnectionManager</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>ConnectionManager</code> registered with this server.
*/
public ConnectionManager getConnectionManager() {
return (ConnectionManager) modules.get(ConnectionManagerImpl.class);
}
/**
* Returns the <code>RoutingTable</code> registered with this server. The
* <code>RoutingTable</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>RoutingTable</code> registered with this server.
*/
public RoutingTable getRoutingTable() {
return (RoutingTable) modules.get(RoutingTableImpl.class);
}
/**
* Returns the <code>PacketDeliverer</code> registered with this server. The
* <code>PacketDeliverer</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>PacketDeliverer</code> registered with this server.
*/
public PacketDeliverer getPacketDeliverer() {
return (PacketDeliverer) modules.get(PacketDelivererImpl.class);
}
/**
* Returns the <code>RosterManager</code> registered with this server. The
* <code>RosterManager</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>RosterManager</code> registered with this server.
*/
public RosterManager getRosterManager() {
return (RosterManager) modules.get(RosterManager.class);
}
/**
* Returns the <code>PresenceManager</code> registered with this server. The
* <code>PresenceManager</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>PresenceManager</code> registered with this server.
*/
public PresenceManager getPresenceManager() {
return (PresenceManager) modules.get(PresenceManagerImpl.class);
}
/**
* Returns the <code>OfflineMessageStore</code> registered with this server. The
* <code>OfflineMessageStore</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>OfflineMessageStore</code> registered with this server.
*/
public OfflineMessageStore getOfflineMessageStore() {
return (OfflineMessageStore) modules.get(OfflineMessageStore.class);
}
/**
* Returns the <code>OfflineMessageStrategy</code> registered with this server. The
* <code>OfflineMessageStrategy</code> was registered with the server as a module while starting
* up the server.
*
* @return the <code>OfflineMessageStrategy</code> registered with this server.
*/
public OfflineMessageStrategy getOfflineMessageStrategy() {
return (OfflineMessageStrategy) modules.get(OfflineMessageStrategy.class);
}
/**
* Returns the <code>PacketRouter</code> registered with this server. The
* <code>PacketRouter</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>PacketRouter</code> registered with this server.
*/
public PacketRouter getPacketRouter() {
return (PacketRouter) modules.get(PacketRouterImpl.class);
}
/**
* Returns the <code>IQRegisterHandler</code> registered with this server. The
* <code>IQRegisterHandler</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>IQRegisterHandler</code> registered with this server.
*/
public IQRegisterHandler getIQRegisterHandler() {
return (IQRegisterHandler) modules.get(IQRegisterHandler.class);
}
/**
* Returns the <code>IQAuthHandler</code> registered with this server. The
* <code>IQAuthHandler</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>IQAuthHandler</code> registered with this server.
*/
public IQAuthHandler getIQAuthHandler() {
return (IQAuthHandler) modules.get(IQAuthHandler.class);
}
/**
* Returns the <code>IQPEPHandler</code> registered with this server. The
* <code>IQPEPHandler</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>IQPEPHandler</code> registered with this server.
*/
public IQPEPHandler getIQPEPHandler() {
return (IQPEPHandler) modules.get(IQPEPHandler.class);
}
/**
* Returns the <code>PluginManager</code> instance registered with this server.
*
* @return the PluginManager instance.
*/
public PluginManager getPluginManager() {
return pluginManager;
}
/**
* Returns the <code>PubSubModule</code> registered with this server. The
* <code>PubSubModule</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>PubSubModule</code> registered with this server.
*/
public PubSubModule getPubSubModule() {
return (PubSubModule) modules.get(PubSubModule.class);
}
/**
* Returns a list with all the modules registered with the server that inherit from IQHandler.
*
* @return a list with all the modules registered with the server that inherit from IQHandler.
*/
public List<IQHandler> getIQHandlers() {
List<IQHandler> answer = new ArrayList<IQHandler>();
for (Module module : modules.values()) {
if (module instanceof IQHandler) {
answer.add((IQHandler) module);
}
}
return answer;
}
/**
* Returns the <code>SessionManager</code> registered with this server. The
* <code>SessionManager</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>SessionManager</code> registered with this server.
*/
public SessionManager getSessionManager() {
return (SessionManager) modules.get(SessionManager.class);
}
/**
* Returns the <code>TransportHandler</code> registered with this server. The
* <code>TransportHandler</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>TransportHandler</code> registered with this server.
*/
public TransportHandler getTransportHandler() {
return (TransportHandler) modules.get(TransportHandler.class);
}
/**
* Returns the <code>PresenceUpdateHandler</code> registered with this server. The
* <code>PresenceUpdateHandler</code> was registered with the server as a module while starting
* up the server.
*
* @return the <code>PresenceUpdateHandler</code> registered with this server.
*/
public PresenceUpdateHandler getPresenceUpdateHandler() {
return (PresenceUpdateHandler) modules.get(PresenceUpdateHandler.class);
}
/**
* Returns the <code>PresenceSubscribeHandler</code> registered with this server. The
* <code>PresenceSubscribeHandler</code> was registered with the server as a module while
* starting up the server.
*
* @return the <code>PresenceSubscribeHandler</code> registered with this server.
*/
public PresenceSubscribeHandler getPresenceSubscribeHandler() {
return (PresenceSubscribeHandler) modules.get(PresenceSubscribeHandler.class);
}
/**
* Returns the <code>IQRouter</code> registered with this server. The
* <code>IQRouter</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>IQRouter</code> registered with this server.
*/
public IQRouter getIQRouter() {
return (IQRouter) modules.get(IQRouter.class);
}
/**
* Returns the <code>MessageRouter</code> registered with this server. The
* <code>MessageRouter</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>MessageRouter</code> registered with this server.
*/
public MessageRouter getMessageRouter() {
return (MessageRouter) modules.get(MessageRouter.class);
}
/**
* Returns the <code>PresenceRouter</code> registered with this server. The
* <code>PresenceRouter</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>PresenceRouter</code> registered with this server.
*/
public PresenceRouter getPresenceRouter() {
return (PresenceRouter) modules.get(PresenceRouter.class);
}
/**
* Returns the <code>MulticastRouter</code> registered with this server. The
* <code>MulticastRouter</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>MulticastRouter</code> registered with this server.
*/
public MulticastRouter getMulticastRouter() {
return (MulticastRouter) modules.get(MulticastRouter.class);
}
/**
* Returns the <code>UserManager</code> registered with this server. The
* <code>UserManager</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>UserManager</code> registered with this server.
*/
public UserManager getUserManager() {
return UserManager.getInstance();
}
/**
* Returns the <code>LockOutManager</code> registered with this server. The
* <code>LockOutManager</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>LockOutManager</code> registered with this server.
*/
public LockOutManager getLockOutManager() {
return LockOutManager.getInstance();
}
/**
* Returns the <code>UpdateManager</code> registered with this server. The
* <code>UpdateManager</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>UpdateManager</code> registered with this server.
*/
public UpdateManager getUpdateManager() {
return (UpdateManager) modules.get(UpdateManager.class);
}
/**
* Returns the <code>AuditManager</code> registered with this server. The
* <code>AuditManager</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>AuditManager</code> registered with this server.
*/
public AuditManager getAuditManager() {
return (AuditManager) modules.get(AuditManagerImpl.class);
}
/**
* Returns a list with all the modules that provide "discoverable" features.
*
* @return a list with all the modules that provide "discoverable" features.
*/
public List<ServerFeaturesProvider> getServerFeaturesProviders() {
List<ServerFeaturesProvider> answer = new ArrayList<ServerFeaturesProvider>();
for (Module module : modules.values()) {
if (module instanceof ServerFeaturesProvider) {
answer.add((ServerFeaturesProvider) module);
}
}
return answer;
}
/**
* Returns a list with all the modules that provide "discoverable" identities.
*
* @return a list with all the modules that provide "discoverable" identities.
*/
public List<ServerIdentitiesProvider> getServerIdentitiesProviders() {
List<ServerIdentitiesProvider> answer = new ArrayList<ServerIdentitiesProvider>();
for (Module module : modules.values()) {
if (module instanceof ServerIdentitiesProvider) {
answer.add((ServerIdentitiesProvider) module);
}
}
return answer;
}
/**
* Returns a list with all the modules that provide "discoverable" items associated with
* the server.
*
* @return a list with all the modules that provide "discoverable" items associated with
* the server.
*/
public List<ServerItemsProvider> getServerItemsProviders() {
List<ServerItemsProvider> answer = new ArrayList<ServerItemsProvider>();
for (Module module : modules.values()) {
if (module instanceof ServerItemsProvider) {
answer.add((ServerItemsProvider) module);
}
}
return answer;
}
/**
* Returns a list with all the modules that provide "discoverable" user identities.
*
* @return a list with all the modules that provide "discoverable" user identities.
*/
public List<UserIdentitiesProvider> getUserIdentitiesProviders() {
List<UserIdentitiesProvider> answer = new ArrayList<UserIdentitiesProvider>();
for (Module module : modules.values()) {
if (module instanceof UserIdentitiesProvider) {
answer.add((UserIdentitiesProvider) module);
}
}
return answer;
}
/**
* Returns a list with all the modules that provide "discoverable" items associated with
* users.
*
* @return a list with all the modules that provide "discoverable" items associated with
* users.
*/
public List<UserItemsProvider> getUserItemsProviders() {
List<UserItemsProvider> answer = new ArrayList<UserItemsProvider>();
for (Module module : modules.values()) {
if (module instanceof UserItemsProvider) {
answer.add((UserItemsProvider) module);
}
}
return answer;
}
/**
* Returns the <code>IQDiscoInfoHandler</code> registered with this server. The
* <code>IQDiscoInfoHandler</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>IQDiscoInfoHandler</code> registered with this server.
*/
public IQDiscoInfoHandler getIQDiscoInfoHandler() {
return (IQDiscoInfoHandler) modules.get(IQDiscoInfoHandler.class);
}
/**
* Returns the <code>IQDiscoItemsHandler</code> registered with this server. The
* <code>IQDiscoItemsHandler</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>IQDiscoItemsHandler</code> registered with this server.
*/
public IQDiscoItemsHandler getIQDiscoItemsHandler() {
return (IQDiscoItemsHandler) modules.get(IQDiscoItemsHandler.class);
}
/**
* Returns the <code>PrivateStorage</code> registered with this server. The
* <code>PrivateStorage</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>PrivateStorage</code> registered with this server.
*/
public PrivateStorage getPrivateStorage() {
return (PrivateStorage) modules.get(PrivateStorage.class);
}
/**
* Returns the <code>MultiUserChatManager</code> registered with this server. The
* <code>MultiUserChatManager</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>MultiUserChatManager</code> registered with this server.
*/
public MultiUserChatManager getMultiUserChatManager() {
return (MultiUserChatManager) modules.get(MultiUserChatManager.class);
}
/**
* Returns the <code>AdHocCommandHandler</code> registered with this server. The
* <code>AdHocCommandHandler</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>AdHocCommandHandler</code> registered with this server.
*/
public AdHocCommandHandler getAdHocCommandHandler() {
return (AdHocCommandHandler) modules.get(AdHocCommandHandler.class);
}
/**
* Returns the <code>FileTransferProxy</code> registered with this server. The
* <code>FileTransferProxy</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>FileTransferProxy</code> registered with this server.
*/
public FileTransferProxy getFileTransferProxy() {
return (FileTransferProxy) modules.get(FileTransferProxy.class);
}
/**
* Returns the <code>FileTransferManager</code> registered with this server. The
* <code>FileTransferManager</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>FileTransferProxy</code> registered with this server.
*/
public FileTransferManager getFileTransferManager() {
return (FileTransferManager) modules.get(DefaultFileTransferManager.class);
}
/**
* Returns the <code>MediaProxyService</code> registered with this server. The
* <code>MediaProxyService</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>MediaProxyService</code> registered with this server.
*/
public MediaProxyService getMediaProxyService() {
return (MediaProxyService) modules.get(MediaProxyService.class);
}
/**
* Returns the <code>STUNService</code> registered with this server. The
* <code>MediaProxyService</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>STUNService</code> registered with this server.
*/
public STUNService getSTUNService() {
return (STUNService) modules.get(STUNService.class);
}
/**
* Returns the <code>FlashCrossDomainHandler</code> registered with this server. The
* <code>FlashCrossDomainHandler</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>FlashCrossDomainHandler</code> registered with this server.
*/
public FlashCrossDomainHandler getFlashCrossDomainHandler() {
return (FlashCrossDomainHandler) modules.get(FlashCrossDomainHandler.class);
}
/**
* Returns the <code>VCardManager</code> registered with this server. The
* <code>VCardManager</code> was registered with the server as a module while starting up
* the server.
* @return the <code>VCardManager</code> registered with this server.
*/
public VCardManager getVCardManager() {
return VCardManager.getInstance();
}
/**
* Returns the <code>InternalComponentManager</code> registered with this server. The
* <code>InternalComponentManager</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>InternalComponentManager</code> registered with this server.
*/
private InternalComponentManager getComponentManager() {
return (InternalComponentManager) modules.get(InternalComponentManager.class);
}
/**
* Returns the locator to use to find sessions hosted in other cluster nodes. When not running
* in a cluster a <tt>null</tt> value is returned.
*
* @return the locator to use to find sessions hosted in other cluster nodes.
*/
public RemoteSessionLocator getRemoteSessionLocator() {
return remoteSessionLocator;
}
/**
* Sets the locator to use to find sessions hosted in other cluster nodes. When not running
* in a cluster set a <tt>null</tt> value.
*
* @param remoteSessionLocator the locator to use to find sessions hosted in other cluster nodes.
*/
public void setRemoteSessionLocator(RemoteSessionLocator remoteSessionLocator) {
this.remoteSessionLocator = remoteSessionLocator;
}
/**
* Returns whether or not the server has been started.
*
* @return whether or not the server has been started.
*/
public boolean isStarted() {
return started;
}
}