package com.lambdaworks.redis.cluster.models.slots; import java.util.*; import com.lambdaworks.redis.RedisURI; import com.lambdaworks.redis.cluster.models.partitions.RedisClusterNode; /** * Parser for redis <a href="http://redis.io/commands/cluster-slots">CLUSTER SLOTS</a> command output. * * @author Mark Paluch * @since 3.0 */ public class ClusterSlotsParser { /** * Utility constructor. */ private ClusterSlotsParser() { } /** * Parse the output of the redis CLUSTER SLOTS command and convert it to a list of * {@link com.lambdaworks.redis.cluster.models.slots.ClusterSlotRange} * * @param clusterSlotsOutput output of CLUSTER SLOTS command * @return List>ClusterSlotRange> */ public static List<ClusterSlotRange> parse(List<?> clusterSlotsOutput) { List<ClusterSlotRange> result = new ArrayList<>(); Map<String, RedisClusterNode> nodeCache = new HashMap<>(); for (Object o : clusterSlotsOutput) { if (!(o instanceof List)) { continue; } List<?> range = (List<?>) o; if (range.size() < 2) { continue; } ClusterSlotRange clusterSlotRange = parseRange(range, nodeCache); result.add(clusterSlotRange); } Collections.sort(result, new Comparator<ClusterSlotRange>() { @Override public int compare(ClusterSlotRange o1, ClusterSlotRange o2) { return o1.getFrom() - o2.getFrom(); } }); return Collections.unmodifiableList(result); } private static ClusterSlotRange parseRange(List<?> range, Map<String, RedisClusterNode> nodeCache) { Iterator<?> iterator = range.iterator(); int from = Math.toIntExact(getLongFromIterator(iterator, 0)); int to = Math.toIntExact(getLongFromIterator(iterator, 0)); RedisClusterNode master = null; List<RedisClusterNode> slaves = new ArrayList<>(); if (iterator.hasNext()) { master = getRedisClusterNode(iterator, nodeCache); if(master != null) { master.setFlags(Collections.singleton(RedisClusterNode.NodeFlag.MASTER)); Set<Integer> slots = new TreeSet<>(master.getSlots()); slots.addAll(createSlots(from, to)); master.setSlots(new ArrayList<>(slots)); } } while (iterator.hasNext()) { RedisClusterNode slave = getRedisClusterNode(iterator, nodeCache); if (slave != null) { slave.setSlaveOf(master.getNodeId()); slave.setFlags(Collections.singleton(RedisClusterNode.NodeFlag.SLAVE)); slaves.add(slave); } } return new ClusterSlotRange(from, to, master, Collections.unmodifiableList(slaves)); } private static List<Integer> createSlots(int from, int to) { List<Integer> slots = new ArrayList<>(); for (int i = from; i < to + 1; i++) { slots.add(i); } return slots; } private static RedisClusterNode getRedisClusterNode(Iterator<?> iterator, Map<String, RedisClusterNode> nodeCache) { Object element = iterator.next(); RedisClusterNode redisClusterNode = null; if (element instanceof List) { List<?> hostAndPortList = (List<?>) element; if (hostAndPortList.size() < 2) { return null; } Iterator<?> hostAndPortIterator = hostAndPortList.iterator(); String host = (String) hostAndPortIterator.next(); int port = Math.toIntExact(getLongFromIterator(hostAndPortIterator, 0)); String nodeId; if (hostAndPortIterator.hasNext()) { nodeId = (String) hostAndPortIterator.next(); redisClusterNode = nodeCache.get(nodeId); if(redisClusterNode == null) { redisClusterNode = createNode(host, port); nodeCache.put(nodeId, redisClusterNode); redisClusterNode.setNodeId(nodeId); } } else { String key = host + ":" + port; redisClusterNode = nodeCache.get(key); if(redisClusterNode == null) { redisClusterNode = createNode(host, port); nodeCache.put(key, redisClusterNode); } } } return redisClusterNode; } private static RedisClusterNode createNode(String host, int port) { RedisClusterNode redisClusterNode = new RedisClusterNode(); redisClusterNode.setUri(RedisURI.create(host, port)); redisClusterNode.setSlots(new ArrayList<>()); return redisClusterNode; } private static long getLongFromIterator(Iterator<?> iterator, long defaultValue) { if (iterator.hasNext()) { Object object = iterator.next(); if (object instanceof String) { return Long.parseLong((String) object); } if (object instanceof Number) { return ((Number) object).longValue(); } } return defaultValue; } }