/* * Copyright 2014-2016 CyberVision, Inc. * * 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.kaaproject.kaa.server.operations.service.event; import org.kaaproject.kaa.common.hash.EndpointObjectHash; import org.kaaproject.kaa.server.common.Base64Util; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; public class RouteTable { private final Map<RouteTableKey, Map<String, RouteTableAddress>> routes; private final Map<RouteTableAddress, Set<String>> reportedAddressMap; private final Set<String> remoteServersSet; private final Map<RouteTableAddress, Set<RouteTableKey>> localAddressMap; /** * Instantiates new route table. */ public RouteTable() { super(); routes = new HashMap<RouteTableKey, Map<String, RouteTableAddress>>(); reportedAddressMap = new HashMap<>(); remoteServersSet = new HashSet<>(); localAddressMap = new HashMap<>(); } /** * Add route to table. * * @param key the key of RouteTable associative array * @param address the value of RouteTable associative array */ public void add(RouteTableKey key, RouteTableAddress address) { Map<String, RouteTableAddress> directionRoutes = routes.get(key); if (directionRoutes == null) { directionRoutes = new HashMap<>(); routes.put(key, directionRoutes); } directionRoutes.put(Base64Util.encode(address.getEndpointKey().getData()), address); if (address.isLocal()) { Set<RouteTableKey> routeKeys = localAddressMap.get(address); if (routeKeys == null) { routeKeys = new HashSet<>(); localAddressMap.put(address, routeKeys); } routeKeys.add(key); } else { remoteServersSet.add(address.getServerId()); } } /** * Get route from table. * * @param key the key of RouteTable associative array * @param target the specific target * @return collection of addresses */ public Collection<RouteTableAddress> getRoutes(RouteTableKey key, String target) { Map<String, RouteTableAddress> directionRoutes = routes.get(key); if (directionRoutes != null) { if (target == null) { return directionRoutes.values(); } else { RouteTableAddress address = directionRoutes.get(target); if (address != null) { return Collections.singletonList(address); } } } return Collections.emptyList(); } /** * Get routes from table by key set. * * @param keys the keys * @param target the specific target * @return set of addresses */ public Set<RouteTableAddress> getRoutes(Set<RouteTableKey> keys, String target) { Set<RouteTableAddress> result = new HashSet<>(); for (RouteTableKey key : keys) { result.addAll(getRoutes(key, target)); } return result; } public Set<RouteTableAddress> getAllLocalRoutes() { return Collections.unmodifiableSet(localAddressMap.keySet()); } /** * Returns local route table keys. * * @param localAddress local address * @return local route table keys */ public Set<RouteTableKey> getLocalRouteTableKeys(RouteTableAddress localAddress) { Set<RouteTableKey> keys = localAddressMap.get(localAddress); if (keys != null) { return keys; } else { return Collections.emptySet(); } } /** * Clear remote server data. * * @param serverId server identifier */ public void clearRemoteServerData(String serverId) { remoteServersSet.remove(serverId); clearReportedAddressMap(serverId); clearRoutes(serverId); } /** * Returns a remote server list. * * @return remote server list */ public Set<String> getRemoteServers() { return Collections.unmodifiableSet(remoteServersSet); } /** * Register a route info report. * * @param localAddresses local address * @param serverId server identifier */ public void registerRouteInfoReport(Set<RouteTableAddress> localAddresses, String serverId) { for (RouteTableAddress address : localAddresses) { Set<String> serverIds = reportedAddressMap.get(address); if (serverIds == null) { serverIds = new HashSet<>(); reportedAddressMap.put(address, serverIds); } serverIds.add(serverId); } } /** * Returns whether delivery is required. * * @param serverId server identifier * @param address address * @return true if delivery is required otherwise false */ public boolean isDeliveryRequired(String serverId, RouteTableAddress address) { Set<String> servers = reportedAddressMap.get(address); return servers == null || !servers.contains(serverId); } /** * Remove a local address. * * @param endpoint endpoint object hash * @return removed address */ public RouteTableAddress removeLocal(EndpointObjectHash endpoint) { clearRoutes(endpoint); RouteTableAddress addressToRemove = null; for (RouteTableAddress address : localAddressMap.keySet()) { if (address.isLocal() && endpoint.equals(address.getEndpointKey())) { addressToRemove = address; break; } } if (addressToRemove != null) { reportedAddressMap.remove(addressToRemove); localAddressMap.remove(addressToRemove); } return addressToRemove; } private void clearRoutes(String serverId) { Set<Entry<RouteTableKey, Map<String, RouteTableAddress>>> entrySet = routes.entrySet(); Iterator<Entry<RouteTableKey, Map<String, RouteTableAddress>>> iterator = entrySet.iterator(); while (iterator.hasNext()) { Entry<RouteTableKey, Map<String, RouteTableAddress>> entry = iterator.next(); Map<String, RouteTableAddress> addressMap = entry.getValue(); Set<Entry<String, RouteTableAddress>> innerEntrySet = addressMap.entrySet(); Iterator<Entry<String, RouteTableAddress>> innerIterator = innerEntrySet.iterator(); while (innerIterator.hasNext()) { Entry<String, RouteTableAddress> innerEntry = innerIterator.next(); if (serverId.equals(innerEntry.getValue().getServerId())) { innerIterator.remove(); } } if (addressMap.isEmpty()) { iterator.remove(); } } } /** * Clear routes for a specified endpoint. * * @param endpoint endpoint object hash */ private void clearRoutes(EndpointObjectHash endpoint) { Set<Entry<RouteTableKey, Map<String, RouteTableAddress>>> entrySet = routes.entrySet(); Iterator<Entry<RouteTableKey, Map<String, RouteTableAddress>>> iterator = entrySet.iterator(); while (iterator.hasNext()) { Entry<RouteTableKey, Map<String, RouteTableAddress>> entry = iterator.next(); Map<String, RouteTableAddress> addressMap = entry.getValue(); Set<Entry<String, RouteTableAddress>> innerEntrySet = addressMap.entrySet(); Iterator<Entry<String, RouteTableAddress>> innerIterator = innerEntrySet.iterator(); while (innerIterator.hasNext()) { Entry<String, RouteTableAddress> innerEntry = innerIterator.next(); if (endpoint.equals(innerEntry.getValue().getEndpointKey())) { innerIterator.remove(); } } if (addressMap.isEmpty()) { iterator.remove(); } } } private void clearReportedAddressMap(String serverId) { Set<Entry<RouteTableAddress, Set<String>>> entrySet = reportedAddressMap.entrySet(); Iterator<Entry<RouteTableAddress, Set<String>>> iterator = entrySet.iterator(); while (iterator.hasNext()) { Entry<RouteTableAddress, Set<String>> entry = iterator.next(); Set<String> servers = entry.getValue(); servers.remove(serverId); if (servers.isEmpty()) { iterator.remove(); } } } /** * Remove a route table address. * * @param address address */ public void removeByAddress(RouteTableAddress address) { Set<Entry<RouteTableKey, Map<String, RouteTableAddress>>> entrySet = routes.entrySet(); Iterator<Entry<RouteTableKey, Map<String, RouteTableAddress>>> iterator = entrySet.iterator(); while (iterator.hasNext()) { Entry<RouteTableKey, Map<String, RouteTableAddress>> entry = iterator.next(); Map<String, RouteTableAddress> addressMap = entry.getValue(); Set<Entry<String, RouteTableAddress>> innerEntrySet = addressMap.entrySet(); Iterator<Entry<String, RouteTableAddress>> innerIterator = innerEntrySet.iterator(); while (innerIterator.hasNext()) { Entry<String, RouteTableAddress> innerEntry = innerIterator.next(); if (address.equals(innerEntry.getValue())) { innerIterator.remove(); } } if (addressMap.isEmpty()) { iterator.remove(); } } } }