package nl.topicus.konijn.xmpp.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.net.ssl.SSLContext;
import org.apache.vysper.storage.OpenStorageProviderRegistry;
import org.apache.vysper.storage.StorageProvider;
import org.apache.vysper.storage.StorageProviderRegistry;
import org.apache.vysper.xmpp.addressing.Entity;
import org.apache.vysper.xmpp.addressing.EntityUtils;
import org.apache.vysper.xmpp.authentication.UserAuthentication;
import org.apache.vysper.xmpp.cryptography.TLSContextFactory;
import org.apache.vysper.xmpp.delivery.StanzaRelay;
import org.apache.vysper.xmpp.modules.Module;
import org.apache.vysper.xmpp.modules.ModuleRegistry;
import org.apache.vysper.xmpp.modules.ServerRuntimeContextService;
import org.apache.vysper.xmpp.protocol.HandlerDictionary;
import org.apache.vysper.xmpp.protocol.ProtocolWorker;
import org.apache.vysper.xmpp.protocol.StanzaHandler;
import org.apache.vysper.xmpp.protocol.StanzaHandlerLookup;
import org.apache.vysper.xmpp.protocol.StanzaProcessor;
import org.apache.vysper.xmpp.server.DefaultServerRuntimeContext;
import org.apache.vysper.xmpp.server.ServerFeatures;
import org.apache.vysper.xmpp.server.ServerRuntimeContext;
import org.apache.vysper.xmpp.server.components.Component;
import org.apache.vysper.xmpp.server.s2s.DefaultXMPPServerConnectorRegistry;
import org.apache.vysper.xmpp.server.s2s.XMPPServerConnectorRegistry;
import org.apache.vysper.xmpp.stanza.Stanza;
import org.apache.vysper.xmpp.state.presence.LatestPresenceCache;
import org.apache.vysper.xmpp.state.presence.SimplePresenceCache;
import org.apache.vysper.xmpp.state.resourcebinding.DefaultResourceRegistry;
import org.apache.vysper.xmpp.state.resourcebinding.ResourceRegistry;
import org.apache.vysper.xmpp.uuid.JVMBuiltinUUIDGenerator;
import org.apache.vysper.xmpp.uuid.UUIDGenerator;
import org.apache.wicket.Application;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author The Apache MINA Project (dev@mina.apache.org)
*/
public class MyServerRuntimeContext implements ServerRuntimeContext,
ModuleRegistry {
final Logger logger = LoggerFactory.getLogger(MyServerRuntimeContext.class);
// basic internal data structures and configuration...
private Application application;
/**
* directory where all available processors for incoming stanzas are located
*/
private StanzaHandlerLookup stanzaHandlerLookup;
/**
* the 'domain' the server is directly serving for
*/
private Entity serverEntity;
/**
* feature configuration
*/
private ServerFeatures serverFeatures = new ServerFeatures();
/**
* the Secure Socket Engine in use
*/
private SSLContext sslContext = null;
/**
* generates unique session ids
*/
private UUIDGenerator sessionIdGenerator = new JVMBuiltinUUIDGenerator();
// basic services the server is using...
/**
* 'input stream': receives stanzas issued by client sessions to be handled
* by the server
*/
private StanzaProcessor stanzaProcessor = new ProtocolWorker();
/**
* 'output stream': receives stanzas issued by a session, which are going to
* other sessions/servers
*/
private StanzaRelay stanzaRelay;
/**
* administrate and query resources and sessions
*/
private ResourceRegistry resourceRegistry;
/**
* holds the latest presence stanza for a resource
*/
private LatestPresenceCache presenceCache = new SimplePresenceCache();
private XMPPServerConnectorRegistry serverConnectorRegistry = new DefaultXMPPServerConnectorRegistry(
this);
/**
* holds the storage services
*/
private StorageProviderRegistry storageProviderRegistry = new OpenStorageProviderRegistry();
/**
* collection of all other services, which are mostly add-ons to the minimal
* setup
*/
final private Map<String, ServerRuntimeContextService> serverRuntimeContextServiceMap = new HashMap<String, ServerRuntimeContextService>();
private List<Module> modules = new ArrayList<Module>();
/**
* map of all registered components, index by the subdomain they are
* registered for
*/
protected final Map<String, Component> componentMap = new HashMap<String, Component>();
public MyServerRuntimeContext(Entity serverEntity, StanzaRelay stanzaRelay) {
this.serverEntity = serverEntity;
this.stanzaRelay = stanzaRelay;
this.resourceRegistry = new DefaultResourceRegistry();
this.stanzaHandlerLookup = new StanzaHandlerLookup(this);
}
public MyServerRuntimeContext(Entity serverEntity, StanzaRelay stanzaRelay,
StorageProviderRegistry storageProviderRegistry) {
this(serverEntity, stanzaRelay);
this.storageProviderRegistry = storageProviderRegistry;
}
public MyServerRuntimeContext(Entity serverEntity, StanzaRelay stanzaRelay,
ServerFeatures serverFeatures,
List<HandlerDictionary> dictionaries,
ResourceRegistry resourceRegistry) {
this(serverEntity, stanzaRelay);
this.serverFeatures = serverFeatures;
this.resourceRegistry = resourceRegistry;
addDictionaries(dictionaries);
}
/**
* change the presence cache implementation. this is a setter intended to be
* used at initialisation time. (thus, this method is not part of
* ServerRuntimeContext.
*
* @param presenceCache
*/
public void setPresenceCache(LatestPresenceCache presenceCache) {
this.presenceCache = presenceCache;
}
public StanzaHandler getHandler(Stanza stanza) {
return stanzaHandlerLookup.getHandler(stanza);
}
public String getNextSessionId() {
return sessionIdGenerator.create();
}
public Entity getServerEnitity() {
return serverEntity;
}
public String getDefaultXMLLang() {
return "en_US"; // TODO must be configurable as of RFC3920
}
public StanzaProcessor getStanzaProcessor() {
return stanzaProcessor;
}
public StanzaRelay getStanzaRelay() {
return stanzaRelay;
}
public ServerFeatures getServerFeatures() {
return serverFeatures;
}
public XMPPServerConnectorRegistry getServerConnectorRegistry() {
return serverConnectorRegistry;
}
public void addDictionary(HandlerDictionary namespaceHandlerDictionary) {
stanzaHandlerLookup.addDictionary(namespaceHandlerDictionary);
}
protected void addDictionaries(List<HandlerDictionary> dictionaries) {
for (HandlerDictionary dictionary : dictionaries) {
addDictionary(dictionary);
}
}
public void setTlsContextFactory(TLSContextFactory tlsContextFactory) {
try {
//sslContext = tlsContextFactory.getSSLContext();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public SSLContext getSslContext() {
return sslContext;
}
/**
* @deprecated use {@link #getStorageProvider(Class)} with
* {@link UserAuthentication}.class instead
*/
public UserAuthentication getUserAuthentication() {
return (UserAuthentication) storageProviderRegistry
.retrieve(UserAuthentication.class);
}
public ResourceRegistry getResourceRegistry() {
return resourceRegistry;
}
public LatestPresenceCache getPresenceCache() {
return presenceCache;
}
/**
* add a runtime service. makes the service dynamically discoverable at
* runtime.
*
* @param service
*/
public void registerServerRuntimeContextService(
ServerRuntimeContextService service) {
if (service == null)
throw new IllegalStateException("service must not be null");
if (serverRuntimeContextServiceMap.get(service.getServiceName()) != null) {
throw new IllegalStateException("service already registered: "
+ service.getServiceName());
}
serverRuntimeContextServiceMap.put(service.getServiceName(), service);
}
/**
* retrieves a previously registered runtime context service. The
* RosterManager is a good example of such a service. This allows for
* modules, extensions and other services to discover their dependencies at
* runtime.
*
* @see org.apache.vysper.xmpp.server.DefaultServerRuntimeContext#getStorageProvider(Class)
* @param name
* @return
*/
public ServerRuntimeContextService getServerRuntimeContextService(
String name) {
return serverRuntimeContextServiceMap.get(name);
}
/**
* adds a whole set of storage providers at once to the system.
*
* @param storageProviderRegistry
*/
public void setStorageProviderRegistry(
StorageProviderRegistry storageProviderRegistry) {
logger.info("replacing the storage provider registry with "
+ storageProviderRegistry.getClass().getCanonicalName());
this.storageProviderRegistry = storageProviderRegistry;
}
/**
* retrieves a particular storage provider.
*
* @param clazz
* @return
*/
public StorageProvider getStorageProvider(
Class<? extends StorageProvider> clazz) {
return storageProviderRegistry.retrieve(clazz);
}
/**
* adds and initializes a list of Modules. A module extends the server's
* functionality by adding an XMPP extension ('XEP') to it. (More) Modules
* can be added at runtime. This approach has an advantage over adding
* modules one by one, in that it allows for a better dependency management:
* all modules from the list to first discover each other before
* initialize() get's called for every one of them.
*
* @see org.apache.vysper.xmpp.server.DefaultServerRuntimeContext#registerServerRuntimeContextService(org.apache.vysper.xmpp.modules.ServerRuntimeContextService)
* @see org.apache.vysper.xmpp.server.DefaultServerRuntimeContext#getServerRuntimeContextService(String)
* @see org.apache.vysper.xmpp.modules.Module
* @param modules
* List of modules
*/
public void addModules(List<Module> modules) {
for (Module module : modules) {
addModuleInternal(module);
}
for (Module module : modules) {
module.initialize(this);
}
}
/**
* adds and initializes a single Module. a module extends the server's
* functionality by adding an XMPP extension ('XEP') to it.
*
* @see org.apache.vysper.xmpp.modules.Module
* @see DefaultServerRuntimeContext#addModules(java.util.List) for adding a
* number of modules at once
* @param modules
*/
public void addModule(Module module) {
addModuleInternal(module);
module.initialize(this);
}
protected void addModuleInternal(Module module) {
logger.info("adding module... {} ({})", module.getName(),
module.getVersion());
List<ServerRuntimeContextService> serviceList = module
.getServerServices();
if (serviceList != null) {
for (ServerRuntimeContextService serverRuntimeContextService : serviceList) {
registerServerRuntimeContextService(serverRuntimeContextService);
// if a storage service, also register there
if (serverRuntimeContextService instanceof StorageProvider) {
StorageProvider storageProvider = (StorageProvider) serverRuntimeContextService;
storageProviderRegistry.add(storageProvider);
}
}
}
List<HandlerDictionary> handlerDictionaryList = module
.getHandlerDictionaries();
if (handlerDictionaryList != null) {
for (HandlerDictionary handlerDictionary : handlerDictionaryList) {
addDictionary(handlerDictionary);
}
}
if (module instanceof Component) {
registerComponent((Component) module);
}
modules.add(module);
}
public List<Module> getModules() {
return Collections.unmodifiableList(modules);
}
@SuppressWarnings("unchecked")
public <T> T getModule(Class<T> clazz) {
for (Module module : modules) {
if (module.getClass().equals(clazz))
return (T) module;
}
return null;
}
public void registerComponent(Component component) {
componentMap.put(component.getSubdomain(), component);
}
public StanzaProcessor getComponentStanzaProcessor(Entity entity) {
String serverDomain = getServerEnitity().getDomain();
if (!EntityUtils
.isAddressingServerComponent(entity, getServerEnitity())) {
return null;
}
String domain = entity.getDomain();
String subdomain = domain.replace("." + serverDomain, "");
Component component = componentMap.get(subdomain);
if (component == null)
return null;
return component.getStanzaProcessor();
}
public void setApplication(Application application) {
this.application = application;
}
public Application getApplication() {
return application;
}
}