From baf245b06f99659833d53b5cce44020023e116ef Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Mon, 8 Nov 2010 14:06:08 +0200 Subject: [PATCH] + add initial support for RedisAtomicInteger + add more redis commands --- .../redis/connection/RedisCommands.java | 11 +- .../redis/connection/RedisStringCommands.java | 8 + .../redis/connection/RedisTxCommands.java | 37 ++++ .../connection/jedis/JedisConnection.java | 71 ++++++- .../connection/jredis/JredisConnection.java | 48 ++++- .../datastore/redis/core/RedisTemplate.java | 12 +- .../redis/util/RedisAtomicInteger.java | 181 ++++++++++++++++++ 7 files changed, 354 insertions(+), 14 deletions(-) create mode 100644 spring-datastore-redis/src/main/java/org/springframework/datastore/redis/connection/RedisTxCommands.java create mode 100644 spring-datastore-redis/src/main/java/org/springframework/datastore/redis/util/RedisAtomicInteger.java diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/connection/RedisCommands.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/connection/RedisCommands.java index 6bf79f7d2..f4cc407d7 100644 --- a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/connection/RedisCommands.java +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/connection/RedisCommands.java @@ -23,7 +23,7 @@ import java.util.Collection; * * @author Costin Leau */ -public interface RedisCommands { +public interface RedisCommands extends RedisTxCommands, RedisStringCommands { Boolean exists(String key); @@ -50,13 +50,4 @@ public interface RedisCommands { void select(int dbIndex); - void watch(String... keys); - - void unwatch(); - - void multi(); - - void exec(); - - void discard(); } \ No newline at end of file diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/connection/RedisStringCommands.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/connection/RedisStringCommands.java index ca6c6b6f1..0fc9ab81b 100644 --- a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/connection/RedisStringCommands.java +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/connection/RedisStringCommands.java @@ -29,5 +29,13 @@ public interface RedisStringCommands { String get(String key); + String getSet(String key, String value); + Integer incr(String key); + + Integer incrBy(String key, int value); + + Integer decr(String key); + + Integer decrBy(String key, int value); } diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/connection/RedisTxCommands.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/connection/RedisTxCommands.java new file mode 100644 index 000000000..23ee193ae --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/connection/RedisTxCommands.java @@ -0,0 +1,37 @@ +/* + * Copyright 2006-2009 the original author or authors. + * + * 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.springframework.datastore.redis.connection; + +import java.util.List; + + +/** + * Redis transaction (aka batch) commands. + * + * @author Costin Leau + */ +public interface RedisTxCommands { + + void multi(); + + List exec(); + + void discard(); + + void watch(String... keys); + + void unwatch(); +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/connection/jedis/JedisConnection.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/connection/jedis/JedisConnection.java index 5abee1a56..091d57d13 100644 --- a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/connection/jedis/JedisConnection.java +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/connection/jedis/JedisConnection.java @@ -18,6 +18,7 @@ package org.springframework.datastore.redis.connection.jedis; import java.io.IOException; import java.lang.reflect.Field; import java.util.Collection; +import java.util.List; import org.springframework.dao.DataAccessException; import org.springframework.datastore.keyvalue.UncategorizedKeyvalueStoreException; @@ -141,9 +142,9 @@ public class JedisConnection implements RedisConnection { } @Override - public void exec() { + public List exec() { try { - client.exec(); + return transaction.exec(); } catch (Exception ex) { throw convertJedisAccessException(ex); } @@ -373,4 +374,70 @@ public class JedisConnection implements RedisConnection { throw convertJedisAccessException(ex); } } + + + @Override + public String getSet(String key, String value) { + try { + if (isQueueing()) { + transaction.getSet(key, value); + return null; + } + return jedis.getSet(key, value); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public Integer decr(String key) { + try { + if (isQueueing()) { + transaction.decr(key); + return null; + } + return jedis.decr(key); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public Integer decrBy(String key, int value) { + try { + if (isQueueing()) { + transaction.decrBy(key, value); + return null; + } + return jedis.decrBy(key, value); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public Integer incr(String key) { + try { + if (isQueueing()) { + transaction.incr(key); + return null; + } + return jedis.incr(key); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public Integer incrBy(String key, int value) { + try { + if (isQueueing()) { + transaction.incrBy(key, value); + return null; + } + return jedis.incrBy(key, value); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } } \ No newline at end of file diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/connection/jredis/JredisConnection.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/connection/jredis/JredisConnection.java index 36dc97caf..d5a742beb 100644 --- a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/connection/jredis/JredisConnection.java +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/connection/jredis/JredisConnection.java @@ -16,6 +16,7 @@ package org.springframework.datastore.redis.connection.jredis; import java.util.Collection; +import java.util.List; import org.jredis.JRedis; import org.jredis.RedisException; @@ -87,7 +88,7 @@ public class JredisConnection implements RedisConnection { } @Override - public void exec() { + public List exec() { throw new UnsupportedOperationException(); } @@ -193,4 +194,49 @@ public class JredisConnection implements RedisConnection { throw JredisUtils.convertJredisAccessException(ex); } } + + @Override + public String getSet(String key, String value) { + try { + return JredisUtils.convertToString(jredis.getset(key, value), encoding); + } catch (RedisException ex) { + throw JredisUtils.convertJredisAccessException(ex); + } + } + + @Override + public Integer decr(String key) { + try { + return (int) jredis.decr(key); + } catch (RedisException ex) { + throw JredisUtils.convertJredisAccessException(ex); + } + } + + @Override + public Integer decrBy(String key, int value) { + try { + return (int) jredis.decrby(key, value); + } catch (RedisException ex) { + throw JredisUtils.convertJredisAccessException(ex); + } + } + + @Override + public Integer incr(String key) { + try { + return (int) jredis.incr(key); + } catch (RedisException ex) { + throw JredisUtils.convertJredisAccessException(ex); + } + } + + @Override + public Integer incrBy(String key, int value) { + try { + return (int) jredis.incrby(key, value); + } catch (RedisException ex) { + throw JredisUtils.convertJredisAccessException(ex); + } + } } \ No newline at end of file diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisTemplate.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisTemplate.java index 92437f8ea..6e89cdb5e 100644 --- a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisTemplate.java +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisTemplate.java @@ -55,11 +55,21 @@ public class RedisTemplate extends RedisAccessor { afterPropertiesSet(); } + public void del(final String redisKey) { + execute(new RedisCallback() { + @Override + public Object doInRedis(RedisConnection connection) throws Exception { + connection.del(redisKey); + return null; + } + }); + } + + public T execute(RedisCallback action) { return execute(action, isExposeConnection()); } - public T execute(RedisCallback action, boolean exposeConnection) { Assert.notNull(action, "Callback object must not be null"); diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/util/RedisAtomicInteger.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/util/RedisAtomicInteger.java new file mode 100644 index 000000000..06e0372a2 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/util/RedisAtomicInteger.java @@ -0,0 +1,181 @@ +/* + * Copyright 2006-2009 the original author or authors. + * + * 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.springframework.datastore.redis.util; + +import java.io.Serializable; + +import org.springframework.datastore.redis.connection.RedisCommands; + +/** + * Atomic integer backed by Redis. + * + * @see java.util.concurrent.atomic.AtomicInteger + * @author Costin Leau + */ +public class RedisAtomicInteger extends Number implements Serializable { + + private final String key; + private RedisCommands commands; + + public RedisAtomicInteger(String redisCounter, RedisCommands commands) { + this.key = redisCounter; + this.commands = commands; + } + + /** + * Get the current value. + * + * @return the current value + */ + public int get() { + return Integer.valueOf(commands.get(key)); + } + + /** + * Set to the given value. + * + * @param newValue the new value + */ + public void set(int newValue) { + commands.set(key, Integer.toString(newValue)); + } + + /** + * Set to the give value and return the old value. + * + * @param newValue the new value + * @return the previous value + */ + public int getAndSet(int newValue) { + return Integer.valueOf(commands.getSet(key, Integer.toString(newValue))); + } + + + /** + * Atomically set the value to the given updated value + * if the current value == the expected value. + * @param expect the expected value + * @param update the new value + * @return true if successful. False return indicates that + * the actual value was not equal to the expected value. + */ + public boolean compareAndSet(int expect, int update) { + for (;;) { + commands.watch(key); + if (expect == get()) { + commands.multi(); + set(update); + if (commands.exec() != null) { + return true; + } + } + return false; + } + } + + /** + * Atomically increment by one the current value. + * @return the previous value + */ + public int getAndIncrement() { + for (;;) { + int current = get(); + int next = current + 1; + if (compareAndSet(current, next)) + return current; + } + } + + + /** + * Atomically decrement by one the current value. + * @return the previous value + */ + public int getAndDecrement() { + for (;;) { + int current = get(); + int next = current - 1; + if (compareAndSet(current, next)) + return current; + } + } + + + /** + * Atomically add the given value to current value. + * @param delta the value to add + * @return the previous value + */ + public int getAndAdd(int delta) { + for (;;) { + int current = get(); + int next = current + delta; + if (compareAndSet(current, next)) + return current; + } + } + + /** + * Atomically increment by one the current value. + * @return the updated value + */ + public int incrementAndGet() { + return commands.incr(key); + } + + /** + * Atomically decrement by one the current value. + * @return the updated value + */ + public int decrementAndGet() { + return commands.decr(key); + } + + + /** + * Atomically add the given value to current value. + * @param delta the value to add + * @return the updated value + */ + public int addAndGet(int delta) { + return commands.incrBy(key, delta); + } + + /** + * Returns the String representation of the current value. + * @return the String representation of the current value. + */ + public String toString() { + return Integer.toString(get()); + } + + + public int intValue() { + return get(); + } + + public long longValue() { + return (long) get(); + } + + public float floatValue() { + return (float) get(); + } + + public double doubleValue() { + return (double) get(); + } +} \ No newline at end of file