package com.janrain.backplane2.server.dao.redis; import com.janrain.backplane.common.BackplaneServerException; import com.janrain.backplane2.server.Token; import com.janrain.backplane2.server.dao.TokenDAO; import com.janrain.commons.supersimpledb.SimpleDBException; import com.janrain.redis.Redis; import org.apache.commons.lang.SerializationUtils; import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; import redis.clients.jedis.Jedis; import java.util.ArrayList; import java.util.List; /** * @author Tom Raney */ public class RedisTokenDAO implements TokenDAO { public static byte[] getKey(String id) { return ("v2_token_" + id).getBytes(); } @Override public Token get(String id) throws BackplaneServerException { byte[] bytes = Redis.getInstance().get(getKey(id)); if (bytes != null) { return (Token) SerializationUtils.deserialize(bytes); } else { return null; } } @Override public List<Token> getAll() throws BackplaneServerException { List<Token> tokens = new ArrayList<Token>(); List<byte[]> byteList = Redis.getInstance().lrange(getKey("list"), 0, -1); for (byte[] bytes : byteList) { if (bytes != null) { tokens.add((Token) SerializationUtils.deserialize(bytes)); } } return tokens; } @Override public void persist(Token token) throws BackplaneServerException { Jedis jedis = null; try { jedis = Redis.getInstance().getWriteJedis(); byte[] bytes = SerializationUtils.serialize(token); jedis.rpush(getKey("list"), bytes); jedis.set(getKey(token.getIdValue()), bytes); // set a TTL if (token.getExpirationDate() != null) { jedis.expireAt(getKey(token.getIdValue()), token.getExpirationDate().getTime() / 1000 +1); } } finally { Redis.getInstance().releaseToPool(jedis); } } @Override public void delete(String tokenId) throws BackplaneServerException { Jedis jedis = null; try { jedis = Redis.getInstance().getWriteJedis(); byte[] bytes = jedis.get(getKey(tokenId)); if (bytes != null) { logger.info("removing token " + tokenId); jedis.lrem(getKey("list"), 0, bytes); jedis.del(getKey(tokenId)); } } finally { Redis.getInstance().releaseToPool(jedis); } } @Override public List<Token> retrieveTokensByGrant(String grantId) throws BackplaneServerException { List<Token> tokens = getAll(); List<Token> filtered = new ArrayList<Token>(); for (Token token: tokens) { if (token.getBackingGrants().contains(grantId)) { logger.info("found"); filtered.add(token); } } return filtered; } @Override public void revokeTokenByGrant(String grantId) throws BackplaneServerException { List<Token> tokens = retrieveTokensByGrant(grantId); for (Token token : tokens) { delete(token.getIdValue()); logger.info("revoked token " + token.getIdValue()); } if (! tokens.isEmpty()) { logger.info("all tokens for grant " + grantId + " have been revoked"); } } @Override public void deleteExpiredTokens() throws BackplaneServerException { // todo: add token cache? Jedis jedis = null; try { jedis = Redis.getInstance().getWriteJedis(); logger.info("Backplane token cleanup task started."); List<Token> tokens = getAll(); if (tokens != null) { for (Token token : tokens) { if (Redis.getInstance().get(getKey(token.getIdValue())) == null) { // remove from list jedis.lrem(getKey("list"), 0, SerializationUtils.serialize(token)); logger.info("removed expired token " + token.getIdValue()); } } } } catch (Exception e) { // catch-all, else cleanup thread stops logger.error("Backplane token cleanup task error: " + e.getMessage(), e); } finally { logger.info("Backplane token cleanup task finished."); Redis.getInstance().releaseToPool(jedis); } } @Override public void cacheRevokedCleanup() throws SimpleDBException { // no-op } // PRIVATE private static final Logger logger = Logger.getLogger(RedisTokenDAO.class); private String getChannelBindingKey(@NotNull String channel) { // todo: key prefixes should be centralized to avoid conflicts return "v2_channel_bus_" + channel; } }