package ddth.dasp.common.redis.impl.jedis; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.lang3.StringUtils; import redis.clients.jedis.Jedis; import redis.clients.util.SafeEncoder; import ddth.dasp.common.redis.IMessageListener; import ddth.dasp.common.redis.impl.AbstractRedisClient; public class PoolableRedisClient extends AbstractRedisClient { private Jedis redisClient; private final Map<String, Set<IMessageListener>> topicSubscriptions = new ConcurrentHashMap<String, Set<IMessageListener>>(); private final Map<IMessageListener, WrappedJedisPubSub> topicSubscriptionMappings = new ConcurrentHashMap<IMessageListener, WrappedJedisPubSub>(); /** * {@inheritDoc} */ @Override public void init() { int timeout = 10000; // timeout in milliseconds // Jedis passes timeout value to a java.net.Socket, hence it should be // in milliseconds redisClient = new Jedis(getRedisHost(), getRedisPort(), timeout); if (!StringUtils.isBlank(getRedisPassword())) { redisClient.auth(getRedisPassword()); } redisClient.connect(); // System.out.println("\tNEW REDIS CLIENT"); } /** * {@inheritDoc} */ public void destroy() { try { redisClient.disconnect(); } finally { redisClient.quit(); } } /** * {@inheritDoc} */ @Override public void close() { if (getRedisClientPool() != null) { getRedisClientPool().returnRedisClient(this); } } /* Redis API */ /** * {@inheritDoc} */ @Override public String ping() { return redisClient.isConnected() ? redisClient.ping() : null; } /** * {@inheritDoc} */ @Override public void expire(String key, int ttlSeconds) { if (ttlSeconds > 0) { redisClient.expire(key, ttlSeconds); } } /** * {@inheritDoc} */ @Override public void delete(String... keys) { redisClient.del(keys); } /** * {@inheritDoc} */ @Override public String get(String key) { return redisClient.get(key); } /** * {@inheritDoc} */ @Override public byte[] getAsBinary(String key) { return redisClient.get(SafeEncoder.encode(key)); } /** * {@inheritDoc} */ public void set(String key, String value, int ttlSeconds) { if (ttlSeconds > 0) { redisClient.setex(key, ttlSeconds, value); } else { redisClient.set(key, value); } } /** * {@inheritDoc} */ public void set(String key, byte[] value, int ttlSeconds) { if (ttlSeconds > 0) { redisClient.setex(SafeEncoder.encode(key), ttlSeconds, value); } else { redisClient.set(SafeEncoder.encode(key), value); } } /** * {@inheritDoc} */ @Override public void hashDelete(String mapName, String... fieldName) { redisClient.hdel(mapName, fieldName); } /** * {@inheritDoc} */ public long hashSize(String mapName) { Long result = redisClient.hlen(mapName); return result != null ? result.longValue() : -1; } /** * {@inheritDoc} */ @Override public String hashGet(String mapName, String fieldName) { return redisClient.hget(mapName, fieldName); } /** * {@inheritDoc} */ @Override public byte[] hashGetAsBinary(String mapName, String fieldName) { return redisClient.hget(SafeEncoder.encode(mapName), SafeEncoder.encode(fieldName)); } /** * {@inheritDoc} */ @Override public void hashSet(String mapName, String fieldName, String value, int ttlSeconds) { redisClient.hset(mapName, fieldName, value); expire(mapName, ttlSeconds); } /** * {@inheritDoc} */ @Override public void hashSet(String mapName, String fieldName, byte[] value, int ttlSeconds) { redisClient.hset(SafeEncoder.encode(mapName), SafeEncoder.encode(fieldName), value); expire(mapName, ttlSeconds); } /** * {@inheritDoc} */ @Override public void listPush(String listName, String... messages) { listPush(listName, 0, messages); } /** * {@inheritDoc} */ @Override public void listPush(String listName, byte[]... messages) { listPush(listName, 0, messages); } /** * {@inheritDoc} */ @Override public void listPush(String listName, int ttlSeconds, String... messages) { redisClient.rpush(listName, messages); expire(listName, ttlSeconds); } /** * {@inheritDoc} */ @Override public void listPush(String listName, int ttlSeconds, byte[]... messages) { redisClient.rpush(SafeEncoder.encode(listName), messages); expire(listName, ttlSeconds); } /** * {@inheritDoc} */ @Override public String listPop(String listName) { return listPop(listName, false); } /** * {@inheritDoc} */ @Override public String listPop(String listName, boolean block) { return listPop(listName, block, DEFAULT_READ_TIMEOUT); } /** * {@inheritDoc} */ @Override public String listPop(String listName, boolean block, int timeout) { if (!block) { return redisClient.lpop(listName); } List<String> result = redisClient.blpop(timeout, listName); return result != null && result.size() > 1 ? result.get(1) : null; } /** * {@inheritDoc} */ @Override public byte[] listPopAsBinary(String listName) { return listPopAsBinary(listName, false); } /** * {@inheritDoc} */ @Override public byte[] listPopAsBinary(String listName, boolean block) { return listPopAsBinary(listName, block, DEFAULT_READ_TIMEOUT); } /** * {@inheritDoc} */ @Override public byte[] listPopAsBinary(String listName, boolean block, int timeout) { if (!block) { return redisClient.lpop(SafeEncoder.encode(listName)); } List<byte[]> result = redisClient.blpop(timeout, SafeEncoder.encode(listName)); return result != null && result.size() > 1 ? result.get(1) : null; } /** * {@inheritDoc} */ public long listSize(String listName) { Long size = redisClient.llen(listName); return size != null ? size.longValue() : -1; } /** * {@inheritDoc} */ @Override public List<String> listMembers(String listName) { long listSize = listSize(listName); List<String> result = redisClient.lrange(listName, 0, listSize - 1); return result; } /** * {@inheritDoc} */ @Override public List<byte[]> listMembersAsBinary(String listName) { long listSize = listSize(listName); List<byte[]> result = redisClient.lrange(SafeEncoder.encode(listName), 0, listSize - 1); return result; } /** * {@inheritDoc} * * @param setName * @param messages */ @Override public void setAdd(String setName, String... messages) { setAdd(setName, 0, messages); } /** * {@inheritDoc} * * @param setName * @param messages */ @Override public void setAdd(String setName, byte[]... messages) { setAdd(setName, 0, messages); } /** * {@inheritDoc} */ @Override public void setAdd(String setName, int ttlSeconds, String... messages) { redisClient.sadd(setName, messages); expire(setName, ttlSeconds); } /** * {@inheritDoc} */ @Override public void setAdd(String setName, int ttlSeconds, byte[]... messages) { redisClient.sadd(SafeEncoder.encode(setName), messages); expire(setName, ttlSeconds); } /** * {@inheritDoc} * * @param setName * @param value * @return */ @Override public boolean setIsMember(String setName, String value) { Boolean result = redisClient.sismember(setName, value); return result != null ? result.booleanValue() : false; } /** * {@inheritDoc} * * @param setName * @param value * @return */ @Override public boolean setIsMember(String setName, byte[] value) { Boolean result = redisClient.sismember(SafeEncoder.encode(setName), value); return result != null ? result.booleanValue() : false; } /** * {@inheritDoc} * * @param setName * @return */ @Override public String setPop(String setName) { return redisClient.spop(setName); } /** * {@inheritDoc} * * @param setName * @return */ @Override public byte[] setPopAsBinary(String setName) { return redisClient.spop(SafeEncoder.encode(setName)); } /** * {@inheritDoc} */ @Override public Set<String> setMembers(String setName) { Set<String> result = redisClient.smembers(setName); return result != null ? result : new HashSet<String>(); } /** * {@inheritDoc} */ @Override public Set<byte[]> setMembersAsBinary(String setName) { Set<byte[]> result = redisClient.smembers(SafeEncoder.encode(setName)); return result != null ? result : new HashSet<byte[]>(); } /** * {@inheritDoc} */ @Override public void setRemove(String setName, String... members) { redisClient.srem(setName, members); } /** * {@inheritDoc} */ @Override public void setRemove(String setName, byte[]... members) { redisClient.srem(SafeEncoder.encode(setName), members); } /** * {@inheritDoc} */ @Override public long setSize(String setName) { Long size = redisClient.scard(setName); return size != null ? size.longValue() : -1; } /** * {@inheritDoc} */ @Override public void publish(String channelName, String message) { redisClient.publish(channelName, message); } /** * {@inheritDoc} */ @Override public void publish(String channelName, byte[] message) { redisClient.publish(SafeEncoder.encode(channelName), message); } /** * {@inheritDoc} */ @Override public boolean subscribe(String channelName, IMessageListener messageListener) { Set<IMessageListener> subcription = topicSubscriptions.get(channelName); if (subcription == null) { subcription = new HashSet<IMessageListener>(); topicSubscriptions.put(channelName, subcription); } boolean subscribe = false; WrappedJedisPubSub wrappedJedisPubSub = null; synchronized (subcription) { if (subcription.add(messageListener)) { wrappedJedisPubSub = new WrappedJedisPubSub(channelName, messageListener); topicSubscriptionMappings.put(messageListener, wrappedJedisPubSub); subscribe = true; } } if (subscribe) { byte[] channel = SafeEncoder.encode(channelName); // this operation blocks! redisClient.subscribe(wrappedJedisPubSub, channel); return true; } else { return false; } } /** * {@inheritDoc} */ @Override public boolean unsubscribe(String channelName, IMessageListener messageListener) { Set<IMessageListener> subcription = topicSubscriptions.get(channelName); boolean unsubscribe = false; WrappedJedisPubSub wrappedJedisPubSub = null; if (subcription != null) { synchronized (subcription) { if (subcription.remove(messageListener)) { wrappedJedisPubSub = topicSubscriptionMappings.remove(messageListener); if (wrappedJedisPubSub != null) { unsubscribe = true; } } } } if (unsubscribe) { byte[] channel = SafeEncoder.encode(channelName); wrappedJedisPubSub.unsubscribe(channel); return true; } else { return false; } } /* Redis API */ }