From 68a1c014c252d49fe11eb54570a7718f5bdba88b Mon Sep 17 00:00:00 2001 From: Nikita Date: Fri, 17 Jul 2015 14:20:52 +0300 Subject: [PATCH] RMapAsync interface added. #186 --- src/main/java/org/redisson/RedissonMap.java | 85 +++++++++++-------- .../client/protocol/RedisCommand.java | 4 + .../client/protocol/RedisCommands.java | 6 +- .../protocol/convertor/NumberConvertor.java | 40 +++++++++ .../decoder/ObjectSetReplayDecoder.java | 41 +++++++++ src/main/java/org/redisson/core/RMap.java | 45 +--------- .../java/org/redisson/core/RMapAsync.java | 84 ++++++++++++++++++ .../java/org/redisson/RedissonMapTest.java | 9 ++ 8 files changed, 234 insertions(+), 80 deletions(-) create mode 100644 src/main/java/org/redisson/client/protocol/convertor/NumberConvertor.java create mode 100644 src/main/java/org/redisson/client/protocol/decoder/ObjectSetReplayDecoder.java create mode 100644 src/main/java/org/redisson/core/RMapAsync.java diff --git a/src/main/java/org/redisson/RedissonMap.java b/src/main/java/org/redisson/RedissonMap.java index ed487fd7a..5d5588ce4 100644 --- a/src/main/java/org/redisson/RedissonMap.java +++ b/src/main/java/org/redisson/RedissonMap.java @@ -21,7 +21,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -30,10 +29,11 @@ import java.util.Set; import org.redisson.client.protocol.RedisCommand; import org.redisson.client.protocol.RedisCommand.ValueType; -import org.redisson.client.protocol.convertor.BooleanReplayConvertor; -import org.redisson.client.protocol.convertor.LongReplayConvertor; import org.redisson.client.protocol.RedisCommands; import org.redisson.client.protocol.StringCodec; +import org.redisson.client.protocol.convertor.BooleanReplayConvertor; +import org.redisson.client.protocol.convertor.LongReplayConvertor; +import org.redisson.client.protocol.convertor.NumberConvertor; import org.redisson.client.protocol.decoder.MapScanResult; import org.redisson.connection.ConnectionManager; import org.redisson.core.Predicate; @@ -61,8 +61,12 @@ public class RedissonMap extends RedissonExpirable implements RMap { @Override public int size() { - Long res = connectionManager.read(getName(), RedisCommands.HLEN, getName()); - return res.intValue(); + return connectionManager.get(sizeAsync()); + } + + @Override + public Future sizeAsync() { + return connectionManager.readAsync(getName(), RedisCommands.HLEN, getName()); } @Override @@ -75,10 +79,27 @@ public class RedissonMap extends RedissonExpirable implements RMap { return connectionManager.read(getName(), RedisCommands.HEXISTS, getName(), key); } + @Override + public Future containsKeyAsync(Object key) { + return connectionManager.readAsync(getName(), RedisCommands.HEXISTS, getName(), key); + } + @Override public boolean containsValue(Object value) { - Collection list = values(); - return list.contains(value); + return connectionManager.get(containsValueAsync(value)); + } + + @Override + public Future containsValueAsync(Object value) { + return connectionManager.evalReadAsync(getName(), new RedisCommand("EVAL", new BooleanReplayConvertor(), 4), + "local s = redis.call('hvals', KEYS[1]);" + + "for i = 0, table.getn(s), 1 do " + + "if ARGV[1] == s[i] then " + + "return true " + + "end " + + "end;" + + "return false", + Collections.singletonList(getName()), value); } @Override @@ -133,13 +154,22 @@ public class RedissonMap extends RedissonExpirable implements RMap { @Override public Set keySet() { - List keys = connectionManager.read(getName(), RedisCommands.HKEYS, getName()); - return new HashSet(keys); + return connectionManager.get(keySetAsync()); + } + + @Override + public Future> keySetAsync() { + return connectionManager.readAsync(getName(), RedisCommands.HKEYS, getName()); } @Override public Collection values() { - return connectionManager.read(getName(), RedisCommands.HVALS, getName()); + return connectionManager.get(valuesAsync()); + } + + @Override + public Future> valuesAsync() { + return connectionManager.readAsync(getName(), RedisCommands.HVALS, getName()); } @Override @@ -232,7 +262,7 @@ public class RedissonMap extends RedissonExpirable implements RMap { } @Override - public Future fastRemoveAsync(final K ... keys) { + public Future fastRemoveAsync(K ... keys) { if (keys == null || keys.length == 0) { return connectionManager.getGroup().next().newSucceededFuture(0L); } @@ -335,31 +365,16 @@ public class RedissonMap extends RedissonExpirable implements RMap { } @Override - public V addAndGet(K key, V value) { - String res = connectionManager.write(getName(), StringCodec.INSTANCE, - RedisCommands.HINCRBYFLOAT, getName(), key, new BigDecimal(value.toString()).toPlainString()); + public V addAndGet(K key, Number value) { + return connectionManager.get(addAndGetAsync(key, value)); + } - if (value instanceof Long) { - Object obj = Long.parseLong(res); - return (V)obj; - } - if (value instanceof Integer) { - Object obj = Integer.parseInt(res); - return (V)obj; - } - if (value instanceof Float) { - Object obj = Float.parseFloat(res); - return (V)obj; - } - if (value instanceof Double) { - Object obj = Double.parseDouble(res); - return (V)obj; - } - if (value instanceof BigDecimal) { - Object obj = new BigDecimal(res); - return (V)obj; - } - throw new IllegalStateException("Wrong value type!"); + @Override + public Future addAndGetAsync(K key, Number value) { + return connectionManager.writeAsync(getName(), StringCodec.INSTANCE, + new RedisCommand("HINCRBYFLOAT", new NumberConvertor(value.getClass())), + getName(), key, new BigDecimal(value.toString()).toPlainString()); } + } diff --git a/src/main/java/org/redisson/client/protocol/RedisCommand.java b/src/main/java/org/redisson/client/protocol/RedisCommand.java index 0bd3cfd4a..71ac63bcc 100644 --- a/src/main/java/org/redisson/client/protocol/RedisCommand.java +++ b/src/main/java/org/redisson/client/protocol/RedisCommand.java @@ -126,6 +126,10 @@ public class RedisCommand { this.inParamType = inParamTypes; } + public RedisCommand(String name, Convertor convertor) { + this(name, convertor, -1); + } + public RedisCommand(String name, Convertor convertor, int encodeParamIndex) { this(name, null, null, null, encodeParamIndex); this.convertor = convertor; diff --git a/src/main/java/org/redisson/client/protocol/RedisCommands.java b/src/main/java/org/redisson/client/protocol/RedisCommands.java index bd81427e1..0f49bbbe0 100644 --- a/src/main/java/org/redisson/client/protocol/RedisCommands.java +++ b/src/main/java/org/redisson/client/protocol/RedisCommands.java @@ -17,6 +17,7 @@ package org.redisson.client.protocol; import java.util.List; import java.util.Map; +import java.util.Set; import org.redisson.client.protocol.RedisCommand.ValueType; import org.redisson.client.protocol.convertor.BooleanReplayConvertor; @@ -30,6 +31,7 @@ import org.redisson.client.protocol.decoder.MapScanResultReplayDecoder; import org.redisson.client.protocol.decoder.NestedMultiDecoder; import org.redisson.client.protocol.decoder.ObjectListReplayDecoder; import org.redisson.client.protocol.decoder.ObjectMapReplayDecoder; +import org.redisson.client.protocol.decoder.ObjectSetReplayDecoder; import org.redisson.client.protocol.decoder.StringDataDecoder; import org.redisson.client.protocol.decoder.StringListReplayDecoder; import org.redisson.client.protocol.decoder.StringMapReplayDecoder; @@ -110,8 +112,8 @@ public interface RedisCommands { RedisCommand> HGETALL = new RedisCommand>("HGETALL", new ObjectMapReplayDecoder(), ValueType.MAP); RedisCommand> HVALS = new RedisCommand>("HVALS", new ObjectListReplayDecoder(), ValueType.MAP_VALUE); RedisCommand HEXISTS = new RedisCommand("HEXISTS", new BooleanReplayConvertor(), 2, ValueType.MAP_KEY); - RedisStrictCommand HLEN = new RedisStrictCommand("HLEN"); - RedisCommand> HKEYS = new RedisCommand>("HKEYS", new ObjectListReplayDecoder(), ValueType.MAP_KEY); + RedisStrictCommand HLEN = new RedisStrictCommand("HLEN", new IntegerReplayConvertor()); + RedisCommand> HKEYS = new RedisCommand>("HKEYS", new ObjectSetReplayDecoder(), ValueType.MAP_KEY); RedisCommand HMSET = new RedisCommand("HMSET", new StringReplayDecoder(), 1, ValueType.MAP); RedisCommand> HMGET = new RedisCommand>("HMGET", new ObjectListReplayDecoder(), 2, ValueType.MAP_KEY, ValueType.MAP_VALUE); RedisCommand HGET = new RedisCommand("HGET", 2, ValueType.MAP_KEY, ValueType.MAP_VALUE); diff --git a/src/main/java/org/redisson/client/protocol/convertor/NumberConvertor.java b/src/main/java/org/redisson/client/protocol/convertor/NumberConvertor.java new file mode 100644 index 000000000..868d7b71d --- /dev/null +++ b/src/main/java/org/redisson/client/protocol/convertor/NumberConvertor.java @@ -0,0 +1,40 @@ +package org.redisson.client.protocol.convertor; + +import java.math.BigDecimal; + +public class NumberConvertor extends SingleConvertor { + + private Class resultClass; + + public NumberConvertor(Class resultClass) { + super(); + this.resultClass = resultClass; + } + + @Override + public Object convert(Object result) { + String res = (String) result; + if (resultClass.isAssignableFrom(Long.class)) { + Object obj = Long.parseLong(res); + return obj; + } + if (resultClass.isAssignableFrom(Integer.class)) { + Object obj = Integer.parseInt(res); + return obj; + } + if (resultClass.isAssignableFrom(Float.class)) { + Object obj = Float.parseFloat(res); + return obj; + } + if (resultClass.isAssignableFrom(Double.class)) { + Object obj = Double.parseDouble(res); + return obj; + } + if (resultClass.isAssignableFrom(BigDecimal.class)) { + Object obj = new BigDecimal(res); + return obj; + } + throw new IllegalStateException("Wrong value type!"); + } + +} diff --git a/src/main/java/org/redisson/client/protocol/decoder/ObjectSetReplayDecoder.java b/src/main/java/org/redisson/client/protocol/decoder/ObjectSetReplayDecoder.java new file mode 100644 index 000000000..589b90014 --- /dev/null +++ b/src/main/java/org/redisson/client/protocol/decoder/ObjectSetReplayDecoder.java @@ -0,0 +1,41 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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.redisson.client.protocol.decoder; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import io.netty.buffer.ByteBuf; + +public class ObjectSetReplayDecoder implements MultiDecoder> { + + @Override + public Object decode(ByteBuf buf) { + throw new UnsupportedOperationException(); + } + + @Override + public Set decode(List parts) { + return new HashSet(parts); + } + + @Override + public boolean isApplicable(int paramNum) { + return false; + } + +} diff --git a/src/main/java/org/redisson/core/RMap.java b/src/main/java/org/redisson/core/RMap.java index b796d2121..2375d974f 100644 --- a/src/main/java/org/redisson/core/RMap.java +++ b/src/main/java/org/redisson/core/RMap.java @@ -15,8 +15,6 @@ */ package org.redisson.core; -import io.netty.util.concurrent.Future; - import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentMap; @@ -30,7 +28,7 @@ import java.util.concurrent.ConcurrentMap; * @param key * @param value */ -public interface RMap extends ConcurrentMap, RExpirable { +public interface RMap extends ConcurrentMap, RMapAsync { /** * Atomically adds the given delta to the current value @@ -42,7 +40,7 @@ public interface RMap extends ConcurrentMap, RExpirable { * @param delta the value to add * @return the updated value */ - V addAndGet(K key, V delta); + V addAndGet(K key, Number delta); /** * Gets a map slice contains the mappings with defined keys @@ -100,31 +98,6 @@ public interface RMap extends ConcurrentMap, RExpirable { */ long fastRemove(K ... keys); - /** - * Removes keys from map by one operation in async manner - * - * Works faster than RMap.removeAsync but not returning - * the value associated with key - * - * @param keys - * @return the number of keys that were removed from the hash, not including specified but non existing keys - */ - Future fastRemoveAsync(K ... keys); - - /** - * Associates the specified value with the specified key - * in async manner. - * - * Works faster than RMap.putAsync but not returning - * the previous value associated with key - * - * @param key - * @param value - * @return true if key is a new key in the hash and value was set. - * false if key already exists in the hash and the value was updated. - */ - Future fastPutAsync(K key, V value); - /** * Associates the specified value with the specified key. * @@ -138,18 +111,4 @@ public interface RMap extends ConcurrentMap, RExpirable { */ boolean fastPut(K key, V value); - Future getAsync(K key); - - Future putAsync(K key, V value); - - Future removeAsync(K key); - - Future replaceAsync(K key, V value); - - Future replaceAsync(K key, V oldValue, V newValue); - - Future removeAsync(Object key, Object value); - - Future putIfAbsentAsync(K key, V value); - } diff --git a/src/main/java/org/redisson/core/RMapAsync.java b/src/main/java/org/redisson/core/RMapAsync.java new file mode 100644 index 000000000..d3e224a7f --- /dev/null +++ b/src/main/java/org/redisson/core/RMapAsync.java @@ -0,0 +1,84 @@ +/** + * Copyright 2014 Nikita Koksharov, Nickolay Borbit + * + * 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.redisson.core; + +import java.util.Collection; +import java.util.Set; + +import io.netty.util.concurrent.Future; + +/** + * Async map functions + * + * @author Nikita Koksharov + * + * @param key + * @param value + */ +public interface RMapAsync extends RExpirableAsync { + + Future addAndGetAsync(K key, Number value); + + Future> valuesAsync(); + + Future> keySetAsync(); + + Future containsValueAsync(Object value); + + Future containsKeyAsync(Object key); + + Future sizeAsync(); + + /** + * Removes keys from map by one operation in async manner + * + * Works faster than RMap.removeAsync but not returning + * the value associated with key + * + * @param keys + * @return the number of keys that were removed from the hash, not including specified but non existing keys + */ + Future fastRemoveAsync(K ... keys); + + /** + * Associates the specified value with the specified key + * in async manner. + * + * Works faster than RMap.putAsync but not returning + * the previous value associated with key + * + * @param key + * @param value + * @return true if key is a new key in the hash and value was set. + * false if key already exists in the hash and the value was updated. + */ + Future fastPutAsync(K key, V value); + + Future getAsync(K key); + + Future putAsync(K key, V value); + + Future removeAsync(K key); + + Future replaceAsync(K key, V value); + + Future replaceAsync(K key, V oldValue, V newValue); + + Future removeAsync(Object key, Object value); + + Future putIfAbsentAsync(K key, V value); + +} diff --git a/src/test/java/org/redisson/RedissonMapTest.java b/src/test/java/org/redisson/RedissonMapTest.java index b66b2aca1..38e36a4ef 100644 --- a/src/test/java/org/redisson/RedissonMapTest.java +++ b/src/test/java/org/redisson/RedissonMapTest.java @@ -1,6 +1,7 @@ package org.redisson; import java.io.Serializable; +import java.math.BigDecimal; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; @@ -131,6 +132,14 @@ public class RedissonMapTest extends BaseTest { Assert.assertEquals(112, (int)res); res = map.get(1); Assert.assertEquals(112, (int)res); + + RMap map2 = redisson.getMap("getAll2"); + map2.put(1, new Double(100.2)); + + Double res2 = map2.addAndGet(1, new Double(12.1)); + Assert.assertTrue(new Double(112.3).compareTo(res2) == 0); + res2 = map2.get(1); + Assert.assertTrue(new Double(112.3).compareTo(res2) == 0); } @Test