| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.apache.vysper.xmpp.server; |
| |
| import static java.util.Objects.requireNonNull; |
| |
| 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.event.EventBus; |
| import org.apache.vysper.event.SimpleEventBus; |
| 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.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.SimpleStanzaHandlerExecutorFactory; |
| import org.apache.vysper.xmpp.protocol.StanzaHandler; |
| import org.apache.vysper.xmpp.protocol.StanzaHandlerExecutorFactory; |
| import org.apache.vysper.xmpp.protocol.StanzaHandlerLookup; |
| import org.apache.vysper.xmpp.protocol.StanzaProcessor; |
| import org.apache.vysper.xmpp.server.components.AlterableComponentRegistry; |
| import org.apache.vysper.xmpp.server.components.Component; |
| import org.apache.vysper.xmpp.server.components.SimpleComponentRegistry; |
| 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.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * |
| * @author The Apache MINA Project (dev@mina.apache.org) |
| */ |
| public class DefaultServerRuntimeContext implements InternalServerRuntimeContext, ModuleRegistry { |
| |
| private final Logger logger = LoggerFactory.getLogger(DefaultServerRuntimeContext.class); |
| |
| // basic internal data structures and configuration... |
| |
| /** |
| * directory where all available processors for incoming stanzas are located |
| */ |
| private final StanzaHandlerLookup stanzaHandlerLookup; |
| |
| /** |
| * the 'domain' the server is directly serving for |
| */ |
| private final Entity serverEntity; |
| |
| /** |
| * feature configuration |
| */ |
| private final ServerFeatures serverFeatures; |
| |
| /** |
| * the Secure Socket Engine in use |
| */ |
| private SSLContext sslContext = null; |
| |
| /** |
| * generates unique session ids |
| */ |
| private final UUIDGenerator sessionIdGenerator = new JVMBuiltinUUIDGenerator(); |
| |
| // basic services the server is using... |
| |
| /** |
| * 'output stream': receives stanzas issued by a session, which are going to |
| * other sessions/servers |
| */ |
| private final 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 final XMPPServerConnectorRegistry serverConnectorRegistry; |
| |
| /** |
| * holds the storage services |
| */ |
| private StorageProviderRegistry storageProviderRegistry = new OpenStorageProviderRegistry(); |
| |
| /** |
| * collection of all other services, which are mostly add-ons to the minimal |
| * setup |
| */ |
| private final 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 |
| */ |
| private final AlterableComponentRegistry componentRegistry; |
| |
| private final SimpleEventBus eventBus; |
| |
| private final ComponentStanzaProcessorFactory componentStanzaProcessorFactory; |
| |
| public DefaultServerRuntimeContext(Entity serverEntity, StanzaRelay stanzaRelay, StanzaProcessor stanzaProcessor, |
| AlterableComponentRegistry componentRegistry, ResourceRegistry resourceRegistry, |
| ServerFeatures serverFeatures, List<HandlerDictionary> dictionaries) { |
| this.serverEntity = serverEntity; |
| this.stanzaRelay = stanzaRelay; |
| this.componentRegistry = requireNonNull(componentRegistry); |
| StanzaHandlerExecutorFactory simpleStanzaHandlerExecutorFactory = new SimpleStanzaHandlerExecutorFactory( |
| stanzaRelay); |
| this.serverConnectorRegistry = new DefaultXMPPServerConnectorRegistry(this, |
| simpleStanzaHandlerExecutorFactory, stanzaProcessor); |
| this.stanzaHandlerLookup = new StanzaHandlerLookup(this); |
| this.eventBus = new SimpleEventBus(); |
| this.serverFeatures = serverFeatures; |
| this.resourceRegistry = resourceRegistry; |
| this.componentStanzaProcessorFactory = new ComponentStanzaProcessorFactory( |
| simpleStanzaHandlerExecutorFactory); |
| |
| addDictionaries(dictionaries); |
| } |
| |
| public DefaultServerRuntimeContext(Entity serverEntity, StanzaRelay stanzaRelay, |
| StorageProviderRegistry storageProviderRegistry) { |
| this(serverEntity, stanzaRelay); |
| this.storageProviderRegistry = storageProviderRegistry; |
| } |
| |
| public DefaultServerRuntimeContext(Entity serverEntity, StanzaRelay stanzaRelay) { |
| this(serverEntity, stanzaRelay, new ProtocolWorker(new SimpleStanzaHandlerExecutorFactory(stanzaRelay)), |
| new SimpleComponentRegistry(serverEntity), new DefaultResourceRegistry(), new ServerFeatures(), |
| Collections.emptyList()); |
| } |
| |
| /** |
| * 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; |
| } |
| |
| @Override |
| public StanzaHandler getHandler(Stanza stanza) { |
| return stanzaHandlerLookup.getHandler(stanza); |
| } |
| |
| @Override |
| public String getNextSessionId() { |
| return sessionIdGenerator.create(); |
| } |
| |
| @Override |
| public Entity getServerEntity() { |
| return serverEntity; |
| } |
| |
| @Override |
| public String getDefaultXMLLang() { |
| return "en_US"; // TODO must be configurable as of RFC3920 |
| } |
| |
| public StanzaRelay getStanzaRelay() { |
| return stanzaRelay; |
| } |
| |
| @Override |
| public ServerFeatures getServerFeatures() { |
| return serverFeatures; |
| } |
| |
| @Override |
| 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); |
| } |
| } |
| |
| @Override |
| public SSLContext getSslContext() { |
| return sslContext; |
| } |
| |
| /** |
| * @deprecated use {@link #getStorageProvider(Class)} with |
| * {@link UserAuthentication}.class instead |
| */ |
| @Override |
| public UserAuthentication getUserAuthentication() { |
| return storageProviderRegistry.retrieve(UserAuthentication.class); |
| } |
| |
| @Override |
| public ResourceRegistry getResourceRegistry() { |
| return resourceRegistry; |
| } |
| |
| @Override |
| public LatestPresenceCache getPresenceCache() { |
| return presenceCache; |
| } |
| |
| /** |
| * add a runtime service. makes the service dynamically discoverable at runtime. |
| * |
| * @param service |
| */ |
| @Override |
| 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 |
| */ |
| @Override |
| 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 |
| */ |
| @Override |
| public <T extends StorageProvider> T getStorageProvider(Class<T> 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 |
| */ |
| @Override |
| 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 module |
| */ |
| @Override |
| 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); |
| } |
| |
| } |
| |
| module.getEventListenerDictionary().ifPresent(eventBus::addDictionary); |
| |
| if (module instanceof Component) { |
| registerComponent((Component) module); |
| } |
| |
| modules.add(module); |
| } |
| |
| @Override |
| public List<Module> getModules() { |
| return Collections.unmodifiableList(modules); |
| } |
| |
| @Override |
| public <T> T getModule(Class<T> clazz) { |
| for (Module module : modules) { |
| if (module.getClass().equals(clazz)) |
| return (T) module; |
| } |
| return null; |
| } |
| |
| @Override |
| public EventBus getEventBus() { |
| return eventBus; |
| } |
| |
| @Override |
| public void registerComponent(Component component) { |
| componentRegistry.registerComponent(componentStanzaProcessorFactory, component); |
| } |
| |
| @Override |
| public boolean hasComponentStanzaProcessor(Entity entity) { |
| return componentRegistry.getComponentStanzaProcessor(entity) != null; |
| } |
| |
| } |