package com.lambdaworks.redis.models.role; import java.util.*; import com.google.common.net.HostAndPort; import com.lambdaworks.redis.internal.LettuceAssert; /** * Parser for redis <a href="http://redis.io/commands/role">ROLE</a> command output. * * @author Mark Paluch * @since 3.0 */ @SuppressWarnings("serial") public class RoleParser { protected static final Map<String, RedisInstance.Role> ROLE_MAPPING; protected static final Map<String, RedisSlaveInstance.State> SLAVE_STATE_MAPPING; static { Map<String, RedisInstance.Role> roleMap = new HashMap<>(); roleMap.put("master", RedisInstance.Role.MASTER); roleMap.put("slave", RedisInstance.Role.SLAVE); roleMap.put("sentinel", RedisInstance.Role.SENTINEL); ROLE_MAPPING = Collections.unmodifiableMap(roleMap); Map<String, RedisSlaveInstance.State> slaveStateMap = new HashMap<>(); slaveStateMap.put("connect", RedisSlaveInstance.State.CONNECT); slaveStateMap.put("connected", RedisSlaveInstance.State.CONNECTED); slaveStateMap.put("connecting", RedisSlaveInstance.State.CONNECTING); slaveStateMap.put("sync", RedisSlaveInstance.State.SYNC); SLAVE_STATE_MAPPING = Collections.unmodifiableMap(slaveStateMap); } /** * Utility constructor. */ private RoleParser() { } /** * Parse the output of the redis ROLE command and convert to a RedisInstance. * * @param roleOutput output of the redis ROLE command * @return RedisInstance */ public static RedisInstance parse(List<?> roleOutput) { LettuceAssert.isTrue(roleOutput != null && !roleOutput.isEmpty(), "Empty role output"); LettuceAssert.isTrue(roleOutput.get(0) instanceof String && ROLE_MAPPING.containsKey(roleOutput.get(0)), "First role element must be a string (any of " + ROLE_MAPPING.keySet() + ")"); RedisInstance.Role role = ROLE_MAPPING.get(roleOutput.get(0)); switch (role) { case MASTER: return parseMaster(roleOutput); case SLAVE: return parseSlave(roleOutput); case SENTINEL: return parseSentinel(roleOutput); } return null; } private static RedisInstance parseMaster(List<?> roleOutput) { long replicationOffset = getMasterReplicationOffset(roleOutput); List<ReplicationPartner> slaves = getMasterSlaveReplicationPartners(roleOutput); RedisMasterInstance redisMasterInstanceRole = new RedisMasterInstance(replicationOffset, Collections.unmodifiableList(slaves)); return redisMasterInstanceRole; } private static RedisInstance parseSlave(List<?> roleOutput) { Iterator<?> iterator = roleOutput.iterator(); iterator.next(); // skip first element String ip = getStringFromIterator(iterator, ""); long port = getLongFromIterator(iterator, 0); String stateString = getStringFromIterator(iterator, null); long replicationOffset = getLongFromIterator(iterator, 0); ReplicationPartner master = new ReplicationPartner(HostAndPort.fromParts(ip, Math.toIntExact(port)), replicationOffset); RedisSlaveInstance.State state = SLAVE_STATE_MAPPING.get(stateString); RedisSlaveInstance redisSlaveInstanceRole = new RedisSlaveInstance(master, state); return redisSlaveInstanceRole; } private static RedisInstance parseSentinel(List<?> roleOutput) { Iterator<?> iterator = roleOutput.iterator(); iterator.next(); // skip first element List<String> monitoredMasters = getMonitoredMasters(iterator); RedisSentinelInstance result = new RedisSentinelInstance(Collections.unmodifiableList(monitoredMasters)); return result; } private static List<String> getMonitoredMasters(Iterator<?> iterator) { List<String> monitoredMasters = new ArrayList<>(); if (!iterator.hasNext()) { return monitoredMasters; } Object masters = iterator.next(); if (!(masters instanceof Collection)) { return monitoredMasters; } for (Object monitoredMaster : (Collection) masters) { if (monitoredMaster instanceof String) { monitoredMasters.add((String) monitoredMaster); } } return monitoredMasters; } private static List<ReplicationPartner> getMasterSlaveReplicationPartners(List<?> roleOutput) { List<ReplicationPartner> slaves = new ArrayList<>(); if (roleOutput.size() > 2 && roleOutput.get(2) instanceof Collection) { Collection<?> slavesOutput = (Collection<?>) roleOutput.get(2); for (Object slaveOutput : slavesOutput) { if (!(slaveOutput instanceof Collection<?>)) { continue; } ReplicationPartner replicationPartner = getMasterSlaveReplicationPartner((Collection<?>) slaveOutput); slaves.add(replicationPartner); } } return slaves; } private static ReplicationPartner getMasterSlaveReplicationPartner(Collection<?> slaveOutput) { Iterator<?> iterator = slaveOutput.iterator(); String ip = getStringFromIterator(iterator, ""); long port = getLongFromIterator(iterator, 0); long replicationOffset = getLongFromIterator(iterator, 0); return new ReplicationPartner(HostAndPort.fromParts(ip, Math.toIntExact(port)), replicationOffset); } 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; } private static String getStringFromIterator(Iterator<?> iterator, String defaultValue) { if (iterator.hasNext()) { Object object = iterator.next(); if (object instanceof String) { return (String) object; } } return defaultValue; } private static long getMasterReplicationOffset(List<?> roleOutput) { long replicationOffset = 0; if (roleOutput.size() > 1 && roleOutput.get(1) instanceof Number) { Number number = (Number) roleOutput.get(1); replicationOffset = number.longValue(); } return replicationOffset; } }