Search in sources :

Example 1 with RoutableChannelHandler

use of org.jivesoftware.openfire.RoutableChannelHandler in project Openfire by igniterealtime.

the class RoutingTableImpl method ccMessage.

private void ccMessage(JID originalRecipient, Message message) {
    // We don't want to CC a message that is already a CC
    final Element receivedElement = message.getChildElement(Received.NAME, Received.NAMESPACE);
    final boolean isCC = receivedElement != null;
    if (message.getType() == Message.Type.chat && !isCC) {
        List<JID> routes = getRoutes(originalRecipient.asBareJID(), null);
        for (JID ccJid : routes) {
            // The receiving server MUST NOT send a forwarded copy to the full JID the original <message/> stanza was addressed to, as that recipient receives the original <message/> stanza.
            if (!ccJid.equals(originalRecipient)) {
                ClientSession clientSession = getClientRoute(ccJid);
                if (clientSession.isMessageCarbonsEnabled()) {
                    Message carbon = new Message();
                    // The wrapping message SHOULD maintain the same 'type' attribute value;
                    carbon.setType(message.getType());
                    // the 'from' attribute MUST be the Carbons-enabled user's bare JID
                    carbon.setFrom(ccJid.asBareJID());
                    // and the 'to' attribute MUST be the full JID of the resource receiving the copy
                    carbon.setTo(ccJid);
                    // The content of the wrapping message MUST contain a <received/> element qualified by the namespace "urn:xmpp:carbons:2", which itself contains a <forwarded/> element qualified by the namespace "urn:xmpp:forward:0" that contains the original <message/>.
                    carbon.addExtension(new Received(new Forwarded(message)));
                    try {
                        final RoutableChannelHandler localRoute = localRoutingTable.getRoute(ccJid);
                        if (localRoute != null) {
                            // This session is on a local cluster node
                            localRoute.process(carbon);
                        } else {
                            // The session is not on a local cluster node, so try a remote
                            final ClientRoute remoteRoute = getClientRouteForLocalUser(ccJid);
                            if (// If we're in a cluster
                            remotePacketRouter != null && // and we've found a route to the other node
                            remoteRoute != null && !remoteRoute.getNodeID().equals(XMPPServer.getInstance().getNodeID())) {
                                // and it really is a remote node
                                // Try and route the packet to the remote session
                                remotePacketRouter.routePacket(remoteRoute.getNodeID().toByteArray(), ccJid, carbon);
                            } else {
                                Log.warn("Unable to find route to CC remote user {}", ccJid);
                            }
                        }
                    } catch (UnauthorizedException e) {
                        Log.error("Unable to route packet {}", message, e);
                    }
                }
            }
        }
    }
}
Also used : JID(org.xmpp.packet.JID) Message(org.xmpp.packet.Message) Element(org.dom4j.Element) LocalClientSession(org.jivesoftware.openfire.session.LocalClientSession) ClientSession(org.jivesoftware.openfire.session.ClientSession) Received(org.jivesoftware.openfire.carbons.Received) Forwarded(org.jivesoftware.openfire.forward.Forwarded) UnauthorizedException(org.jivesoftware.openfire.auth.UnauthorizedException) RoutableChannelHandler(org.jivesoftware.openfire.RoutableChannelHandler)

Example 2 with RoutableChannelHandler

use of org.jivesoftware.openfire.RoutableChannelHandler in project Openfire by igniterealtime.

the class RoutingTableImpl method restoreCacheContent.

/**
 * When the local node is joining or leaving a cluster, {@link org.jivesoftware.util.cache.CacheFactory} will swap
 * the implementation used to instantiate caches. This causes the cache content to be 'reset': it will no longer
 * contain the data that's provided by the local node. This method restores data that's provided by the local node
 * in the cache. It is expected to be invoked right after joining ({@link #joinedCluster()} or leaving
 * ({@link #leftCluster()} a cluster.
 */
private void restoreCacheContent() {
    Log.debug("Restoring cache content for cache '{}' by adding all outgoing server routes that are connected to the local cluster node.", serversCache.getName());
    // Check if there are local s2s connections that are already in the cache for remote nodes
    Set<DomainPair> localServerRoutesToRemove = new HashSet<>();
    localRoutingTable.getServerRoutes().forEach(route -> route.getOutgoingDomainPairs().forEach(address -> {
        final Lock lock = serversCache.getLock(address);
        lock.lock();
        try {
            if (serversCache.containsKey(address)) {
                Log.info("We have an s2s connection to {}, but this connection also exists on other nodes. They are not allowed to both exist, so this local s2s connection will be terminated.", address);
                localServerRoutesToRemove.add(address);
            } else {
                serversCache.put(address, server.getNodeID());
            }
        } finally {
            lock.unlock();
        }
    }));
    for (DomainPair localServerRouteToRemove : localServerRoutesToRemove) {
        final RoutableChannelHandler route = localRoutingTable.getRoute(localServerRouteToRemove);
        if (route instanceof LocalOutgoingServerSession) {
            // That will result in the s2s connection actually being removed from the LocalRoutingTable.
            try {
                LocalOutgoingServerSession.class.cast(route).close();
            } catch (Exception e) {
                Log.warn("Failed to terminate the local s2s connection for " + localServerRouteToRemove + ".", e);
            }
        } else {
            Log.warn("Failed to terminate the local s2s connection for {} because it is a {} instead of a LocalOutgoingServerSession.", localServerRouteToRemove, route.getClass());
        }
    }
    Log.debug("Restoring cache content for cache '{}' by adding all component routes that are connected to the local cluster node.", componentsCache.getName());
    localRoutingTable.getComponentRoute().forEach(route -> CacheUtil.addValueToMultiValuedCache(componentsCache, route.getAddress().getDomain(), server.getNodeID(), HashSet::new));
    addLocalClientRoutesToCache();
}
Also used : Presence(org.xmpp.packet.Presence) LocalClientSession(org.jivesoftware.openfire.session.LocalClientSession) Forwarded(org.jivesoftware.openfire.forward.Forwarded) ClientSession(org.jivesoftware.openfire.session.ClientSession) Received(org.jivesoftware.openfire.carbons.Received) BasicModule(org.jivesoftware.openfire.container.BasicModule) CacheFactory(org.jivesoftware.util.cache.CacheFactory) ClusteredCacheEntryListener(org.jivesoftware.openfire.cluster.ClusteredCacheEntryListener) LoggerFactory(org.slf4j.LoggerFactory) JiveGlobals(org.jivesoftware.util.JiveGlobals) MessageRouter(org.jivesoftware.openfire.MessageRouter) ReverseLookupUpdatingCacheEntryListener(org.jivesoftware.util.cache.ReverseLookupUpdatingCacheEntryListener) PresenceUpdateHandler(org.jivesoftware.openfire.handler.PresenceUpdateHandler) Message(org.xmpp.packet.Message) OutgoingServerSession(org.jivesoftware.openfire.session.OutgoingServerSession) CacheUtil(org.jivesoftware.util.cache.CacheUtil) RemoteServerManager(org.jivesoftware.openfire.server.RemoteServerManager) Cache(org.jivesoftware.util.cache.Cache) RoutingTable(org.jivesoftware.openfire.RoutingTable) PresenceRouter(org.jivesoftware.openfire.PresenceRouter) ClusterManager(org.jivesoftware.openfire.cluster.ClusterManager) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Collectors(java.util.stream.Collectors) Stream(java.util.stream.Stream) ConnectionSettings(org.jivesoftware.openfire.session.ConnectionSettings) RemoteSessionLocator(org.jivesoftware.openfire.session.RemoteSessionLocator) java.util(java.util) PacketException(org.jivesoftware.openfire.PacketException) Multimap(com.google.common.collect.Multimap) ReverseLookupComputingCacheEntryListener(org.jivesoftware.util.cache.ReverseLookupComputingCacheEntryListener) JID(org.xmpp.packet.JID) Function(java.util.function.Function) UnauthorizedException(org.jivesoftware.openfire.auth.UnauthorizedException) ConcurrentMap(java.util.concurrent.ConcurrentMap) NodeID(org.jivesoftware.openfire.cluster.NodeID) XMPPServer(org.jivesoftware.openfire.XMPPServer) RoutableChannelHandler(org.jivesoftware.openfire.RoutableChannelHandler) RemotePacketRouter(org.jivesoftware.openfire.RemotePacketRouter) ClusterEventListener(org.jivesoftware.openfire.cluster.ClusterEventListener) DomainPair(org.jivesoftware.openfire.session.DomainPair) LocalOutgoingServerSession(org.jivesoftware.openfire.session.LocalOutgoingServerSession) Logger(org.slf4j.Logger) ConsistencyChecks(org.jivesoftware.util.cache.ConsistencyChecks) ExternalComponentManager(org.jivesoftware.openfire.component.ExternalComponentManager) AtomicLong(java.util.concurrent.atomic.AtomicLong) Lock(java.util.concurrent.locks.Lock) Packet(org.xmpp.packet.Packet) OutgoingSessionPromise(org.jivesoftware.openfire.server.OutgoingSessionPromise) Element(org.dom4j.Element) QName(org.dom4j.QName) IQRouter(org.jivesoftware.openfire.IQRouter) IQ(org.xmpp.packet.IQ) LocalOutgoingServerSession(org.jivesoftware.openfire.session.LocalOutgoingServerSession) DomainPair(org.jivesoftware.openfire.session.DomainPair) RoutableChannelHandler(org.jivesoftware.openfire.RoutableChannelHandler) PacketException(org.jivesoftware.openfire.PacketException) UnauthorizedException(org.jivesoftware.openfire.auth.UnauthorizedException) Lock(java.util.concurrent.locks.Lock)

Example 3 with RoutableChannelHandler

use of org.jivesoftware.openfire.RoutableChannelHandler in project Openfire by igniterealtime.

the class ConsistencyChecks method generateReportForRoutingTableComponentRoutes.

/**
 * Verifies that #componentsCache, #localRoutingTable#getComponentRoute and #componentsByClusterNode of
 * {@link org.jivesoftware.openfire.spi.RoutingTableImpl} are in a consistent state.
 * <p>
 * Note that this operation can be costly in terms of resource usage. Use with caution in large / busy systems.
 * <p>
 * The returned multi-map can contain up to four keys: info, fail, pass, data. All entry values are a human readable
 * description of a checked characteristic. When the state is consistent, no 'fail' entries will be returned.
 *
 * @param componentsCache         The cache that is used to share data across cluster nodes
 * @param localComponentRoutes    The data structure that keeps track of what data was added to the cache by the local cluster node.
 * @param componentsByClusterNode The data structure that keeps track of what data was added to the cache by the remote cluster nodes.
 * @return A consistency state report.
 */
public static Multimap<String, String> generateReportForRoutingTableComponentRoutes(@Nonnull final Cache<String, HashSet<NodeID>> componentsCache, @Nonnull final Collection<RoutableChannelHandler> localComponentRoutes, @Nonnull final HashMap<NodeID, Set<String>> componentsByClusterNode) {
    final Set<NodeID> clusterNodeIDs = ClusterManager.getNodesInfo().stream().map(ClusterNodeInfo::getNodeID).collect(Collectors.toSet());
    // Take a snapshots to reduce the chance of data changing while diagnostics are being performed
    final ConcurrentMap<String, HashSet<NodeID>> cache = new ConcurrentHashMap<>(componentsCache);
    final List<String> localComponentRoutesAddressing = localComponentRoutes.stream().map(r -> r.getAddress().toString()).collect(Collectors.toList());
    final Set<String> localComponentRoutesAddressingDuplicates = CollectionUtils.findDuplicates(localComponentRoutesAddressing);
    final List<String> remoteComponentRoutesAddressingWithNodeId = new ArrayList<>();
    for (Map.Entry<NodeID, Set<String>> entry : componentsByClusterNode.entrySet()) {
        for (String item : entry.getValue()) {
            remoteComponentRoutesAddressingWithNodeId.add(item + " (" + entry.getKey() + ")");
        }
    }
    final Multimap<String, String> result = HashMultimap.create();
    result.put("info", String.format("The cache named %s is used to share data in the cluster, which contains %d component routes.", componentsCache.getName(), cache.size()));
    result.put("info", String.format("LocalRoutingTable's getComponentRoute() response is used to track 'local' data to be restored after a cache switch-over. It tracks %d routes.", localComponentRoutesAddressing.size()));
    result.put("info", String.format("The field componentsByClusterNode is used to track data in the cache from every other cluster node. It contains %d routes for %d cluster nodes.", componentsByClusterNode.values().stream().reduce(0, (subtotal, values) -> subtotal + values.size(), Integer::sum), componentsByClusterNode.keySet().size()));
    result.put("data", String.format("%s contains these entries (these are shared in the cluster):\n%s", componentsCache.getName(), cache.entrySet().stream().map(e -> e.getKey() + "on nodes: " + e.getValue().stream().map(NodeID::toString).collect(Collectors.joining(", "))).collect(Collectors.joining("\n"))));
    result.put("data", String.format("LocalRoutingTable's getComponentRoute() response contains these entries (these represent 'local' data):\n%s", localComponentRoutes.stream().map(RoutableChannelHandler::getAddress).map(JID::toString).collect(Collectors.joining("\n"))));
    result.put("data", String.format("componentsByClusterNode contains these entries (these represent 'remote' data):\n%s", String.join("\n", remoteComponentRoutesAddressingWithNodeId)));
    if (localComponentRoutesAddressingDuplicates.isEmpty()) {
        result.put("pass", "There is no overlap in addressing of LocalRoutingTable's getComponentRoute() response (They are all unique values).");
    } else {
        result.put("fail", String.format("There is overlap in addressing of LocalRoutingTable's getComponentRoute() response (They are not all unique values). These %d values are duplicated: %s", localComponentRoutesAddressingDuplicates.size(), String.join(", ", localComponentRoutesAddressingDuplicates)));
    }
    if (!componentsByClusterNode.containsKey(XMPPServer.getInstance().getNodeID())) {
        result.put("pass", "componentsByClusterNode does not track data for the local cluster node.");
    } else {
        result.put("fail", "componentsByClusterNode tracks data for the local cluster node.");
    }
    if (clusterNodeIDs.containsAll(componentsByClusterNode.keySet())) {
        result.put("pass", "componentsByClusterNode tracks data for cluster nodes that are recognized in the cluster.");
    } else {
        result.put("fail", String.format("componentsByClusterNode tracks data for cluster nodes that are not recognized. All cluster nodeIDs as recognized: %s All cluster nodeIDs for which data is tracked: %s.", clusterNodeIDs.stream().map(NodeID::toString).collect(Collectors.joining(", ")), componentsByClusterNode.keySet().stream().map(NodeID::toString).collect(Collectors.joining(", "))));
    }
    final Set<String> nonCachedLocalComponentRouteAddressing = localComponentRoutesAddressing.stream().filter(v -> !cache.containsKey(v) || !cache.get(v).contains(XMPPServer.getInstance().getNodeID())).collect(Collectors.toSet());
    if (nonCachedLocalComponentRouteAddressing.isEmpty()) {
        result.put("pass", String.format("All elements in LocalRoutingTable's getComponentRoute() response exist in %s.", componentsCache.getName()));
    } else {
        result.put("fail", String.format("Not all elements in of LocalRoutingTable's getComponentRoute() response exist in %s. These %d entries do not: %s", componentsCache.getName(), nonCachedLocalComponentRouteAddressing.size(), nonCachedLocalComponentRouteAddressing.stream().map(v -> v + " on " + XMPPServer.getInstance().getNodeID()).collect(Collectors.joining(", "))));
    }
    final Set<String> nonCachedRemoteComponentRouteAddressing = new HashSet<>();
    for (final Map.Entry<NodeID, Set<String>> entry : componentsByClusterNode.entrySet()) {
        final NodeID remoteNodeID = entry.getKey();
        final Set<String> remoteComponentAddresses = entry.getValue();
        for (final String remoteComponentAddress : remoteComponentAddresses) {
            if (!cache.containsKey(remoteComponentAddress) || !cache.get(remoteComponentAddress).contains(remoteNodeID)) {
                nonCachedRemoteComponentRouteAddressing.add(remoteComponentAddress + " on " + remoteNodeID);
            }
        }
    }
    if (nonCachedRemoteComponentRouteAddressing.isEmpty()) {
        result.put("pass", String.format("All elements in componentsByClusterNode exist in %s.", componentsCache.getName()));
    } else {
        result.put("fail", String.format("Not all component routes in componentsByClusterNode exist in %s. These %d entries do not: %s", componentsCache.getName(), nonCachedRemoteComponentRouteAddressing.size(), String.join(", ", nonCachedLocalComponentRouteAddressing)));
    }
    final Set<String> nonLocallyStoredCachedComponentRouteAddressing = new HashSet<>();
    for (final Map.Entry<String, HashSet<NodeID>> entry : cache.entrySet()) {
        final String componentAddress = entry.getKey();
        final Set<NodeID> nodeIDs = entry.getValue();
        for (final NodeID nodeID : nodeIDs) {
            if (nodeID.equals(XMPPServer.getInstance().getNodeID())) {
                if (localComponentRoutes.stream().noneMatch(v -> v.getAddress().toString().equals(componentAddress))) {
                    nonLocallyStoredCachedComponentRouteAddressing.add(componentAddress + " on " + nodeID + " (the local cluster node)");
                }
            } else {
                if (!componentsByClusterNode.containsKey(nodeID) || !componentsByClusterNode.get(nodeID).contains(componentAddress)) {
                    nonLocallyStoredCachedComponentRouteAddressing.add(componentAddress + " on " + nodeID);
                }
            }
        }
    }
    if (nonLocallyStoredCachedComponentRouteAddressing.isEmpty()) {
        result.put("pass", String.format("All cache entries of %s exist in componentsByClusterNode and/or LocalRoutingTable's getComponentRoute() response.", componentsCache.getName()));
    } else {
        result.put("fail", String.format("Not all cache entries of %s exist in componentsByClusterNode and/or LocalRoutingTable's getComponentRoute() response. These %d entries do not: %s", componentsCache.getName(), nonLocallyStoredCachedComponentRouteAddressing.size(), String.join(", ", nonLocallyStoredCachedComponentRouteAddressing)));
    }
    return result;
}
Also used : java.util(java.util) ClusterManager(org.jivesoftware.openfire.cluster.ClusterManager) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) MUCRoom(org.jivesoftware.openfire.muc.MUCRoom) Multimap(com.google.common.collect.Multimap) StreamID(org.jivesoftware.openfire.StreamID) JID(org.xmpp.packet.JID) Collectors(java.util.stream.Collectors) org.jivesoftware.openfire.session(org.jivesoftware.openfire.session) ClientRoute(org.jivesoftware.openfire.spi.ClientRoute) ConcurrentMap(java.util.concurrent.ConcurrentMap) OccupantManager(org.jivesoftware.openfire.muc.spi.OccupantManager) ClusterNodeInfo(org.jivesoftware.openfire.cluster.ClusterNodeInfo) MUCRole(org.jivesoftware.openfire.muc.MUCRole) HashMultimap(com.google.common.collect.HashMultimap) NodeID(org.jivesoftware.openfire.cluster.NodeID) XMPPServer(org.jivesoftware.openfire.XMPPServer) RoutableChannelHandler(org.jivesoftware.openfire.RoutableChannelHandler) Nonnull(javax.annotation.Nonnull) CollectionUtils(org.jivesoftware.util.CollectionUtils) NodeID(org.jivesoftware.openfire.cluster.NodeID) RoutableChannelHandler(org.jivesoftware.openfire.RoutableChannelHandler) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) ConcurrentMap(java.util.concurrent.ConcurrentMap)

Example 4 with RoutableChannelHandler

use of org.jivesoftware.openfire.RoutableChannelHandler in project Openfire by igniterealtime.

the class RoutingTableImpl method routeToComponent.

/**
 * Routes packets that are sent to components of the XMPP domain (which are
 * subdomains of the XMPP domain)
 *
 * @param jid
 *            the recipient of the packet to route.
 * @param packet
 *            the packet to route.
 * @throws PacketException
 *             thrown if the packet is malformed (results in the sender's
 *             session being shutdown).
 * @return {@code true} if the packet was routed successfully,
 *         {@code false} otherwise.
 */
private boolean routeToComponent(JID jid, Packet packet) {
    if (!hasComponentRoute(jid) && !ExternalComponentManager.hasConfiguration(jid.getDomain())) {
        return false;
    }
    // First check if the component is being hosted in this JVM
    boolean routed = false;
    RoutableChannelHandler route = localRoutingTable.getRoute(new JID(null, jid.getDomain(), null, true));
    if (route != null) {
        try {
            route.process(packet);
            routed = true;
        } catch (UnauthorizedException e) {
            Log.error("Unable to route packet " + packet.toXML(), e);
        }
    } else {
        // Check if other cluster nodes are hosting this component
        Set<NodeID> nodes = componentsCache.get(jid.getDomain());
        if (nodes != null) {
            for (NodeID nodeID : nodes) {
                if (server.getNodeID().equals(nodeID)) {
                    // could have been added after our previous check)
                    try {
                        RoutableChannelHandler localRoute = localRoutingTable.getRoute(new JID(null, jid.getDomain(), null, true));
                        if (localRoute != null) {
                            localRoute.process(packet);
                            routed = true;
                            break;
                        }
                    } catch (UnauthorizedException e) {
                        Log.error("Unable to route packet " + packet.toXML(), e);
                    }
                } else {
                    // This is a route to a local component hosted in other node
                    if (remotePacketRouter != null) {
                        routed = remotePacketRouter.routePacket(nodeID.toByteArray(), jid, packet);
                        if (routed) {
                            break;
                        }
                    }
                }
            }
        }
    }
    return routed;
}
Also used : JID(org.xmpp.packet.JID) UnauthorizedException(org.jivesoftware.openfire.auth.UnauthorizedException) NodeID(org.jivesoftware.openfire.cluster.NodeID) RoutableChannelHandler(org.jivesoftware.openfire.RoutableChannelHandler)

Example 5 with RoutableChannelHandler

use of org.jivesoftware.openfire.RoutableChannelHandler in project Openfire by igniterealtime.

the class LocalRoutingTable method removeRoute.

/**
 * Removes a route of a local {@link RoutableChannelHandler}
 *
 * @param pair DomainPair associated to the route.
 */
void removeRoute(DomainPair pair) {
    final RoutableChannelHandler removed = routes.remove(pair);
    Log.trace("Remove local route '{}' (for pair: '{}') {}", removed == null ? "(null)" : removed.getAddress(), pair, removed != null ? "removed" : "not removed (was not present).");
}
Also used : RoutableChannelHandler(org.jivesoftware.openfire.RoutableChannelHandler)

Aggregations

RoutableChannelHandler (org.jivesoftware.openfire.RoutableChannelHandler)5 JID (org.xmpp.packet.JID)4 UnauthorizedException (org.jivesoftware.openfire.auth.UnauthorizedException)3 NodeID (org.jivesoftware.openfire.cluster.NodeID)3 Multimap (com.google.common.collect.Multimap)2 java.util (java.util)2 ConcurrentHashMap (java.util.concurrent.ConcurrentHashMap)2 ConcurrentMap (java.util.concurrent.ConcurrentMap)2 Collectors (java.util.stream.Collectors)2 Element (org.dom4j.Element)2 XMPPServer (org.jivesoftware.openfire.XMPPServer)2 Received (org.jivesoftware.openfire.carbons.Received)2 ClusterManager (org.jivesoftware.openfire.cluster.ClusterManager)2 Forwarded (org.jivesoftware.openfire.forward.Forwarded)2 ClientSession (org.jivesoftware.openfire.session.ClientSession)2 LocalClientSession (org.jivesoftware.openfire.session.LocalClientSession)2 Message (org.xmpp.packet.Message)2 HashMultimap (com.google.common.collect.HashMultimap)1 AtomicLong (java.util.concurrent.atomic.AtomicLong)1 Lock (java.util.concurrent.locks.Lock)1