Merge branch 'mrniko/master' into LUA-array-indices-start-fix
commit
d1da920180
@ -0,0 +1,40 @@
|
||||
# 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.
|
||||
|
||||
sudo: false
|
||||
dist: trusty
|
||||
|
||||
language: java
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
env:
|
||||
matrix:
|
||||
- REDIS_VERSION=3.0.7
|
||||
- REDIS_VERSION=2.8.24
|
||||
- REDIS_VERSION=3.2.0-rc3
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.m2
|
||||
install:
|
||||
- export REDIS_BIN=$HOME/redis/${REDIS_VERSION}/bin
|
||||
- wget -c https://github.com/antirez/redis/archive/${REDIS_VERSION}.tar.gz -O redis-${REDIS_VERSION}.tar.gz
|
||||
- tar -xvf redis-${REDIS_VERSION}.tar.gz
|
||||
- make -C redis-${REDIS_VERSION} PREFIX=$HOME/redis/${REDIS_VERSION} install
|
||||
before_script:
|
||||
- $REDIS_BIN/redis-server --daemonize yes
|
||||
- sleep 3
|
||||
- $REDIS_BIN/redis-cli PING
|
||||
- export REDIS_VERSION="$(redis-cli INFO SERVER | sed -n 2p)"
|
||||
- echo $REDIS_VERSION
|
||||
script: mvn -DargLine="-DredisBinary=$REDIS_BIN/redis-server -DtravisEnv=true" -Punit-test -Ptravis clean verify
|
@ -0,0 +1,96 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import org.redisson.client.protocol.decoder.ListScanResult;
|
||||
|
||||
abstract class RedissonBaseIterator<V> implements Iterator<V> {
|
||||
|
||||
private List<V> firstValues;
|
||||
private Iterator<V> iter;
|
||||
private InetSocketAddress client;
|
||||
private long nextIterPos;
|
||||
private long startPos = -1;
|
||||
|
||||
private boolean currentElementRemoved;
|
||||
private boolean removeExecuted;
|
||||
private V value;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
if (iter == null || !iter.hasNext()) {
|
||||
if (nextIterPos == -1) {
|
||||
return false;
|
||||
}
|
||||
long prevIterPos;
|
||||
do {
|
||||
prevIterPos = nextIterPos;
|
||||
ListScanResult<V> res = iterator(client, nextIterPos);
|
||||
client = res.getRedisClient();
|
||||
if (startPos == -1) {
|
||||
startPos = res.getPos();
|
||||
}
|
||||
if (nextIterPos == 0 && firstValues == null) {
|
||||
firstValues = res.getValues();
|
||||
} else if (res.getValues().equals(firstValues) && res.getPos() == startPos) {
|
||||
return false;
|
||||
}
|
||||
iter = res.getValues().iterator();
|
||||
nextIterPos = res.getPos();
|
||||
} while (!iter.hasNext() && nextIterPos != prevIterPos);
|
||||
if (prevIterPos == nextIterPos && !removeExecuted) {
|
||||
nextIterPos = -1;
|
||||
}
|
||||
}
|
||||
return iter.hasNext();
|
||||
}
|
||||
|
||||
abstract ListScanResult<V> iterator(InetSocketAddress client, long nextIterPos);
|
||||
|
||||
@Override
|
||||
public V next() {
|
||||
if (!hasNext()) {
|
||||
throw new NoSuchElementException("No such element");
|
||||
}
|
||||
|
||||
value = iter.next();
|
||||
currentElementRemoved = false;
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
if (currentElementRemoved) {
|
||||
throw new IllegalStateException("Element been already deleted");
|
||||
}
|
||||
if (iter == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
iter.remove();
|
||||
remove(value);
|
||||
currentElementRemoved = true;
|
||||
removeExecuted = true;
|
||||
}
|
||||
|
||||
abstract void remove(V value);
|
||||
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.redisson.client.codec.Codec;
|
||||
import org.redisson.client.codec.DelegateDecoderCodec;
|
||||
import org.redisson.client.protocol.RedisCommand;
|
||||
import org.redisson.client.protocol.RedisCommand.ValueType;
|
||||
import org.redisson.client.protocol.RedisCommands;
|
||||
import org.redisson.command.CommandExecutor;
|
||||
import org.redisson.connection.decoder.MapGetAllDecoder;
|
||||
import org.redisson.core.RBucket;
|
||||
import org.redisson.core.RBuckets;
|
||||
|
||||
import io.netty.util.concurrent.Future;
|
||||
|
||||
public class RedissonBuckets implements RBuckets {
|
||||
|
||||
private final Codec codec;
|
||||
private final CommandExecutor commandExecutor;
|
||||
private final Redisson redisson;
|
||||
|
||||
public RedissonBuckets(Redisson redisson, CommandExecutor commandExecutor) {
|
||||
this(redisson, commandExecutor.getConnectionManager().getCodec(), commandExecutor);
|
||||
}
|
||||
|
||||
public RedissonBuckets(Redisson redisson, Codec codec, CommandExecutor commandExecutor) {
|
||||
super();
|
||||
this.codec = codec;
|
||||
this.commandExecutor = commandExecutor;
|
||||
this.redisson = redisson;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> List<RBucket<V>> find(String pattern) {
|
||||
Collection<String> keys = commandExecutor.get(commandExecutor.<List<String>, String>readAllAsync(RedisCommands.KEYS, pattern));
|
||||
List<RBucket<V>> buckets = new ArrayList<RBucket<V>>(keys.size());
|
||||
for (String key : keys) {
|
||||
if(key == null) {
|
||||
continue;
|
||||
}
|
||||
buckets.add(redisson.<V>getBucket(key, codec));
|
||||
}
|
||||
return buckets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Map<String, V> get(String... keys) {
|
||||
if (keys.length == 0) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
RedisCommand<Map<Object, Object>> command = new RedisCommand<Map<Object, Object>>("MGET", new MapGetAllDecoder(Arrays.<Object>asList(keys), 0), ValueType.OBJECTS);
|
||||
Future<Map<String, V>> future = commandExecutor.readAsync(keys[0], new DelegateDecoderCodec(codec), command, keys);
|
||||
return commandExecutor.get(future);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean trySet(Map<String, ?> buckets) {
|
||||
if (buckets.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Object> params = new ArrayList<Object>(buckets.size());
|
||||
for (Entry<String, ?> entry : buckets.entrySet()) {
|
||||
params.add(entry.getKey());
|
||||
try {
|
||||
params.add(codec.getValueEncoder().encode(entry.getValue()));
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return commandExecutor.write(params.get(0).toString(), RedisCommands.MSETNX, params.toArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(Map<String, ?> buckets) {
|
||||
if (buckets.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Object> params = new ArrayList<Object>(buckets.size());
|
||||
for (Entry<String, ?> entry : buckets.entrySet()) {
|
||||
params.add(entry.getKey());
|
||||
try {
|
||||
params.add(codec.getValueEncoder().encode(entry.getValue()));
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
commandExecutor.write(params.get(0).toString(), RedisCommands.MSET, params.toArray());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,197 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.redisson.client.codec.Codec;
|
||||
import org.redisson.client.codec.GeoEntryCodec;
|
||||
import org.redisson.client.codec.ScoredCodec;
|
||||
import org.redisson.client.protocol.RedisCommand;
|
||||
import org.redisson.client.protocol.RedisCommand.ValueType;
|
||||
import org.redisson.client.protocol.RedisCommands;
|
||||
import org.redisson.client.protocol.decoder.GeoDistanceDecoder;
|
||||
import org.redisson.client.protocol.decoder.GeoMapReplayDecoder;
|
||||
import org.redisson.client.protocol.decoder.GeoPositionDecoder;
|
||||
import org.redisson.client.protocol.decoder.GeoPositionMapDecoder;
|
||||
import org.redisson.client.protocol.decoder.MultiDecoder;
|
||||
import org.redisson.client.protocol.decoder.NestedMultiDecoder;
|
||||
import org.redisson.client.protocol.decoder.FlatNestedMultiDecoder;
|
||||
import org.redisson.command.CommandAsyncExecutor;
|
||||
import org.redisson.connection.decoder.MapGetAllDecoder;
|
||||
import org.redisson.core.GeoEntry;
|
||||
import org.redisson.core.GeoPosition;
|
||||
import org.redisson.core.GeoUnit;
|
||||
import org.redisson.core.RGeo;
|
||||
|
||||
import io.netty.util.concurrent.Future;
|
||||
|
||||
public class RedissonGeo<V> extends RedissonExpirable implements RGeo<V> {
|
||||
|
||||
MultiDecoder<Map<Object, Object>> postitionDecoder;
|
||||
MultiDecoder<Map<Object, Object>> distanceDecoder;
|
||||
|
||||
public RedissonGeo(CommandAsyncExecutor connectionManager, String name) {
|
||||
super(connectionManager, name);
|
||||
postitionDecoder = new NestedMultiDecoder(new GeoPositionDecoder(), new GeoDistanceDecoder(codec), new GeoMapReplayDecoder(), true);
|
||||
distanceDecoder = new FlatNestedMultiDecoder(new GeoDistanceDecoder(codec), new GeoMapReplayDecoder(), true);
|
||||
}
|
||||
|
||||
public RedissonGeo(Codec codec, CommandAsyncExecutor connectionManager, String name) {
|
||||
super(codec, connectionManager, name);
|
||||
postitionDecoder = new NestedMultiDecoder(new GeoPositionDecoder(), new GeoDistanceDecoder(codec), new GeoMapReplayDecoder(), true);
|
||||
distanceDecoder = new FlatNestedMultiDecoder(new GeoDistanceDecoder(codec), new GeoMapReplayDecoder(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Long> addAsync(double longitude, double latitude, V member) {
|
||||
return commandExecutor.writeAsync(getName(), RedisCommands.GEOADD, getName(), convert(longitude), convert(latitude), member);
|
||||
}
|
||||
|
||||
private String convert(double longitude) {
|
||||
return BigDecimal.valueOf(longitude).toPlainString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long add(double longitude, double latitude, V member) {
|
||||
return get(addAsync(longitude, latitude, member));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long add(GeoEntry... entries) {
|
||||
return get(addAsync(entries));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Long> addAsync(GeoEntry... entries) {
|
||||
List<Object> params = new ArrayList<Object>(entries.length + 1);
|
||||
params.add(getName());
|
||||
for (GeoEntry entry : entries) {
|
||||
params.add(entry.getLongitude());
|
||||
params.add(entry.getLatitude());
|
||||
params.add(entry.getMember());
|
||||
}
|
||||
return commandExecutor.writeAsync(getName(), new GeoEntryCodec(codec), RedisCommands.GEOADD_ENTRIES, params.toArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double dist(V firstMember, V secondMember, GeoUnit geoUnit) {
|
||||
return get(distAsync(firstMember, secondMember, geoUnit));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Double> distAsync(V firstMember, V secondMember, GeoUnit geoUnit) {
|
||||
return commandExecutor.readAsync(getName(), new ScoredCodec(codec), RedisCommands.GEODIST, getName(), firstMember, secondMember, geoUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<V, String> hash(V... members) {
|
||||
return get(hashAsync(members));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Map<V, String>> hashAsync(V... members) {
|
||||
List<Object> params = new ArrayList<Object>(members.length + 1);
|
||||
params.add(getName());
|
||||
params.addAll(Arrays.asList(members));
|
||||
RedisCommand<Map<Object, Object>> command = new RedisCommand<Map<Object, Object>>("GEOHASH", new MapGetAllDecoder(params, 1), 2, ValueType.OBJECTS);
|
||||
return commandExecutor.readAsync(getName(), new ScoredCodec(codec), command, params.toArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<V, GeoPosition> pos(V... members) {
|
||||
return get(posAsync(members));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Map<V, GeoPosition>> posAsync(V... members) {
|
||||
List<Object> params = new ArrayList<Object>(members.length + 1);
|
||||
params.add(getName());
|
||||
params.addAll(Arrays.asList(members));
|
||||
|
||||
MultiDecoder<Map<Object, Object>> decoder = new NestedMultiDecoder(new GeoPositionDecoder(), new GeoPositionMapDecoder(params), true);
|
||||
RedisCommand<Map<Object, Object>> command = new RedisCommand<Map<Object, Object>>("GEOPOS", decoder, 2, ValueType.OBJECTS);
|
||||
return commandExecutor.readAsync(getName(), new ScoredCodec(codec), command, params.toArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<V> radius(double longitude, double latitude, double radius, GeoUnit geoUnit) {
|
||||
return get(radiusAsync(longitude, latitude, radius, geoUnit));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<List<V>> radiusAsync(double longitude, double latitude, double radius, GeoUnit geoUnit) {
|
||||
return commandExecutor.readAsync(getName(), codec, RedisCommands.GEORADIUS, getName(), convert(longitude), convert(latitude), radius, geoUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<V, Double> radiusWithDistance(double longitude, double latitude, double radius, GeoUnit geoUnit) {
|
||||
return get(radiusWithDistanceAsync(longitude, latitude, radius, geoUnit));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Map<V, Double>> radiusWithDistanceAsync(double longitude, double latitude, double radius, GeoUnit geoUnit) {
|
||||
RedisCommand<Map<Object, Object>> command = new RedisCommand<Map<Object, Object>>("GEORADIUS", distanceDecoder);
|
||||
return commandExecutor.readAsync(getName(), codec, command, getName(), convert(longitude), convert(latitude), radius, geoUnit, "WITHDIST");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<V, GeoPosition> radiusWithPosition(double longitude, double latitude, double radius, GeoUnit geoUnit) {
|
||||
return get(radiusWithPositionAsync(longitude, latitude, radius, geoUnit));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Map<V, GeoPosition>> radiusWithPositionAsync(double longitude, double latitude, double radius, GeoUnit geoUnit) {
|
||||
RedisCommand<Map<Object, Object>> command = new RedisCommand<Map<Object, Object>>("GEORADIUS", postitionDecoder);
|
||||
return commandExecutor.readAsync(getName(), codec, command, getName(), convert(longitude), convert(latitude), radius, geoUnit, "WITHCOORD");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<V> radius(V member, double radius, GeoUnit geoUnit) {
|
||||
return get(radiusAsync(member, radius, geoUnit));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<List<V>> radiusAsync(V member, double radius, GeoUnit geoUnit) {
|
||||
return commandExecutor.readAsync(getName(), codec, RedisCommands.GEORADIUSBYMEMBER, getName(), member, radius, geoUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<V, Double> radiusWithDistance(V member, double radius, GeoUnit geoUnit) {
|
||||
return get(radiusWithDistanceAsync(member, radius, geoUnit));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Map<V, Double>> radiusWithDistanceAsync(V member, double radius, GeoUnit geoUnit) {
|
||||
RedisCommand command = new RedisCommand("GEORADIUSBYMEMBER", distanceDecoder, 2);
|
||||
return commandExecutor.readAsync(getName(), codec, command, getName(), member, radius, geoUnit, "WITHDIST");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<V, GeoPosition> radiusWithPosition(V member, double radius, GeoUnit geoUnit) {
|
||||
return get(radiusWithPositionAsync(member, radius, geoUnit));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Map<V, GeoPosition>> radiusWithPositionAsync(V member, double radius, GeoUnit geoUnit) {
|
||||
RedisCommand<Map<Object, Object>> command = new RedisCommand<Map<Object, Object>>("GEORADIUSBYMEMBER", postitionDecoder, 2);
|
||||
return commandExecutor.readAsync(getName(), codec, command, getName(), member, radius, geoUnit, "WITHCOORD");
|
||||
}
|
||||
}
|
@ -0,0 +1,232 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.redisson.client.codec.Codec;
|
||||
import org.redisson.client.protocol.RedisCommands;
|
||||
import org.redisson.command.CommandAsyncExecutor;
|
||||
import org.redisson.core.RListMultimapCache;
|
||||
|
||||
import io.netty.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* @author Nikita Koksharov
|
||||
*
|
||||
* @param <K> key
|
||||
* @param <V> value
|
||||
*/
|
||||
public class RedissonListMultimapCache<K, V> extends RedissonListMultimap<K, V> implements RListMultimapCache<K, V> {
|
||||
|
||||
private final RedissonMultimapCache<K> baseCache;
|
||||
|
||||
RedissonListMultimapCache(EvictionScheduler evictionScheduler, CommandAsyncExecutor connectionManager, String name) {
|
||||
super(connectionManager, name);
|
||||
evictionScheduler.scheduleCleanMultimap(name, getTimeoutSetName());
|
||||
baseCache = new RedissonMultimapCache<K>(connectionManager, name, codec, getTimeoutSetName());
|
||||
}
|
||||
|
||||
RedissonListMultimapCache(EvictionScheduler evictionScheduler, Codec codec, CommandAsyncExecutor connectionManager, String name) {
|
||||
super(codec, connectionManager, name);
|
||||
evictionScheduler.scheduleCleanMultimap(name, getTimeoutSetName());
|
||||
baseCache = new RedissonMultimapCache<K>(connectionManager, name, codec, getTimeoutSetName());
|
||||
}
|
||||
|
||||
public Future<Boolean> containsKeyAsync(Object key) {
|
||||
try {
|
||||
byte[] keyState = codec.getMapKeyEncoder().encode(key);
|
||||
String keyHash = hash(keyState);
|
||||
|
||||
String valuesName = getValuesName(keyHash);
|
||||
|
||||
return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN,
|
||||
"local value = redis.call('hget', KEYS[1], ARGV[2]); " +
|
||||
"if value ~= false then " +
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[2], ARGV[2]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore) "
|
||||
+ "end; "
|
||||
+ "if expireDate <= tonumber(ARGV[1]) then "
|
||||
+ "return 0;"
|
||||
+ "end; "
|
||||
+ "return redis.call('llen', ARGV[3]) > 0 and 1 or 0;" +
|
||||
"end;" +
|
||||
"return 0; ",
|
||||
Arrays.<Object>asList(getName(), getTimeoutSetName()), System.currentTimeMillis(), keyState, valuesName);
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
String getTimeoutSetName() {
|
||||
return "redisson_list_multimap_ttl{" + getName() + "}";
|
||||
}
|
||||
|
||||
|
||||
public Future<Boolean> containsValueAsync(Object value) {
|
||||
try {
|
||||
byte[] valueState = codec.getMapValueEncoder().encode(value);
|
||||
|
||||
return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN,
|
||||
"local keys = redis.call('hgetall', KEYS[1]); " +
|
||||
"for i, v in ipairs(keys) do " +
|
||||
"if i % 2 == 0 then " +
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[2], keys[i-1]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore) "
|
||||
+ "end; "
|
||||
+ "if expireDate > tonumber(ARGV[2]) then " +
|
||||
"local name = '{' .. KEYS[1] .. '}:' .. v; " +
|
||||
|
||||
"local items = redis.call('lrange', name, 0, -1) " +
|
||||
"for i=1,#items do " +
|
||||
"if items[i] == ARGV[1] then " +
|
||||
"return 1; " +
|
||||
"end; " +
|
||||
"end; " +
|
||||
|
||||
"end; " +
|
||||
"end;" +
|
||||
"end; " +
|
||||
"return 0; ",
|
||||
Arrays.<Object>asList(getName(), getTimeoutSetName()), valueState, System.currentTimeMillis());
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Future<Boolean> containsEntryAsync(Object key, Object value) {
|
||||
try {
|
||||
byte[] keyState = codec.getMapKeyEncoder().encode(key);
|
||||
String keyHash = hash(keyState);
|
||||
byte[] valueState = codec.getMapValueEncoder().encode(value);
|
||||
|
||||
String valuesName = getValuesName(keyHash);
|
||||
return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN,
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[2], ARGV[2]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore) "
|
||||
+ "end; "
|
||||
+ "if expireDate > tonumber(ARGV[1]) then " +
|
||||
"local items = redis.call('lrange', KEYS[1], 0, -1); " +
|
||||
"for i=0, #items do " +
|
||||
"if items[i] == ARGV[3] then " +
|
||||
"return 1; " +
|
||||
"end; " +
|
||||
"end; " +
|
||||
"end; " +
|
||||
"return 0; ",
|
||||
Arrays.<Object>asList(valuesName, getTimeoutSetName()), System.currentTimeMillis(), keyState, valueState);
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<V> get(K key) {
|
||||
try {
|
||||
byte[] keyState = codec.getMapKeyEncoder().encode(key);
|
||||
String keyHash = hash(keyState);
|
||||
String valuesName = getValuesName(keyHash);
|
||||
|
||||
return new RedissonListMultimapValues<V>(codec, commandExecutor, valuesName, getTimeoutSetName(), key);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Future<Collection<V>> getAllAsync(K key) {
|
||||
try {
|
||||
byte[] keyState = codec.getMapKeyEncoder().encode(key);
|
||||
String keyHash = hash(keyState);
|
||||
String valuesName = getValuesName(keyHash);
|
||||
|
||||
return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_LIST,
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[2], ARGV[2]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore) "
|
||||
+ "end; "
|
||||
+ "if expireDate > tonumber(ARGV[1]) then " +
|
||||
"return redis.call('lrange', KEYS[1], 0, -1); " +
|
||||
"end; " +
|
||||
"return {}; ",
|
||||
Arrays.<Object>asList(valuesName, getTimeoutSetName()), System.currentTimeMillis(), keyState);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Future<Collection<V>> removeAllAsync(Object key) {
|
||||
try {
|
||||
byte[] keyState = codec.getMapKeyEncoder().encode(key);
|
||||
String keyHash = hash(keyState);
|
||||
|
||||
String valuesName = getValuesName(keyHash);
|
||||
return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_SET,
|
||||
"redis.call('hdel', KEYS[1], ARGV[1]); " +
|
||||
"local members = redis.call('lrange', KEYS[2], 0, -1); " +
|
||||
"redis.call('del', KEYS[2]); " +
|
||||
"redis.call('zrem', KEYS[3], ARGV[1]); " +
|
||||
"return members; ",
|
||||
Arrays.<Object>asList(getName(), valuesName, getTimeoutSetName()), keyState);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean expireKey(K key, long timeToLive, TimeUnit timeUnit) {
|
||||
return get(expireKeyAsync(key, timeToLive, timeUnit));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> expireKeyAsync(K key, long timeToLive, TimeUnit timeUnit) {
|
||||
return baseCache.expireKeyAsync(key, timeToLive, timeUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> deleteAsync() {
|
||||
return baseCache.deleteAsync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> expireAsync(long timeToLive, TimeUnit timeUnit) {
|
||||
return baseCache.expireAsync(timeToLive, timeUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> expireAtAsync(long timestamp) {
|
||||
return baseCache.expireAtAsync(timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> clearExpireAsync() {
|
||||
return baseCache.clearExpireAsync();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,688 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import static org.redisson.client.protocol.RedisCommands.EVAL_OBJECT;
|
||||
import static org.redisson.client.protocol.RedisCommands.LPOP;
|
||||
import static org.redisson.client.protocol.RedisCommands.LPUSH_BOOLEAN;
|
||||
import static org.redisson.client.protocol.RedisCommands.RPUSH_BOOLEAN;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
|
||||
import org.redisson.client.codec.Codec;
|
||||
import org.redisson.client.protocol.RedisCommand;
|
||||
import org.redisson.client.protocol.RedisCommand.ValueType;
|
||||
import org.redisson.client.protocol.RedisCommands;
|
||||
import org.redisson.client.protocol.convertor.BooleanNumberReplayConvertor;
|
||||
import org.redisson.client.protocol.convertor.BooleanReplayConvertor;
|
||||
import org.redisson.client.protocol.convertor.Convertor;
|
||||
import org.redisson.client.protocol.convertor.IntegerReplayConvertor;
|
||||
import org.redisson.client.protocol.decoder.ObjectSetReplayDecoder;
|
||||
import org.redisson.command.CommandAsyncExecutor;
|
||||
import org.redisson.core.RList;
|
||||
|
||||
import io.netty.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* List based Multimap Cache values holder
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
*
|
||||
* @param <V> the type of elements held in this collection
|
||||
*/
|
||||
public class RedissonListMultimapValues<V> extends RedissonExpirable implements RList<V> {
|
||||
|
||||
private static final RedisCommand<Integer> LAST_INDEX = new RedisCommand<Integer>("EVAL", new IntegerReplayConvertor(), 4, Arrays.asList(ValueType.MAP_KEY, ValueType.MAP_VALUE));
|
||||
private static final RedisCommand<Integer> EVAL_SIZE = new RedisCommand<Integer>("EVAL", new IntegerReplayConvertor(), 6, ValueType.MAP_KEY);
|
||||
private static final RedisCommand<Integer> EVAL_GET = new RedisCommand<Integer>("EVAL", 7, ValueType.MAP_KEY);
|
||||
private static final RedisCommand<Set<Object>> EVAL_READALL = new RedisCommand<Set<Object>>("EVAL", new ObjectSetReplayDecoder(), 6, ValueType.MAP_KEY);
|
||||
private static final RedisCommand<Boolean> EVAL_CONTAINS_VALUE = new RedisCommand<Boolean>("EVAL", new BooleanReplayConvertor(), 7, Arrays.asList(ValueType.MAP_KEY, ValueType.MAP_VALUE));
|
||||
private static final RedisCommand<Boolean> EVAL_CONTAINS_ALL_WITH_VALUES = new RedisCommand<Boolean>("EVAL", new BooleanReplayConvertor(), 7, ValueType.OBJECTS);
|
||||
|
||||
|
||||
public static final RedisCommand<Boolean> EVAL_BOOLEAN_ARGS2 = new RedisCommand<Boolean>("EVAL", new BooleanReplayConvertor(), 5, ValueType.OBJECTS);
|
||||
|
||||
private final Object key;
|
||||
private final String timeoutSetName;
|
||||
|
||||
public RedissonListMultimapValues(Codec codec, CommandAsyncExecutor commandExecutor, String name, String timeoutSetName, Object key) {
|
||||
super(codec, commandExecutor, name);
|
||||
this.timeoutSetName = timeoutSetName;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return get(sizeAsync());
|
||||
}
|
||||
|
||||
public Future<Integer> sizeAsync() {
|
||||
return commandExecutor.evalReadAsync(getName(), codec, EVAL_SIZE,
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[2]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore) "
|
||||
+ "end; "
|
||||
+ "if expireDate <= tonumber(ARGV[1]) then "
|
||||
+ "return 0;"
|
||||
+ "end; "
|
||||
+ "return redis.call('llen', KEYS[2]);",
|
||||
Arrays.<Object>asList(timeoutSetName, getName()), System.currentTimeMillis(), key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return get(containsAsync(o));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<V> iterator() {
|
||||
return listIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
List<V> list = readAll();
|
||||
return list.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<V> readAll() {
|
||||
return get(readAllAsync());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<List<V>> readAllAsync() {
|
||||
return commandExecutor.evalReadAsync(getName(), codec, EVAL_READALL,
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[2]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore) "
|
||||
+ "end; "
|
||||
+ "if expireDate <= tonumber(ARGV[1]) then "
|
||||
+ "return {};"
|
||||
+ "end; "
|
||||
+ "return redis.call('lrange', KEYS[2], 0, -1);",
|
||||
Arrays.<Object>asList(timeoutSetName, getName()), System.currentTimeMillis(), key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T[] toArray(T[] a) {
|
||||
List<V> list = readAll();
|
||||
return list.toArray(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(V e) {
|
||||
return get(addAsync(e));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> addAsync(V e) {
|
||||
return commandExecutor.writeAsync(getName(), codec, RPUSH_BOOLEAN, getName(), e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
return get(removeAsync(o));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> removeAsync(Object o) {
|
||||
return removeAsync(o, 1);
|
||||
}
|
||||
|
||||
protected Future<Boolean> removeAsync(Object o, int count) {
|
||||
return commandExecutor.evalWriteAsync(getName(), codec, EVAL_CONTAINS_VALUE,
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[3]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore) "
|
||||
+ "end; "
|
||||
+ "if expireDate <= tonumber(ARGV[1]) then "
|
||||
+ "return 0;"
|
||||
+ "end; "
|
||||
+ "return redis.call('lrem', KEYS[2], ARGV[2], ARGV[4]) > 0 and 1 or 0;",
|
||||
Arrays.<Object>asList(timeoutSetName, getName()), System.currentTimeMillis(), count, key, o);
|
||||
}
|
||||
|
||||
protected boolean remove(Object o, int count) {
|
||||
return get(removeAsync(o, count));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> containsAllAsync(Collection<?> c) {
|
||||
List<Object> args = new ArrayList<Object>(c.size() + 2);
|
||||
try {
|
||||
byte[] keyState = codec.getMapKeyEncoder().encode(key);
|
||||
args.add(System.currentTimeMillis());
|
||||
args.add(keyState);
|
||||
args.addAll(c);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return commandExecutor.evalReadAsync(getName(), codec, EVAL_CONTAINS_ALL_WITH_VALUES,
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[2]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore) "
|
||||
+ "end; "
|
||||
+ "if expireDate <= tonumber(ARGV[1]) then "
|
||||
+ "return 0;"
|
||||
+ "end; " +
|
||||
"local items = redis.call('lrange', KEYS[2], 0, -1);" +
|
||||
"for i = 1, #items, 1 do " +
|
||||
"for j = 2, #ARGV, 1 do "
|
||||
+ "if ARGV[j] == items[i] "
|
||||
+ "then table.remove(ARGV, j) end "
|
||||
+ "end; "
|
||||
+ "end;"
|
||||
+ "return #ARGV == 2 and 1 or 0; ",
|
||||
Arrays.<Object>asList(timeoutSetName, getName()), args.toArray());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> c) {
|
||||
return get(containsAllAsync(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends V> c) {
|
||||
return get(addAllAsync(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> addAllAsync(final Collection<? extends V> c) {
|
||||
if (c.isEmpty()) {
|
||||
return newSucceededFuture(false);
|
||||
}
|
||||
|
||||
List<Object> args = new ArrayList<Object>(c.size() + 1);
|
||||
args.add(getName());
|
||||
args.addAll(c);
|
||||
return commandExecutor.writeAsync(getName(), codec, RPUSH_BOOLEAN, args.toArray());
|
||||
}
|
||||
|
||||
public Future<Boolean> addAllAsync(int index, Collection<? extends V> coll) {
|
||||
if (index < 0) {
|
||||
throw new IndexOutOfBoundsException("index: " + index);
|
||||
}
|
||||
|
||||
if (coll.isEmpty()) {
|
||||
return newSucceededFuture(false);
|
||||
}
|
||||
|
||||
if (index == 0) { // prepend elements to list
|
||||
List<Object> elements = new ArrayList<Object>(coll);
|
||||
Collections.reverse(elements);
|
||||
elements.add(0, getName());
|
||||
|
||||
return commandExecutor.writeAsync(getName(), codec, LPUSH_BOOLEAN, elements.toArray());
|
||||
}
|
||||
|
||||
List<Object> args = new ArrayList<Object>(coll.size() + 1);
|
||||
args.add(index);
|
||||
args.addAll(coll);
|
||||
return commandExecutor.evalWriteAsync(getName(), codec, EVAL_BOOLEAN_ARGS2,
|
||||
"local ind = table.remove(ARGV, 1); " + // index is the first parameter
|
||||
"local size = redis.call('llen', KEYS[1]); " +
|
||||
"assert(tonumber(ind) <= size, 'index: ' .. ind .. ' but current size: ' .. size); " +
|
||||
"local tail = redis.call('lrange', KEYS[1], ind, -1); " +
|
||||
"redis.call('ltrim', KEYS[1], 0, ind - 1); " +
|
||||
"for i=1, #ARGV, 5000 do "
|
||||
+ "redis.call('rpush', KEYS[1], unpack(ARGV, i, math.min(i+4999, #ARGV))); "
|
||||
+ "end " +
|
||||
"if #tail > 0 then " +
|
||||
"for i=1, #tail, 5000 do "
|
||||
+ "redis.call('rpush', KEYS[1], unpack(tail, i, math.min(i+4999, #tail))); "
|
||||
+ "end "
|
||||
+ "end;" +
|
||||
"return 1;",
|
||||
Collections.<Object>singletonList(getName()), args.toArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends V> coll) {
|
||||
return get(addAllAsync(index, coll));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> removeAllAsync(Collection<?> c) {
|
||||
List<Object> args = new ArrayList<Object>(c.size() + 2);
|
||||
try {
|
||||
byte[] keyState = codec.getMapKeyEncoder().encode(key);
|
||||
args.add(System.currentTimeMillis());
|
||||
args.add(keyState);
|
||||
args.addAll(c);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return commandExecutor.evalWriteAsync(getName(), codec, EVAL_CONTAINS_ALL_WITH_VALUES,
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[2]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore) "
|
||||
+ "end; "
|
||||
+ "if expireDate <= tonumber(ARGV[1]) then "
|
||||
+ "return 0;"
|
||||
+ "end; " +
|
||||
|
||||
"local v = 0 " +
|
||||
"for i = 2, #ARGV, 1 do "
|
||||
+ "if redis.call('lrem', KEYS[2], 0, ARGV[i]) == 1 "
|
||||
+ "then v = 1 end "
|
||||
+"end "
|
||||
+ "return v ",
|
||||
Arrays.<Object>asList(timeoutSetName, getName()), args.toArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
return get(removeAllAsync(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
return get(retainAllAsync(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> retainAllAsync(Collection<?> c) {
|
||||
List<Object> args = new ArrayList<Object>(c.size() + 2);
|
||||
try {
|
||||
byte[] keyState = codec.getMapKeyEncoder().encode(key);
|
||||
args.add(System.currentTimeMillis());
|
||||
args.add(keyState);
|
||||
args.addAll(c);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return commandExecutor.evalWriteAsync(getName(), codec, EVAL_CONTAINS_ALL_WITH_VALUES,
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[2]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore) "
|
||||
+ "end; "
|
||||
+ "if expireDate <= tonumber(ARGV[1]) then "
|
||||
+ "return 0;"
|
||||
+ "end; " +
|
||||
|
||||
"local changed = 0; " +
|
||||
"local s = redis.call('lrange', KEYS[2], 0, -1); "
|
||||
+ "local i = 1; "
|
||||
+ "while i <= #s do "
|
||||
+ "local element = s[i]; "
|
||||
+ "local isInAgrs = false; "
|
||||
+ "for j = 2, #ARGV, 1 do "
|
||||
+ "if ARGV[j] == element then "
|
||||
+ "isInAgrs = true; "
|
||||
+ "break; "
|
||||
+ "end; "
|
||||
+ "end; "
|
||||
+ "if isInAgrs == false then "
|
||||
+ "redis.call('lrem', KEYS[2], 0, element); "
|
||||
+ "changed = 1; "
|
||||
+ "end; "
|
||||
+ "i = i + 1; "
|
||||
+ "end; "
|
||||
+ "return changed; ",
|
||||
Arrays.<Object>asList(timeoutSetName, getName()), args.toArray());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
delete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<V> getAsync(int index) {
|
||||
return commandExecutor.evalReadAsync(getName(), codec, EVAL_GET,
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[3]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore); "
|
||||
+ "end; "
|
||||
+ "if expireDate <= tonumber(ARGV[1]) then "
|
||||
+ "return nil;"
|
||||
+ "end; "
|
||||
+ "return redis.call('lindex', KEYS[2], ARGV[2]);",
|
||||
Arrays.<Object>asList(timeoutSetName, getName()), System.currentTimeMillis(), index, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(int index) {
|
||||
checkIndex(index);
|
||||
return getValue(index);
|
||||
}
|
||||
|
||||
V getValue(int index) {
|
||||
return get(getAsync(index));
|
||||
}
|
||||
|
||||
private void checkIndex(int index) {
|
||||
int size = size();
|
||||
if (!isInRange(index, size))
|
||||
throw new IndexOutOfBoundsException("index: " + index + " but current size: "+ size);
|
||||
}
|
||||
|
||||
private boolean isInRange(int index, int size) {
|
||||
return index >= 0 && index < size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V set(int index, V element) {
|
||||
checkIndex(index);
|
||||
return get(setAsync(index, element));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<V> setAsync(int index, V element) {
|
||||
return commandExecutor.evalWriteAsync(getName(), codec, new RedisCommand<Object>("EVAL", 5),
|
||||
"local v = redis.call('lindex', KEYS[1], ARGV[1]); " +
|
||||
"redis.call('lset', KEYS[1], ARGV[1], ARGV[2]); " +
|
||||
"return v",
|
||||
Collections.<Object>singletonList(getName()), index, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fastSet(int index, V element) {
|
||||
get(fastSetAsync(index, element));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> fastSetAsync(int index, V element) {
|
||||
return commandExecutor.writeAsync(getName(), codec, RedisCommands.LSET, getName(), index, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, V element) {
|
||||
addAll(index, Collections.singleton(element));
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(int index) {
|
||||
checkIndex(index);
|
||||
|
||||
if (index == 0) {
|
||||
Future<V> f = commandExecutor.writeAsync(getName(), codec, LPOP, getName());
|
||||
return get(f);
|
||||
}
|
||||
|
||||
Future<V> f = commandExecutor.evalWriteAsync(getName(), codec, EVAL_OBJECT,
|
||||
"local v = redis.call('lindex', KEYS[1], ARGV[1]); " +
|
||||
"local tail = redis.call('lrange', KEYS[1], ARGV[1] + 1, -1);" +
|
||||
"redis.call('ltrim', KEYS[1], 0, ARGV[1] - 1);" +
|
||||
"if #tail > 0 then " +
|
||||
"for i=1, #tail, 5000 do "
|
||||
+ "redis.call('rpush', KEYS[1], unpack(tail, i, math.min(i+4999, #tail))); "
|
||||
+ "end "
|
||||
+ "end;" +
|
||||
"return v",
|
||||
Collections.<Object>singletonList(getName()), index);
|
||||
return get(f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(Object o) {
|
||||
return get(indexOfAsync(o));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> containsAsync(Object o) {
|
||||
return indexOfAsync(o, new BooleanNumberReplayConvertor(-1L));
|
||||
}
|
||||
|
||||
private <R> Future<R> indexOfAsync(Object o, Convertor<R> convertor) {
|
||||
return commandExecutor.evalReadAsync(getName(), codec, new RedisCommand<R>("EVAL", convertor, 6, Arrays.asList(ValueType.MAP_KEY, ValueType.MAP_VALUE)),
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[2]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore); "
|
||||
+ "end; "
|
||||
+ "if expireDate <= tonumber(ARGV[1]) then "
|
||||
+ "return -1;"
|
||||
+ "end; " +
|
||||
|
||||
"local items = redis.call('lrange', KEYS[2], 0, -1); " +
|
||||
"for i=1,#items do " +
|
||||
"if items[i] == ARGV[3] then " +
|
||||
"return i - 1; " +
|
||||
"end; " +
|
||||
"end; " +
|
||||
"return -1;",
|
||||
Arrays.<Object>asList(timeoutSetName, getName()), System.currentTimeMillis(), key, o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Integer> indexOfAsync(Object o) {
|
||||
return indexOfAsync(o, new IntegerReplayConvertor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lastIndexOf(Object o) {
|
||||
return get(lastIndexOfAsync(o));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Integer> lastIndexOfAsync(Object o) {
|
||||
return commandExecutor.evalReadAsync(getName(), codec, LAST_INDEX,
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[2]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore); "
|
||||
+ "end; "
|
||||
+ "if expireDate <= tonumber(ARGV[1]) then "
|
||||
+ "return -1;"
|
||||
+ "end; " +
|
||||
|
||||
"local items = redis.call('lrange', KEYS[1], 0, -1) " +
|
||||
"for i = #items, 1, -1 do " +
|
||||
"if items[i] == ARGV[1] then " +
|
||||
"return i - 1 " +
|
||||
"end " +
|
||||
"end " +
|
||||
"return -1",
|
||||
Arrays.<Object>asList(timeoutSetName, getName()), System.currentTimeMillis(), key, o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trim(int fromIndex, int toIndex) {
|
||||
get(trimAsync(fromIndex, toIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> trimAsync(int fromIndex, int toIndex) {
|
||||
return commandExecutor.writeAsync(getName(), codec, RedisCommands.LTRIM, getName(), fromIndex, toIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<V> listIterator() {
|
||||
return listIterator(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<V> listIterator(final int ind) {
|
||||
return new ListIterator<V>() {
|
||||
|
||||
private V prevCurrentValue;
|
||||
private V nextCurrentValue;
|
||||
private V currentValueHasRead;
|
||||
private int currentIndex = ind - 1;
|
||||
private boolean hasBeenModified = true;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
V val = RedissonListMultimapValues.this.getValue(currentIndex+1);
|
||||
if (val != null) {
|
||||
nextCurrentValue = val;
|
||||
}
|
||||
return val != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V next() {
|
||||
if (nextCurrentValue == null && !hasNext()) {
|
||||
throw new NoSuchElementException("No such element at index " + currentIndex);
|
||||
}
|
||||
currentIndex++;
|
||||
currentValueHasRead = nextCurrentValue;
|
||||
nextCurrentValue = null;
|
||||
hasBeenModified = false;
|
||||
return currentValueHasRead;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
if (currentValueHasRead == null) {
|
||||
throw new IllegalStateException("Neither next nor previous have been called");
|
||||
}
|
||||
if (hasBeenModified) {
|
||||
throw new IllegalStateException("Element been already deleted");
|
||||
}
|
||||
RedissonListMultimapValues.this.remove(currentIndex);
|
||||
currentIndex--;
|
||||
hasBeenModified = true;
|
||||
currentValueHasRead = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPrevious() {
|
||||
if (currentIndex < 0) {
|
||||
return false;
|
||||
}
|
||||
V val = RedissonListMultimapValues.this.getValue(currentIndex);
|
||||
if (val != null) {
|
||||
prevCurrentValue = val;
|
||||
}
|
||||
return val != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V previous() {
|
||||
if (prevCurrentValue == null && !hasPrevious()) {
|
||||
throw new NoSuchElementException("No such element at index " + currentIndex);
|
||||
}
|
||||
currentIndex--;
|
||||
hasBeenModified = false;
|
||||
currentValueHasRead = prevCurrentValue;
|
||||
prevCurrentValue = null;
|
||||
return currentValueHasRead;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextIndex() {
|
||||
return currentIndex + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int previousIndex() {
|
||||
return currentIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(V e) {
|
||||
if (hasBeenModified) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
RedissonListMultimapValues.this.fastSet(currentIndex, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(V e) {
|
||||
RedissonListMultimapValues.this.add(currentIndex+1, e);
|
||||
currentIndex++;
|
||||
hasBeenModified = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public RList<V> subList(int fromIndex, int toIndex) {
|
||||
int size = size();
|
||||
if (fromIndex < 0 || toIndex > size) {
|
||||
throw new IndexOutOfBoundsException("fromIndex: " + fromIndex + " toIndex: " + toIndex + " size: " + size);
|
||||
}
|
||||
if (fromIndex > toIndex) {
|
||||
throw new IllegalArgumentException("fromIndex: " + fromIndex + " toIndex: " + toIndex);
|
||||
}
|
||||
|
||||
return new RedissonSubList<V>(codec, commandExecutor, getName(), fromIndex, toIndex);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
Iterator<V> it = iterator();
|
||||
if (! it.hasNext())
|
||||
return "[]";
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('[');
|
||||
for (;;) {
|
||||
V e = it.next();
|
||||
sb.append(e == this ? "(this Collection)" : e);
|
||||
if (! it.hasNext())
|
||||
return sb.append(']').toString();
|
||||
sb.append(',').append(' ');
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this)
|
||||
return true;
|
||||
if (!(o instanceof List))
|
||||
return false;
|
||||
|
||||
Iterator<V> e1 = iterator();
|
||||
Iterator<?> e2 = ((List<?>) o).iterator();
|
||||
while (e1.hasNext() && e2.hasNext()) {
|
||||
V o1 = e1.next();
|
||||
Object o2 = e2.next();
|
||||
if (!(o1==null ? o2==null : o1.equals(o2)))
|
||||
return false;
|
||||
}
|
||||
return !(e1.hasNext() || e2.hasNext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = 1;
|
||||
for (V e : this) {
|
||||
hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.redisson.client.codec.Codec;
|
||||
import org.redisson.client.codec.LongCodec;
|
||||
import org.redisson.client.protocol.RedisCommand;
|
||||
import org.redisson.client.protocol.RedisCommands;
|
||||
import org.redisson.client.protocol.RedisCommand.ValueType;
|
||||
import org.redisson.client.protocol.convertor.BooleanReplayConvertor;
|
||||
import org.redisson.command.CommandAsyncExecutor;
|
||||
|
||||
import io.netty.util.concurrent.Future;
|
||||
|
||||
public class RedissonMultimapCache<K> {
|
||||
|
||||
private static final RedisCommand<Boolean> EVAL_EXPIRE_KEY = new RedisCommand<Boolean>("EVAL", new BooleanReplayConvertor(), 6, ValueType.MAP_KEY);
|
||||
|
||||
private final CommandAsyncExecutor commandExecutor;
|
||||
private final String name;
|
||||
private final Codec codec;
|
||||
private final String timeoutSetName;
|
||||
|
||||
public RedissonMultimapCache(CommandAsyncExecutor commandExecutor, String name, Codec codec, String timeoutSetName) {
|
||||
this.commandExecutor = commandExecutor;
|
||||
this.name = name;
|
||||
this.codec = codec;
|
||||
this.timeoutSetName = timeoutSetName;
|
||||
}
|
||||
|
||||
public Future<Boolean> expireKeyAsync(K key, long timeToLive, TimeUnit timeUnit) {
|
||||
long ttlTimeout = System.currentTimeMillis() + timeUnit.toMillis(timeToLive);
|
||||
|
||||
return commandExecutor.evalWriteAsync(name, codec, EVAL_EXPIRE_KEY,
|
||||
"if redis.call('hexists', KEYS[1], ARGV[2]) == 1 then "
|
||||
+ "if tonumber(ARGV[1]) > 0 then "
|
||||
+ "redis.call('zadd', KEYS[2], ARGV[1], ARGV[2]); " +
|
||||
"else " +
|
||||
"redis.call('zrem', KEYS[2], ARGV[2]); "
|
||||
+ "end; "
|
||||
+ "return 1; "
|
||||
+ "else "
|
||||
+ "return 0; "
|
||||
+ "end",
|
||||
Arrays.<Object>asList(name, timeoutSetName), ttlTimeout, key);
|
||||
}
|
||||
|
||||
public Future<Boolean> deleteAsync() {
|
||||
return commandExecutor.evalWriteAsync(name, LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN_AMOUNT,
|
||||
"local entries = redis.call('hgetall', KEYS[1]); " +
|
||||
"local keys = {KEYS[1], KEYS[2]}; " +
|
||||
"for i, v in ipairs(entries) do " +
|
||||
"if i % 2 == 0 then " +
|
||||
"local name = '{' .. KEYS[1] .. '}:' .. v; " +
|
||||
"table.insert(keys, name); " +
|
||||
"end;" +
|
||||
"end; " +
|
||||
|
||||
"local n = 0 "
|
||||
+ "for i=1, #keys,5000 do "
|
||||
+ "n = n + redis.call('del', unpack(keys, i, math.min(i+4999, table.getn(keys)))) "
|
||||
+ "end; "
|
||||
+ "return n;",
|
||||
Arrays.<Object>asList(name, timeoutSetName));
|
||||
}
|
||||
|
||||
public Future<Boolean> expireAsync(long timeToLive, TimeUnit timeUnit) {
|
||||
return commandExecutor.evalWriteAsync(name, LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
|
||||
"redis.call('zadd', KEYS[2], 92233720368547758, 'redisson__expiretag'); " +
|
||||
"local entries = redis.call('hgetall', KEYS[1]); " +
|
||||
"for i, v in ipairs(entries) do " +
|
||||
"if i % 2 == 0 then " +
|
||||
"local name = '{' .. KEYS[1] .. '}:' .. v; " +
|
||||
"redis.call('pexpire', name, ARGV[1]); " +
|
||||
"end;" +
|
||||
"end; " +
|
||||
"redis.call('pexpire', KEYS[2], ARGV[1]); " +
|
||||
"return redis.call('pexpire', KEYS[1], ARGV[1]); ",
|
||||
Arrays.<Object>asList(name, timeoutSetName), timeUnit.toMillis(timeToLive));
|
||||
}
|
||||
|
||||
public Future<Boolean> expireAtAsync(long timestamp) {
|
||||
return commandExecutor.evalWriteAsync(name, LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
|
||||
"redis.call('zadd', KEYS[2], 92233720368547758, 'redisson__expiretag');" +
|
||||
"local entries = redis.call('hgetall', KEYS[1]); " +
|
||||
"for i, v in ipairs(entries) do " +
|
||||
"if i % 2 == 0 then " +
|
||||
"local name = '{' .. KEYS[1] .. '}:' .. v; " +
|
||||
"redis.call('pexpireat', name, ARGV[1]); " +
|
||||
"end;" +
|
||||
"end; " +
|
||||
"redis.call('pexpireat', KEYS[2], ARGV[1]); " +
|
||||
"return redis.call('pexpireat', KEYS[1], ARGV[1]); ",
|
||||
Arrays.<Object>asList(name, timeoutSetName), timestamp);
|
||||
}
|
||||
|
||||
public Future<Boolean> clearExpireAsync() {
|
||||
return commandExecutor.evalWriteAsync(name, LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
|
||||
"redis.call('zrem', KEYS[2], 'redisson__expiretag'); " +
|
||||
"local entries = redis.call('hgetall', KEYS[1]); " +
|
||||
"for i, v in ipairs(entries) do " +
|
||||
"if i % 2 == 0 then " +
|
||||
"local name = '{' .. KEYS[1] .. '}:' .. v; " +
|
||||
"redis.call('persist', name); " +
|
||||
"end;" +
|
||||
"end; " +
|
||||
"redis.call('persist', KEYS[2]); " +
|
||||
"return redis.call('persist', KEYS[1]); ",
|
||||
Arrays.<Object>asList(name, timeoutSetName));
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,212 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.redisson.core.RBatch;
|
||||
import org.redisson.core.RBlockingQueue;
|
||||
import org.redisson.core.RBlockingQueueAsync;
|
||||
import org.redisson.core.RRemoteService;
|
||||
import org.redisson.remote.RRemoteServiceResponse;
|
||||
import org.redisson.remote.RemoteServiceAck;
|
||||
import org.redisson.remote.RemoteServiceAckTimeoutException;
|
||||
import org.redisson.remote.RemoteServiceKey;
|
||||
import org.redisson.remote.RemoteServiceMethod;
|
||||
import org.redisson.remote.RemoteServiceRequest;
|
||||
import org.redisson.remote.RemoteServiceResponse;
|
||||
import org.redisson.remote.RemoteServiceTimeoutException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.FutureListener;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
import io.netty.util.internal.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
*
|
||||
*/
|
||||
public class RedissonRemoteService implements RRemoteService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(RedissonRemoteService.class);
|
||||
|
||||
private final Map<RemoteServiceKey, RemoteServiceMethod> beans = PlatformDependent.newConcurrentHashMap();
|
||||
|
||||
private final Redisson redisson;
|
||||
|
||||
public RedissonRemoteService(Redisson redisson) {
|
||||
this.redisson = redisson;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void register(Class<T> remoteInterface, T object) {
|
||||
register(remoteInterface, object, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void register(Class<T> remoteInterface, T object, int executorsAmount) {
|
||||
if (executorsAmount < 1) {
|
||||
throw new IllegalArgumentException("executorsAmount can't be lower than 1");
|
||||
}
|
||||
for (Method method : remoteInterface.getMethods()) {
|
||||
RemoteServiceMethod value = new RemoteServiceMethod(method, object);
|
||||
RemoteServiceKey key = new RemoteServiceKey(remoteInterface, method.getName());
|
||||
if (beans.put(key, value) != null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < executorsAmount; i++) {
|
||||
String requestQueueName = "redisson_remote_service:{" + remoteInterface.getName() + "}";
|
||||
RBlockingQueue<RemoteServiceRequest> requestQueue = redisson.getBlockingQueue(requestQueueName);
|
||||
subscribe(remoteInterface, requestQueue);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> void subscribe(final Class<T> remoteInterface, final RBlockingQueue<RemoteServiceRequest> requestQueue) {
|
||||
Future<RemoteServiceRequest> take = requestQueue.takeAsync();
|
||||
take.addListener(new FutureListener<RemoteServiceRequest>() {
|
||||
@Override
|
||||
public void operationComplete(Future<RemoteServiceRequest> future) throws Exception {
|
||||
if (!future.isSuccess()) {
|
||||
if (future.cause() instanceof RedissonShutdownException) {
|
||||
return;
|
||||
}
|
||||
subscribe(remoteInterface, requestQueue);
|
||||
return;
|
||||
}
|
||||
subscribe(remoteInterface, requestQueue);
|
||||
|
||||
final RemoteServiceRequest request = future.getNow();
|
||||
if (System.currentTimeMillis() - request.getDate() > request.getAckTimeout()) {
|
||||
log.debug("request: {} has been skipped due to ackTimeout");
|
||||
return;
|
||||
}
|
||||
|
||||
final RemoteServiceMethod method = beans.get(new RemoteServiceKey(remoteInterface, request.getMethodName()));
|
||||
final String responseName = "redisson_remote_service:{" + remoteInterface.getName() + "}:" + request.getRequestId();
|
||||
|
||||
Future<List<?>> ackClientsFuture = send(request.getAckTimeout(), responseName, new RemoteServiceAck());
|
||||
ackClientsFuture.addListener(new FutureListener<List<?>>() {
|
||||
@Override
|
||||
public void operationComplete(Future<List<?>> future) throws Exception {
|
||||
if (!future.isSuccess()) {
|
||||
log.error("Can't send ack for request: " + request, future.cause());
|
||||
return;
|
||||
}
|
||||
|
||||
invokeMethod(request, method, responseName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private void invokeMethod(final RemoteServiceRequest request, RemoteServiceMethod method, String responseName) {
|
||||
final AtomicReference<RemoteServiceResponse> responseHolder = new AtomicReference<RemoteServiceResponse>();
|
||||
try {
|
||||
Object result = method.getMethod().invoke(method.getBean(), request.getArgs());
|
||||
RemoteServiceResponse response = new RemoteServiceResponse(result);
|
||||
responseHolder.set(response);
|
||||
} catch (Exception e) {
|
||||
RemoteServiceResponse response = new RemoteServiceResponse(e.getCause());
|
||||
responseHolder.set(response);
|
||||
log.error("Can't execute: " + request, e);
|
||||
}
|
||||
|
||||
Future<List<?>> clientsFuture = send(request.getResponseTimeout(), responseName, responseHolder.get());
|
||||
clientsFuture.addListener(new FutureListener<List<?>>() {
|
||||
@Override
|
||||
public void operationComplete(Future<List<?>> future) throws Exception {
|
||||
if (!future.isSuccess()) {
|
||||
log.error("Can't send response: " + responseHolder.get() + " for request: " + request, future.cause());
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T get(Class<T> remoteInterface) {
|
||||
return get(remoteInterface, 30, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T get(final Class<T> remoteInterface, final long executionTimeout, final TimeUnit executionTimeUnit) {
|
||||
return get(remoteInterface, executionTimeout, executionTimeUnit, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public <T> T get(final Class<T> remoteInterface, final long executionTimeout, final TimeUnit executionTimeUnit,
|
||||
final long ackTimeout, final TimeUnit ackTimeUnit) {
|
||||
InvocationHandler handler = new InvocationHandler() {
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
String requestId = generateRequestId();
|
||||
|
||||
String requestQueueName = "redisson_remote_service:{" + remoteInterface.getName() + "}";
|
||||
RBlockingQueue<RemoteServiceRequest> requestQueue = redisson.getBlockingQueue(requestQueueName);
|
||||
RemoteServiceRequest request = new RemoteServiceRequest(requestId, method.getName(), args,
|
||||
ackTimeUnit.toMillis(ackTimeout), executionTimeUnit.toMillis(executionTimeout), System.currentTimeMillis());
|
||||
requestQueue.add(request);
|
||||
|
||||
String responseName = "redisson_remote_service:{" + remoteInterface.getName() + "}:" + requestId;
|
||||
RBlockingQueue<RRemoteServiceResponse> responseQueue = redisson.getBlockingQueue(responseName);
|
||||
|
||||
RemoteServiceAck ack = (RemoteServiceAck) responseQueue.poll(ackTimeout, ackTimeUnit);
|
||||
if (ack == null) {
|
||||
throw new RemoteServiceAckTimeoutException("No ACK response after " + ackTimeUnit.toMillis(ackTimeout) + "ms for request: " + request);
|
||||
}
|
||||
|
||||
RemoteServiceResponse response = (RemoteServiceResponse) responseQueue.poll(executionTimeout, executionTimeUnit);
|
||||
if (response == null) {
|
||||
throw new RemoteServiceTimeoutException("No response after " + executionTimeUnit.toMillis(executionTimeout) + "ms for request: " + request);
|
||||
}
|
||||
if (response.getError() != null) {
|
||||
throw response.getError();
|
||||
}
|
||||
return response.getResult();
|
||||
}
|
||||
};
|
||||
return (T) Proxy.newProxyInstance(remoteInterface.getClassLoader(), new Class[] {remoteInterface}, handler);
|
||||
}
|
||||
|
||||
private String generateRequestId() {
|
||||
byte[] id = new byte[16];
|
||||
// TODO JDK UPGRADE replace to native ThreadLocalRandom
|
||||
ThreadLocalRandom.current().nextBytes(id);
|
||||
return ByteBufUtil.hexDump(id);
|
||||
}
|
||||
|
||||
private <T extends RRemoteServiceResponse> Future<List<?>> send(long timeout, String responseName, T response) {
|
||||
RBatch batch = redisson.createBatch();
|
||||
RBlockingQueueAsync<T> queue = batch.getBlockingQueue(responseName);
|
||||
queue.putAsync(response);
|
||||
queue.expireAsync(timeout, TimeUnit.MILLISECONDS);
|
||||
return batch.executeAsync();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,224 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.redisson.client.codec.Codec;
|
||||
import org.redisson.client.protocol.RedisCommands;
|
||||
import org.redisson.command.CommandAsyncExecutor;
|
||||
import org.redisson.core.RSetMultimapCache;
|
||||
|
||||
import io.netty.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* @author Nikita Koksharov
|
||||
*
|
||||
* @param <K> key
|
||||
* @param <V> value
|
||||
*/
|
||||
public class RedissonSetMultimapCache<K, V> extends RedissonSetMultimap<K, V> implements RSetMultimapCache<K, V> {
|
||||
|
||||
private final RedissonMultimapCache<K> baseCache;
|
||||
|
||||
RedissonSetMultimapCache(EvictionScheduler evictionScheduler, CommandAsyncExecutor connectionManager, String name) {
|
||||
super(connectionManager, name);
|
||||
evictionScheduler.scheduleCleanMultimap(name, getTimeoutSetName());
|
||||
baseCache = new RedissonMultimapCache<K>(connectionManager, name, codec, getTimeoutSetName());
|
||||
}
|
||||
|
||||
RedissonSetMultimapCache(EvictionScheduler evictionScheduler, Codec codec, CommandAsyncExecutor connectionManager, String name) {
|
||||
super(codec, connectionManager, name);
|
||||
evictionScheduler.scheduleCleanMultimap(name, getTimeoutSetName());
|
||||
baseCache = new RedissonMultimapCache<K>(connectionManager, name, codec, getTimeoutSetName());
|
||||
}
|
||||
|
||||
public Future<Boolean> containsKeyAsync(Object key) {
|
||||
try {
|
||||
byte[] keyState = codec.getMapKeyEncoder().encode(key);
|
||||
String keyHash = hash(keyState);
|
||||
|
||||
String setName = getValuesName(keyHash);
|
||||
|
||||
return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN,
|
||||
"local value = redis.call('hget', KEYS[1], ARGV[2]); " +
|
||||
"if value ~= false then " +
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[2], ARGV[2]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore) "
|
||||
+ "end; "
|
||||
+ "if expireDate <= tonumber(ARGV[1]) then "
|
||||
+ "return 0;"
|
||||
+ "end; "
|
||||
+ "return redis.call('scard', ARGV[3]) > 0 and 1 or 0;" +
|
||||
"end;" +
|
||||
"return 0; ",
|
||||
Arrays.<Object>asList(getName(), getTimeoutSetName()), System.currentTimeMillis(), keyState, setName);
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
String getTimeoutSetName() {
|
||||
return "redisson_set_multimap_ttl{" + getName() + "}";
|
||||
}
|
||||
|
||||
|
||||
public Future<Boolean> containsValueAsync(Object value) {
|
||||
try {
|
||||
byte[] valueState = codec.getMapValueEncoder().encode(value);
|
||||
|
||||
return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN,
|
||||
"local keys = redis.call('hgetall', KEYS[1]); " +
|
||||
"for i, v in ipairs(keys) do " +
|
||||
"if i % 2 == 0 then " +
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[2], keys[i-1]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore) "
|
||||
+ "end; "
|
||||
+ "if expireDate > tonumber(ARGV[2]) then " +
|
||||
"local name = '{' .. KEYS[1] .. '}:' .. v; " +
|
||||
"if redis.call('sismember', name, ARGV[1]) == 1 then "
|
||||
+ "return 1; " +
|
||||
"end;" +
|
||||
"end; " +
|
||||
"end;" +
|
||||
"end; " +
|
||||
"return 0; ",
|
||||
Arrays.<Object>asList(getName(), getTimeoutSetName()), valueState, System.currentTimeMillis());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Future<Boolean> containsEntryAsync(Object key, Object value) {
|
||||
try {
|
||||
byte[] keyState = codec.getMapKeyEncoder().encode(key);
|
||||
String keyHash = hash(keyState);
|
||||
byte[] valueState = codec.getMapValueEncoder().encode(value);
|
||||
|
||||
String setName = getValuesName(keyHash);
|
||||
return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN,
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[2], ARGV[2]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore) "
|
||||
+ "end; "
|
||||
+ "if expireDate > tonumber(ARGV[1]) then " +
|
||||
"if redis.call('sismember', KEYS[1], ARGV[3]) == 1 then "
|
||||
+ "return 1; " +
|
||||
"end;" +
|
||||
"end; " +
|
||||
"return 0; ",
|
||||
Arrays.<Object>asList(setName, getTimeoutSetName()), System.currentTimeMillis(), keyState, valueState);
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> get(K key) {
|
||||
try {
|
||||
byte[] keyState = codec.getMapKeyEncoder().encode(key);
|
||||
String keyHash = hash(keyState);
|
||||
String setName = getValuesName(keyHash);
|
||||
|
||||
return new RedissonSetMultimapValues<V>(codec, commandExecutor, setName, getTimeoutSetName(), key);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Future<Collection<V>> getAllAsync(K key) {
|
||||
try {
|
||||
byte[] keyState = codec.getMapKeyEncoder().encode(key);
|
||||
String keyHash = hash(keyState);
|
||||
String setName = getValuesName(keyHash);
|
||||
|
||||
return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_SET,
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[2], ARGV[2]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore) "
|
||||
+ "end; "
|
||||
+ "if expireDate > tonumber(ARGV[1]) then " +
|
||||
"return redis.call('smembers', KEYS[1]); " +
|
||||
"end; " +
|
||||
"return {}; ",
|
||||
Arrays.<Object>asList(setName, getTimeoutSetName()), System.currentTimeMillis(), keyState);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Future<Collection<V>> removeAllAsync(Object key) {
|
||||
try {
|
||||
byte[] keyState = codec.getMapKeyEncoder().encode(key);
|
||||
String keyHash = hash(keyState);
|
||||
|
||||
String setName = getValuesName(keyHash);
|
||||
return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_SET,
|
||||
"redis.call('hdel', KEYS[1], ARGV[1]); " +
|
||||
"local members = redis.call('smembers', KEYS[2]); " +
|
||||
"redis.call('del', KEYS[2]); " +
|
||||
"redis.call('zrem', KEYS[3], ARGV[1]); " +
|
||||
"return members; ",
|
||||
Arrays.<Object>asList(getName(), setName, getTimeoutSetName()), keyState);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean expireKey(K key, long timeToLive, TimeUnit timeUnit) {
|
||||
return get(expireKeyAsync(key, timeToLive, timeUnit));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> expireKeyAsync(K key, long timeToLive, TimeUnit timeUnit) {
|
||||
return baseCache.expireKeyAsync(key, timeToLive, timeUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> deleteAsync() {
|
||||
return baseCache.deleteAsync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> expireAsync(long timeToLive, TimeUnit timeUnit) {
|
||||
return baseCache.expireAsync(timeToLive, timeUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> expireAtAsync(long timestamp) {
|
||||
return baseCache.expireAtAsync(timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> clearExpireAsync() {
|
||||
return baseCache.clearExpireAsync();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,397 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.redisson.client.codec.Codec;
|
||||
import org.redisson.client.protocol.RedisCommand;
|
||||
import org.redisson.client.protocol.RedisCommand.ValueType;
|
||||
import org.redisson.client.protocol.RedisCommands;
|
||||
import org.redisson.client.protocol.convertor.BooleanReplayConvertor;
|
||||
import org.redisson.client.protocol.convertor.IntegerReplayConvertor;
|
||||
import org.redisson.client.protocol.decoder.ListScanResult;
|
||||
import org.redisson.client.protocol.decoder.ListScanResultReplayDecoder;
|
||||
import org.redisson.client.protocol.decoder.NestedMultiDecoder;
|
||||
import org.redisson.client.protocol.decoder.ObjectListReplayDecoder;
|
||||
import org.redisson.client.protocol.decoder.ObjectSetReplayDecoder;
|
||||
import org.redisson.command.CommandAsyncExecutor;
|
||||
import org.redisson.core.RSet;
|
||||
|
||||
import io.netty.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* Set based Multimap Cache values holder
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
*
|
||||
* @param <V> value
|
||||
*/
|
||||
public class RedissonSetMultimapValues<V> extends RedissonExpirable implements RSet<V> {
|
||||
|
||||
private static final RedisCommand<ListScanResult<Object>> EVAL_SSCAN = new RedisCommand<ListScanResult<Object>>("EVAL", new NestedMultiDecoder(new ObjectListReplayDecoder<Object>(), new ListScanResultReplayDecoder()), 7, ValueType.MAP_KEY, ValueType.OBJECT);
|
||||
private static final RedisCommand<Integer> EVAL_SIZE = new RedisCommand<Integer>("EVAL", new IntegerReplayConvertor(), 6, ValueType.MAP_KEY);
|
||||
private static final RedisCommand<Set<Object>> EVAL_READALL = new RedisCommand<Set<Object>>("EVAL", new ObjectSetReplayDecoder(), 6, ValueType.MAP_KEY);
|
||||
private static final RedisCommand<Boolean> EVAL_CONTAINS_VALUE = new RedisCommand<Boolean>("EVAL", new BooleanReplayConvertor(), 6, Arrays.asList(ValueType.MAP_KEY, ValueType.MAP_VALUE));
|
||||
private static final RedisCommand<Boolean> EVAL_CONTAINS_ALL_WITH_VALUES = new RedisCommand<Boolean>("EVAL", new BooleanReplayConvertor(), 7, ValueType.OBJECTS);
|
||||
|
||||
private final Object key;
|
||||
private final String timeoutSetName;
|
||||
|
||||
public RedissonSetMultimapValues(Codec codec, CommandAsyncExecutor commandExecutor, String name, String timeoutSetName, Object key) {
|
||||
super(codec, commandExecutor, name);
|
||||
this.timeoutSetName = timeoutSetName;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return get(sizeAsync());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Integer> sizeAsync() {
|
||||
return commandExecutor.evalReadAsync(getName(), codec, EVAL_SIZE,
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[2]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore) "
|
||||
+ "end; "
|
||||
+ "if expireDate <= tonumber(ARGV[1]) then "
|
||||
+ "return 0;"
|
||||
+ "end; "
|
||||
+ "return redis.call('scard', KEYS[2]);",
|
||||
Arrays.<Object>asList(timeoutSetName, getName()), System.currentTimeMillis(), key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return get(containsAsync(o));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> containsAsync(Object o) {
|
||||
return commandExecutor.evalReadAsync(getName(), codec, EVAL_CONTAINS_VALUE,
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[2]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore) "
|
||||
+ "end; "
|
||||
+ "if expireDate <= tonumber(ARGV[1]) then "
|
||||
+ "return 0;"
|
||||
+ "end; "
|
||||
+ "return redis.call('sismember', KEYS[2], ARGV[3]);",
|
||||
Arrays.<Object>asList(timeoutSetName, getName()), System.currentTimeMillis(), key, o);
|
||||
}
|
||||
|
||||
private ListScanResult<V> scanIterator(InetSocketAddress client, long startPos) {
|
||||
Future<ListScanResult<V>> f = commandExecutor.evalReadAsync(client, getName(), codec, EVAL_SSCAN,
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[3]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore) "
|
||||
+ "end; "
|
||||
+ "if expireDate <= tonumber(ARGV[1]) then "
|
||||
+ "return {0, {}};"
|
||||
+ "end;"
|
||||
|
||||
+ "return redis.call('sscan', KEYS[2], ARGV[2]);",
|
||||
Arrays.<Object>asList(timeoutSetName, getName()), System.currentTimeMillis(), startPos, key);
|
||||
return get(f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<V> iterator() {
|
||||
return new RedissonBaseIterator<V>() {
|
||||
|
||||
@Override
|
||||
ListScanResult<V> iterator(InetSocketAddress client, long nextIterPos) {
|
||||
return scanIterator(client, nextIterPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
void remove(V value) {
|
||||
RedissonSetMultimapValues.this.remove(value);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Set<V>> readAllAsync() {
|
||||
return commandExecutor.evalReadAsync(getName(), codec, EVAL_READALL,
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[2]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore) "
|
||||
+ "end; "
|
||||
+ "if expireDate <= tonumber(ARGV[1]) then "
|
||||
+ "return {};"
|
||||
+ "end; "
|
||||
+ "return redis.call('smembers', KEYS[2]);",
|
||||
Arrays.<Object>asList(timeoutSetName, getName()), System.currentTimeMillis(), key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> readAll() {
|
||||
return get(readAllAsync());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
Set<Object> res = (Set<Object>) get(readAllAsync());
|
||||
return res.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T[] toArray(T[] a) {
|
||||
Set<Object> res = (Set<Object>) get(readAllAsync());
|
||||
return res.toArray(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(V e) {
|
||||
return get(addAsync(e));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> addAsync(V e) {
|
||||
return commandExecutor.writeAsync(getName(), codec, RedisCommands.SADD_SINGLE, getName(), e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V removeRandom() {
|
||||
return get(removeRandomAsync());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<V> removeRandomAsync() {
|
||||
return commandExecutor.writeAsync(getName(), codec, RedisCommands.SPOP_SINGLE, getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> removeAsync(Object o) {
|
||||
return commandExecutor.evalWriteAsync(getName(), codec, EVAL_CONTAINS_VALUE,
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[2]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore) "
|
||||
+ "end; "
|
||||
+ "if expireDate <= tonumber(ARGV[1]) then "
|
||||
+ "return 0;"
|
||||
+ "end; "
|
||||
+ "return redis.call('srem', KEYS[2], ARGV[3]) > 0 and 1 or 0;",
|
||||
Arrays.<Object>asList(timeoutSetName, getName()), System.currentTimeMillis(), key, o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object value) {
|
||||
return get(removeAsync((V)value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> moveAsync(String destination, V member) {
|
||||
return commandExecutor.writeAsync(getName(), codec, RedisCommands.SMOVE, getName(), destination, member);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean move(String destination, V member) {
|
||||
return get(moveAsync(destination, member));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> c) {
|
||||
return get(containsAllAsync(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> containsAllAsync(Collection<?> c) {
|
||||
List<Object> args = new ArrayList<Object>(c.size() + 2);
|
||||
try {
|
||||
byte[] keyState = codec.getMapKeyEncoder().encode(key);
|
||||
args.add(System.currentTimeMillis());
|
||||
args.add(keyState);
|
||||
args.addAll(c);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return commandExecutor.evalReadAsync(getName(), codec, EVAL_CONTAINS_ALL_WITH_VALUES,
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[2]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore) "
|
||||
+ "end; "
|
||||
+ "if expireDate <= tonumber(ARGV[1]) then "
|
||||
+ "return 0;"
|
||||
+ "end; " +
|
||||
"local s = redis.call('smembers', KEYS[2]);" +
|
||||
"for i = 1, #s, 1 do " +
|
||||
"for j = 2, #ARGV, 1 do "
|
||||
+ "if ARGV[j] == s[i] "
|
||||
+ "then table.remove(ARGV, j) end "
|
||||
+ "end; "
|
||||
+ "end;"
|
||||
+ "return #ARGV == 2 and 1 or 0; ",
|
||||
Arrays.<Object>asList(timeoutSetName, getName()), args.toArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends V> c) {
|
||||
if (c.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return get(addAllAsync(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> addAllAsync(Collection<? extends V> c) {
|
||||
List<Object> args = new ArrayList<Object>(c.size() + 1);
|
||||
args.add(getName());
|
||||
args.addAll(c);
|
||||
return commandExecutor.writeAsync(getName(), codec, RedisCommands.SADD_BOOL, args.toArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
return get(retainAllAsync(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> retainAllAsync(Collection<?> c) {
|
||||
List<Object> args = new ArrayList<Object>(c.size() + 2);
|
||||
try {
|
||||
byte[] keyState = codec.getMapKeyEncoder().encode(key);
|
||||
args.add(System.currentTimeMillis());
|
||||
args.add(keyState);
|
||||
args.addAll(c);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return commandExecutor.evalWriteAsync(getName(), codec, EVAL_CONTAINS_ALL_WITH_VALUES,
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[2]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore) "
|
||||
+ "end; "
|
||||
+ "if expireDate <= tonumber(ARGV[1]) then "
|
||||
+ "return 0;"
|
||||
+ "end; " +
|
||||
|
||||
"local changed = 0 " +
|
||||
"local s = redis.call('smembers', KEYS[2]) "
|
||||
+ "local i = 1 "
|
||||
+ "while i <= #s do "
|
||||
+ "local element = s[i] "
|
||||
+ "local isInAgrs = false "
|
||||
+ "for j = 2, #ARGV, 1 do "
|
||||
+ "if ARGV[j] == element then "
|
||||
+ "isInAgrs = true "
|
||||
+ "break "
|
||||
+ "end "
|
||||
+ "end "
|
||||
+ "if isInAgrs == false then "
|
||||
+ "redis.call('SREM', KEYS[2], element) "
|
||||
+ "changed = 1 "
|
||||
+ "end "
|
||||
+ "i = i + 1 "
|
||||
+ "end "
|
||||
+ "return changed ",
|
||||
Arrays.<Object>asList(timeoutSetName, getName()), args.toArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Boolean> removeAllAsync(Collection<?> c) {
|
||||
List<Object> args = new ArrayList<Object>(c.size() + 2);
|
||||
try {
|
||||
byte[] keyState = codec.getMapKeyEncoder().encode(key);
|
||||
args.add(System.currentTimeMillis());
|
||||
args.add(keyState);
|
||||
args.addAll(c);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return commandExecutor.evalWriteAsync(getName(), codec, EVAL_CONTAINS_ALL_WITH_VALUES,
|
||||
"local expireDate = 92233720368547758; " +
|
||||
"local expireDateScore = redis.call('zscore', KEYS[1], ARGV[2]); "
|
||||
+ "if expireDateScore ~= false then "
|
||||
+ "expireDate = tonumber(expireDateScore) "
|
||||
+ "end; "
|
||||
+ "if expireDate <= tonumber(ARGV[1]) then "
|
||||
+ "return 0;"
|
||||
+ "end; " +
|
||||
|
||||
"local v = 0 " +
|
||||
"for i = 2, #ARGV, 1 do "
|
||||
+ "if redis.call('srem', KEYS[2], ARGV[i]) == 1 "
|
||||
+ "then v = 1 end "
|
||||
+"end "
|
||||
+ "return v ",
|
||||
Arrays.<Object>asList(timeoutSetName, getName()), args.toArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
return get(removeAllAsync(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int union(String... names) {
|
||||
return get(unionAsync(names));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Integer> unionAsync(String... names) {
|
||||
List<Object> args = new ArrayList<Object>(names.length + 1);
|
||||
args.add(getName());
|
||||
args.addAll(Arrays.asList(names));
|
||||
return commandExecutor.writeAsync(getName(), codec, RedisCommands.SUNIONSTORE_INT, args.toArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> readUnion(String... names) {
|
||||
return get(readUnionAsync(names));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Set<V>> readUnionAsync(String... names) {
|
||||
List<Object> args = new ArrayList<Object>(names.length + 1);
|
||||
args.add(getName());
|
||||
args.addAll(Arrays.asList(names));
|
||||
return commandExecutor.writeAsync(getName(), codec, RedisCommands.SUNION, args.toArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
delete();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
public class RedissonShutdownException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = -2694051226420789395L;
|
||||
|
||||
public RedissonShutdownException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 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.codec;
|
||||
|
||||
import org.redisson.client.protocol.Decoder;
|
||||
|
||||
public class DelegateDecoderCodec extends StringCodec {
|
||||
|
||||
private final Codec delegate;
|
||||
|
||||
public DelegateDecoderCodec(Codec delegate) {
|
||||
super();
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Decoder<Object> getValueDecoder() {
|
||||
return delegate.getValueDecoder();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/**
|
||||
* 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.codec;
|
||||
|
||||
import org.redisson.client.protocol.Encoder;
|
||||
|
||||
public class GeoEntryCodec extends StringCodec {
|
||||
|
||||
private final ThreadLocal<Integer> pos = new ThreadLocal<Integer>() {
|
||||
protected Integer initialValue() {
|
||||
return 0;
|
||||
};
|
||||
};
|
||||
|
||||
private final Codec delegate;
|
||||
|
||||
public GeoEntryCodec(Codec delegate) {
|
||||
super();
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Encoder getValueEncoder() {
|
||||
Integer p = pos.get() + 1;
|
||||
pos.set(p);
|
||||
if (p % 3 == 0) {
|
||||
return delegate.getValueEncoder();
|
||||
}
|
||||
return super.getValueEncoder();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 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.io.IOException;
|
||||
|
||||
import org.redisson.client.handler.State;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class FlatNestedMultiDecoder<T> extends NestedMultiDecoder {
|
||||
|
||||
public FlatNestedMultiDecoder(MultiDecoder<Object> firstDecoder, MultiDecoder<Object> secondDecoder) {
|
||||
super(firstDecoder, secondDecoder);
|
||||
}
|
||||
|
||||
public FlatNestedMultiDecoder(MultiDecoder firstDecoder, MultiDecoder secondDecoder, boolean handleEmpty) {
|
||||
super(firstDecoder, secondDecoder, handleEmpty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object decode(ByteBuf buf, State state) throws IOException {
|
||||
return firstDecoder.decode(buf, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(int paramNum, State state) {
|
||||
DecoderState ds = getDecoder(state);
|
||||
if (paramNum == 0) {
|
||||
ds.resetDecoderIndex();
|
||||
}
|
||||
return firstDecoder.isApplicable(paramNum, state);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.redisson.client.codec.Codec;
|
||||
import org.redisson.client.codec.DoubleCodec;
|
||||
import org.redisson.client.handler.State;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
public class GeoDistanceDecoder implements MultiDecoder<List<Object>> {
|
||||
|
||||
private final ThreadLocal<Integer> pos = new ThreadLocal<Integer>();
|
||||
|
||||
private final Codec codec;
|
||||
|
||||
public GeoDistanceDecoder(Codec codec) {
|
||||
super();
|
||||
this.codec = codec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object decode(ByteBuf buf, State state) throws IOException {
|
||||
if (pos.get() % 2 == 0) {
|
||||
return codec.getValueDecoder().decode(buf, state);
|
||||
}
|
||||
return DoubleCodec.INSTANCE.getValueDecoder().decode(buf, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(int paramNum, State state) {
|
||||
pos.set(paramNum);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> decode(List<Object> parts, State state) {
|
||||
return parts;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/**
|
||||
* 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.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.redisson.client.codec.Codec;
|
||||
import org.redisson.client.codec.DoubleCodec;
|
||||
import org.redisson.client.handler.State;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class GeoDistanceMapDecoder implements MultiDecoder<Map<Object, Object>> {
|
||||
|
||||
private final ThreadLocal<Integer> pos = new ThreadLocal<Integer>();
|
||||
|
||||
private final Codec codec;
|
||||
|
||||
public GeoDistanceMapDecoder(Codec codec) {
|
||||
super();
|
||||
this.codec = codec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object decode(ByteBuf buf, State state) throws IOException {
|
||||
if (pos.get() % 2 == 0) {
|
||||
return codec.getValueDecoder().decode(buf, state);
|
||||
}
|
||||
return DoubleCodec.INSTANCE.getValueDecoder().decode(buf, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(int paramNum, State state) {
|
||||
pos.set(paramNum);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Object, Object> decode(List<Object> parts, State state) {
|
||||
Map<Object, Object> result = new HashMap<Object, Object>(parts.size()/2);
|
||||
for (int i = 0; i < parts.size(); i++) {
|
||||
if (i % 2 != 0) {
|
||||
result.put(parts.get(i-1), parts.get(i));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 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.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.redisson.client.handler.State;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class GeoMapReplayDecoder implements MultiDecoder<Map<Object, Object>> {
|
||||
|
||||
@Override
|
||||
public Object decode(ByteBuf buf, State state) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Object, Object> decode(List<Object> parts, State state) {
|
||||
Map<Object, Object> result = new HashMap<Object, Object>(parts.size());
|
||||
for (Object object : parts) {
|
||||
List<Object> vals = ((List<Object>) object);
|
||||
result.put(vals.get(0), vals.get(1));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(int paramNum, State state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* 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.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.redisson.client.codec.DoubleCodec;
|
||||
import org.redisson.client.handler.State;
|
||||
import org.redisson.core.GeoPosition;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class GeoPositionDecoder implements MultiDecoder<GeoPosition> {
|
||||
|
||||
@Override
|
||||
public Double decode(ByteBuf buf, State state) throws IOException {
|
||||
return (Double) DoubleCodec.INSTANCE.getValueDecoder().decode(buf, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(int paramNum, State state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoPosition decode(List<Object> parts, State state) {
|
||||
if (parts.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Double longitude = Double.valueOf(parts.get(0).toString());
|
||||
Double latitude = Double.valueOf(parts.get(1).toString());
|
||||
return new GeoPosition(longitude, latitude);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/**
|
||||
* 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.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.redisson.client.handler.State;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class GeoPositionMapDecoder implements MultiDecoder<Map<Object, Object>> {
|
||||
|
||||
private final List<Object> args;
|
||||
|
||||
public GeoPositionMapDecoder(List<Object> args) {
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double decode(ByteBuf buf, State state) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(int paramNum, State state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Object, Object> decode(List<Object> parts, State state) {
|
||||
if (parts.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<Object, Object> result = new HashMap<Object, Object>(parts.size());
|
||||
for (int index = 0; index < args.size()-1; index++) {
|
||||
Object value = parts.get(index);
|
||||
if (value == null || value == Collections.emptyMap()) {
|
||||
continue;
|
||||
}
|
||||
result.put(args.get(index+1), value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
/**
|
||||
* 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.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.redisson.client.handler.State;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class NestedMultiDecoder2<T> implements MultiDecoder<Object> {
|
||||
|
||||
private final MultiDecoder<Object> firstDecoder;
|
||||
private final MultiDecoder<Object> secondDecoder;
|
||||
|
||||
public NestedMultiDecoder2(MultiDecoder<Object> firstDecoder, MultiDecoder<Object> secondDecoder) {
|
||||
this.firstDecoder = firstDecoder;
|
||||
this.secondDecoder = secondDecoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object decode(ByteBuf buf, State state) throws IOException {
|
||||
return firstDecoder.decode(buf, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(int paramNum, State state) {
|
||||
if (paramNum == 0) {
|
||||
setCounter(state, 0);
|
||||
}
|
||||
return firstDecoder.isApplicable(paramNum, state);
|
||||
}
|
||||
|
||||
private Integer getCounter(State state) {
|
||||
Integer value = state.getDecoderState();
|
||||
if (value == null) {
|
||||
return 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private void setCounter(State state, Integer value) {
|
||||
state.setDecoderState(value);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object decode(List<Object> parts, State state) {
|
||||
// handle empty result
|
||||
if (parts.isEmpty() && state.getDecoderState() == null) {
|
||||
return secondDecoder.decode(parts, state);
|
||||
}
|
||||
|
||||
int counter = getCounter(state);
|
||||
if (counter == 2) {
|
||||
counter = 0;
|
||||
}
|
||||
counter++;
|
||||
setCounter(state, counter);
|
||||
MultiDecoder<?> decoder = null;
|
||||
if (counter == 1) {
|
||||
decoder = firstDecoder;
|
||||
}
|
||||
if (counter == 2) {
|
||||
decoder = secondDecoder;
|
||||
}
|
||||
return decoder.decode(parts, state);
|
||||
}
|
||||
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
/**
|
||||
* 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.connection;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.netty.util.concurrent.CompleteFuture;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
|
||||
/**
|
||||
* Invokes Future listeners in the same thread unlike {@code SucceededFuture} does.
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
*
|
||||
* @param <V>
|
||||
*/
|
||||
public abstract class FastCompleteFuture<V> extends CompleteFuture<V> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(FastCompleteFuture.class);
|
||||
|
||||
protected FastCompleteFuture() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<V> addListener(GenericFutureListener<? extends Future<? super V>> listener) {
|
||||
if (listener == null) {
|
||||
throw new NullPointerException("listener");
|
||||
}
|
||||
|
||||
notify(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void notify(GenericFutureListener<? extends Future<? super V>> listener) {
|
||||
try {
|
||||
((GenericFutureListener)listener).operationComplete(this);
|
||||
} catch (Throwable t) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("An exception was thrown by " + listener.getClass().getName() + ".operationComplete()", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<V> addListeners(GenericFutureListener<? extends Future<? super V>>... listeners) {
|
||||
if (listeners == null) {
|
||||
throw new NullPointerException("listeners");
|
||||
}
|
||||
for (GenericFutureListener<? extends Future<? super V>> l: listeners) {
|
||||
if (l == null) {
|
||||
break;
|
||||
}
|
||||
notify(l);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
/**
|
||||
* 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.connection;
|
||||
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
*
|
||||
* @param <V>
|
||||
*/
|
||||
public class FastFailedFuture<V> extends FastCompleteFuture<V> {
|
||||
|
||||
private final Throwable cause;
|
||||
|
||||
protected FastFailedFuture(Throwable cause) {
|
||||
if (cause == null) {
|
||||
throw new NullPointerException("cause");
|
||||
}
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Throwable cause() {
|
||||
return cause;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuccess() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<V> sync() {
|
||||
PlatformDependent.throwException(cause);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<V> syncUninterruptibly() {
|
||||
PlatformDependent.throwException(cause);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getNow() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
public class GeoEntry {
|
||||
|
||||
private final double longitude;
|
||||
private final double latitude;
|
||||
private final Object member;
|
||||
|
||||
public GeoEntry(double longitude, double latitude, Object member) {
|
||||
super();
|
||||
this.longitude = longitude;
|
||||
this.latitude = latitude;
|
||||
this.member = member;
|
||||
}
|
||||
|
||||
public double getLatitude() {
|
||||
return latitude;
|
||||
}
|
||||
|
||||
public double getLongitude() {
|
||||
return longitude;
|
||||
}
|
||||
|
||||
public Object getMember() {
|
||||
return member;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
public class GeoPosition {
|
||||
|
||||
private final double longitude;
|
||||
private final double latitude;
|
||||
|
||||
public GeoPosition(double longitude, double latitude) {
|
||||
super();
|
||||
this.longitude = longitude;
|
||||
this.latitude = latitude;
|
||||
}
|
||||
|
||||
public double getLatitude() {
|
||||
return latitude;
|
||||
}
|
||||
|
||||
public double getLongitude() {
|
||||
return longitude;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(latitude);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(longitude);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
GeoPosition other = (GeoPosition) obj;
|
||||
if (Double.doubleToLongBits(latitude) != Double.doubleToLongBits(other.latitude))
|
||||
return false;
|
||||
if (Double.doubleToLongBits(longitude) != Double.doubleToLongBits(other.longitude))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GeoPosition [longitude=" + longitude + ", latitude=" + latitude + "]";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
public enum GeoUnit {
|
||||
|
||||
METERS {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "m";
|
||||
}
|
||||
},
|
||||
|
||||
KILOMETERS {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "km";
|
||||
}
|
||||
},
|
||||
|
||||
MILES {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "mi";
|
||||
}
|
||||
},
|
||||
|
||||
FEET {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ft";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* 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.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface RBuckets {
|
||||
|
||||
/**
|
||||
* <p>Returns a list of object holder instances by a key pattern.
|
||||
*
|
||||
* <pre>Supported glob-style patterns:
|
||||
* h?llo subscribes to hello, hallo and hxllo
|
||||
* h*llo subscribes to hllo and heeeello
|
||||
* h[ae]llo subscribes to hello and hallo, but not hillo
|
||||
* h[^e]llo matches hallo, hbllo, ... but not hello
|
||||
* h[a-b]llo matches hallo and hbllo</pre>
|
||||
* <p>Use \ to escape special characters if you want to match them verbatim.
|
||||
*
|
||||
* @param pattern
|
||||
* @return
|
||||
*/
|
||||
<V> List<RBucket<V>> find(String pattern);
|
||||
|
||||
/**
|
||||
* Returns Redis object mapped by key. Result Map is not contains
|
||||
* key-value entry for null values.
|
||||
*
|
||||
* @param keys
|
||||
* @return
|
||||
*/
|
||||
<V> Map<String, V> get(String ... keys);
|
||||
|
||||
/**
|
||||
* Try to save objects mapped by Redis key.
|
||||
* If at least one of them is already exist then
|
||||
* don't set none of them.
|
||||
*
|
||||
* @param buckets
|
||||
*/
|
||||
boolean trySet(Map<String, ?> buckets);
|
||||
|
||||
/**
|
||||
* Saves objects mapped by Redis key.
|
||||
*
|
||||
* @param buckets
|
||||
*/
|
||||
void set(Map<String, ?> buckets);
|
||||
|
||||
}
|
@ -0,0 +1,166 @@
|
||||
/**
|
||||
* 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.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Geospatial items holder
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
*
|
||||
* @param <V>
|
||||
*/
|
||||
public interface RGeo<V> extends RExpirable, RGeoAsync<V> {
|
||||
|
||||
/**
|
||||
* Adds geospatial member.
|
||||
*
|
||||
* @param entries
|
||||
* @return number of elements added to the sorted set,
|
||||
* not including elements already existing for which
|
||||
* the score was updated
|
||||
*/
|
||||
long add(double longitude, double latitude, V member);
|
||||
|
||||
/**
|
||||
* Adds geospatial members.
|
||||
*
|
||||
* @param entries
|
||||
* @return number of elements added to the sorted set,
|
||||
* not including elements already existing for which
|
||||
* the score was updated
|
||||
*/
|
||||
long add(GeoEntry... entries);
|
||||
|
||||
/**
|
||||
* Returns distance between members in <code>GeoUnit</code> units.
|
||||
*
|
||||
* @see {@link GeoUnit}
|
||||
*
|
||||
* @param firstMember
|
||||
* @param secondMember
|
||||
* @param geoUnit
|
||||
* @return
|
||||
*/
|
||||
Double dist(V firstMember, V secondMember, GeoUnit geoUnit);
|
||||
|
||||
/**
|
||||
* Returns 11 characters Geohash string mapped by defined member.
|
||||
*
|
||||
* @param members
|
||||
* @return
|
||||
*/
|
||||
Map<V, String> hash(V... members);
|
||||
|
||||
/**
|
||||
* Returns geo-position mapped by defined member.
|
||||
*
|
||||
* @param members
|
||||
* @return
|
||||
*/
|
||||
Map<V, GeoPosition> pos(V... members);
|
||||
|
||||
/**
|
||||
* Returns the members of a sorted set, which are within the
|
||||
* borders of the area specified with the center location
|
||||
* and the maximum distance from the center (the radius)
|
||||
* in <code>GeoUnit</code> units.
|
||||
*
|
||||
* @param longitude
|
||||
* @param latitude
|
||||
* @param radius
|
||||
* @param geoUnit
|
||||
* @return
|
||||
*/
|
||||
List<V> radius(double longitude, double latitude, double radius, GeoUnit geoUnit);
|
||||
|
||||
/**
|
||||
* Returns the distance mapped by member, distance between member and the location.
|
||||
* Members of a sorted set, which are within the
|
||||
* borders of the area specified with the center location
|
||||
* and the maximum distance from the center (the radius)
|
||||
* in <code>GeoUnit</code> units.
|
||||
*
|
||||
* @param longitude
|
||||
* @param latitude
|
||||
* @param radius
|
||||
* @param geoUnit
|
||||
* @return
|
||||
*/
|
||||
Map<V, Double> radiusWithDistance(double longitude, double latitude, double radius, GeoUnit geoUnit);
|
||||
|
||||
/**
|
||||
* Returns the geo-position mapped by member.
|
||||
* Members of a sorted set, which are within the
|
||||
* borders of the area specified with the center location
|
||||
* and the maximum distance from the center (the radius)
|
||||
* in <code>GeoUnit</code> units.
|
||||
*
|
||||
* @param longitude
|
||||
* @param latitude
|
||||
* @param radius
|
||||
* @param geoUnit
|
||||
* @return
|
||||
*/
|
||||
Map<V, GeoPosition> radiusWithPosition(double longitude, double latitude, double radius, GeoUnit geoUnit);
|
||||
|
||||
/**
|
||||
* Returns the members of a sorted set, which are within the
|
||||
* borders of the area specified with the defined member location
|
||||
* and the maximum distance from the defined member location (the radius)
|
||||
* in <code>GeoUnit</code> units.
|
||||
*
|
||||
* @param longitude
|
||||
* @param latitude
|
||||
* @param radius
|
||||
* @param geoUnit
|
||||
* @return
|
||||
*/
|
||||
List<V> radius(V member, double radius, GeoUnit geoUnit);
|
||||
|
||||
/**
|
||||
* Returns the distance mapped by member, distance between member and the defined member location.
|
||||
* Members of a sorted set, which are within the
|
||||
* borders of the area specified with the defined member location
|
||||
* and the maximum distance from the defined member location (the radius)
|
||||
* in <code>GeoUnit</code> units.
|
||||
*
|
||||
* @param longitude
|
||||
* @param latitude
|
||||
* @param radius
|
||||
* @param geoUnit
|
||||
* @return
|
||||
*/
|
||||
Map<V, Double> radiusWithDistance(V member, double radius, GeoUnit geoUnit);
|
||||
|
||||
/**
|
||||
* Returns the geo-position mapped by member.
|
||||
* Members of a sorted set, which are within the
|
||||
* borders of the area specified with the defined member location
|
||||
* and the maximum distance from the defined member location (the radius)
|
||||
* in <code>GeoUnit</code> units.
|
||||
*
|
||||
* @param longitude
|
||||
* @param latitude
|
||||
* @param radius
|
||||
* @param geoUnit
|
||||
* @return
|
||||
*/
|
||||
Map<V, GeoPosition> radiusWithPosition(V member, double radius, GeoUnit geoUnit);
|
||||
|
||||
}
|
@ -0,0 +1,167 @@
|
||||
/**
|
||||
* 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.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.netty.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
*
|
||||
* @param <V>
|
||||
*/
|
||||
public interface RGeoAsync<V> extends RExpirableAsync {
|
||||
|
||||
/**
|
||||
* Adds geospatial member.
|
||||
*
|
||||
* @param entries
|
||||
* @return number of elements added to the sorted set,
|
||||
* not including elements already existing for which
|
||||
* the score was updated
|
||||
*/
|
||||
Future<Long> addAsync(double longitude, double latitude, V member);
|
||||
|
||||
/**
|
||||
* Adds geospatial members.
|
||||
*
|
||||
* @param entries
|
||||
* @return number of elements added to the sorted set,
|
||||
* not including elements already existing for which
|
||||
* the score was updated
|
||||
*/
|
||||
Future<Long> addAsync(GeoEntry... entries);
|
||||
|
||||
/**
|
||||
* Returns distance between members in <code>GeoUnit</code> units.
|
||||
*
|
||||
* @see {@link GeoUnit}
|
||||
*
|
||||
* @param firstMember
|
||||
* @param secondMember
|
||||
* @param geoUnit
|
||||
* @return
|
||||
*/
|
||||
Future<Double> distAsync(V firstMember, V secondMember, GeoUnit geoUnit);
|
||||
|
||||
/**
|
||||
* Returns 11 characters Geohash string mapped by defined member.
|
||||
*
|
||||
* @param members
|
||||
* @return
|
||||
*/
|
||||
Future<Map<V, String>> hashAsync(V... members);
|
||||
|
||||
/**
|
||||
* Returns geo-position mapped by defined member.
|
||||
*
|
||||
* @param members
|
||||
* @return
|
||||
*/
|
||||
Future<Map<V, GeoPosition>> posAsync(V... members);
|
||||
|
||||
/**
|
||||
* Returns the members of a sorted set, which are within the
|
||||
* borders of the area specified with the center location
|
||||
* and the maximum distance from the center (the radius)
|
||||
* in <code>GeoUnit</code> units.
|
||||
*
|
||||
* @param longitude
|
||||
* @param latitude
|
||||
* @param radius
|
||||
* @param geoUnit
|
||||
* @return
|
||||
*/
|
||||
Future<List<V>> radiusAsync(double longitude, double latitude, double radius, GeoUnit geoUnit);
|
||||
|
||||
/**
|
||||
* Returns the distance mapped by member, distance between member and the location.
|
||||
* Members of a sorted set, which are within the
|
||||
* borders of the area specified with the center location
|
||||
* and the maximum distance from the center (the radius)
|
||||
* in <code>GeoUnit</code> units.
|
||||
*
|
||||
* @param longitude
|
||||
* @param latitude
|
||||
* @param radius
|
||||
* @param geoUnit
|
||||
* @return
|
||||
*/
|
||||
Future<Map<V, Double>> radiusWithDistanceAsync(double longitude, double latitude, double radius, GeoUnit geoUnit);
|
||||
|
||||
/**
|
||||
* Returns the geo-position mapped by member.
|
||||
* Members of a sorted set, which are within the
|
||||
* borders of the area specified with the center location
|
||||
* and the maximum distance from the center (the radius)
|
||||
* in <code>GeoUnit</code> units.
|
||||
*
|
||||
* @param longitude
|
||||
* @param latitude
|
||||
* @param radius
|
||||
* @param geoUnit
|
||||
* @return
|
||||
*/
|
||||
Future<Map<V, GeoPosition>> radiusWithPositionAsync(double longitude, double latitude, double radius, GeoUnit geoUnit);
|
||||
|
||||
/**
|
||||
* Returns the members of a sorted set, which are within the
|
||||
* borders of the area specified with the defined member location
|
||||
* and the maximum distance from the defined member location (the radius)
|
||||
* in <code>GeoUnit</code> units.
|
||||
*
|
||||
* @param longitude
|
||||
* @param latitude
|
||||
* @param radius
|
||||
* @param geoUnit
|
||||
* @return
|
||||
*/
|
||||
Future<List<V>> radiusAsync(V member, double radius, GeoUnit geoUnit);
|
||||
|
||||
/**
|
||||
* Returns the distance mapped by member, distance between member and the defined member location.
|
||||
* Members of a sorted set, which are within the
|
||||
* borders of the area specified with the defined member location
|
||||
* and the maximum distance from the defined member location (the radius)
|
||||
* in <code>GeoUnit</code> units.
|
||||
*
|
||||
* @param longitude
|
||||
* @param latitude
|
||||
* @param radius
|
||||
* @param geoUnit
|
||||
* @return
|
||||
*/
|
||||
Future<Map<V, Double>> radiusWithDistanceAsync(V member, double radius, GeoUnit geoUnit);
|
||||
|
||||
/**
|
||||
* Returns the geo-position mapped by member.
|
||||
* Members of a sorted set, which are within the
|
||||
* borders of the area specified with the defined member location
|
||||
* and the maximum distance from the defined member location (the radius)
|
||||
* in <code>GeoUnit</code> units.
|
||||
*
|
||||
* @param longitude
|
||||
* @param latitude
|
||||
* @param radius
|
||||
* @param geoUnit
|
||||
* @return
|
||||
*/
|
||||
Future<Map<V, GeoPosition>> radiusWithPositionAsync(V member, double radius, GeoUnit geoUnit);
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
public interface RListMultimapCache<K, V> extends RListMultimap<K, V>, RMultimapCache<K, V> {
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 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.concurrent.TimeUnit;
|
||||
|
||||
public interface RMultimapCache<K, V> extends RMultimap<K, V>, RMultimapCacheAsync<K, V> {
|
||||
|
||||
/**
|
||||
* Set a timeout for key. After the timeout has expired,
|
||||
* the key and its values will automatically be deleted.
|
||||
*
|
||||
* @param key
|
||||
* @param timeToLive - timeout before key will be deleted
|
||||
* @param timeUnit - timeout time unit
|
||||
* @return <code>true</code> if key exists and the timeout was set and <code>false</code> if key not exists
|
||||
*/
|
||||
boolean expireKey(K key, long timeToLive, TimeUnit timeUnit);
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 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.concurrent.TimeUnit;
|
||||
|
||||
import io.netty.util.concurrent.Future;
|
||||
|
||||
public interface RMultimapCacheAsync<K, V> extends RMultimapAsync<K, V> {
|
||||
|
||||
/**
|
||||
* Set a timeout for key in async mode. After the timeout has expired,
|
||||
* the key and its values will automatically be deleted.
|
||||
*
|
||||
* @param key
|
||||
* @param timeToLive - timeout before key will be deleted
|
||||
* @param timeUnit - timeout time unit
|
||||
* @return <code>true</code> if key exists and the timeout was set and <code>false</code> if key not exists
|
||||
*/
|
||||
Future<Boolean> expireKeyAsync(K key, long timeToLive, TimeUnit timeUnit);
|
||||
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
/**
|
||||
* 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.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Allows to execute object methods remotely between Redisson instances (Server side and Client side instances in terms of remote invocation).
|
||||
* <p/>
|
||||
* <b>1. Server side instance (worker instance).</b> Register object with RRemoteService instance.
|
||||
* <p/>
|
||||
* <code>
|
||||
* RRemoteService remoteService = redisson.getRemoteService();<br/>
|
||||
* <br/>
|
||||
* // register remote service before any remote invocation<br/>
|
||||
* remoteService.register(SomeServiceInterface.class, someServiceImpl);
|
||||
* </code>
|
||||
* <p/>
|
||||
* <b>2. Client side instance.</b> Invokes method remotely.
|
||||
* <p/>
|
||||
* <code>
|
||||
* RRemoteService remoteService = redisson.getRemoteService();<br/>
|
||||
* SomeServiceInterface service = remoteService.get(SomeServiceInterface.class);<br/>
|
||||
* <br/>
|
||||
* String result = service.doSomeStuff(1L, "secondParam", new AnyParam());
|
||||
* </code>
|
||||
* <p/>
|
||||
* <p/>
|
||||
* There are two timeouts during execution:
|
||||
* <p/>
|
||||
* <b>Acknowledge (Ack) timeout.</b>Client side instance waits for acknowledge message from Server side instance.
|
||||
* <p/>
|
||||
* If acknowledge has not been received by Client side instance then <code>RemoteServiceAckTimeoutException</code> will be thrown.
|
||||
* And next invocation attempt can be made.
|
||||
* <p/>
|
||||
* If acknowledge has not been received Client side instance but Server side instance has received invocation message already.
|
||||
* In this case invocation will be skipped, due to ack timeout checking by Server side instance.
|
||||
* <p/>
|
||||
* <b>Execution timeout.</b> Client side instance received acknowledge message. If it hasn't received any result or error
|
||||
* from server side during execution timeout then <code>RemoteServiceTimeoutException</code> will be thrown.
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
*
|
||||
*/
|
||||
public interface RRemoteService {
|
||||
|
||||
/**
|
||||
* Register remote service with single executor
|
||||
*
|
||||
* @param remoteInterface
|
||||
* @param object
|
||||
*/
|
||||
<T> void register(Class<T> remoteInterface, T object);
|
||||
|
||||
/**
|
||||
* Register remote service with custom executors amount
|
||||
*
|
||||
* @param remoteInterface
|
||||
* @param object
|
||||
* @param executorsAmount
|
||||
*/
|
||||
<T> void register(Class<T> remoteInterface, T object, int executorsAmount);
|
||||
|
||||
/**
|
||||
* Get remote service object for remote invocations.
|
||||
* <p/>
|
||||
* Ack timeout = 1000 ms by default
|
||||
* <p/>
|
||||
* Execution timeout = 30 sec by default
|
||||
*
|
||||
* @param remoteInterface
|
||||
* @return
|
||||
*/
|
||||
<T> T get(Class<T> remoteInterface);
|
||||
|
||||
/**
|
||||
* Get remote service object for remote invocations
|
||||
* with specified invocation timeout.
|
||||
* <p/>
|
||||
* Ack timeout = 1000 ms by default
|
||||
*
|
||||
* @param remoteInterface
|
||||
* @param executionTimeout - invocation timeout
|
||||
* @param executionTimeUnit
|
||||
* @return
|
||||
*/
|
||||
<T> T get(Class<T> remoteInterface, long executionTimeout, TimeUnit executionTimeUnit);
|
||||
|
||||
/**
|
||||
* Get remote service object for remote invocations
|
||||
* with specified invocation and ack timeouts
|
||||
*
|
||||
* @param remoteInterface
|
||||
* @param executionTimeout - invocation timeout
|
||||
* @param executionTimeUnit
|
||||
* @param ackTimeout - ack timeout
|
||||
* @param ackTimeUnit
|
||||
* @return
|
||||
*/
|
||||
<T> T get(Class<T> remoteInterface, long executionTimeout, TimeUnit executionTimeUnit, long ackTimeout, TimeUnit ackTimeUnit);
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
public interface RSetMultimapCache<K, V> extends RSetMultimap<K, V>, RMultimapCache<K, V> {
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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.remote;
|
||||
|
||||
public interface RRemoteServiceResponse {
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 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.remote;
|
||||
|
||||
/**
|
||||
* Worker sends this message when it has received a {@link RemoteServiceRequest}.
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
*
|
||||
*/
|
||||
public class RemoteServiceAck implements RRemoteServiceResponse {
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 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.remote;
|
||||
|
||||
/**
|
||||
* Rises when remote method executor has not answered
|
||||
* within Ack timeout.
|
||||
* <p/>
|
||||
* Method invocation has not been started in this case.
|
||||
* So a new invocation attempt can be made.
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
*
|
||||
*/
|
||||
public class RemoteServiceAckTimeoutException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1820133675653636587L;
|
||||
|
||||
public RemoteServiceAckTimeoutException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/**
|
||||
* 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.remote;
|
||||
|
||||
public class RemoteServiceKey {
|
||||
|
||||
private final Class<?> serviceInterface;
|
||||
private final String methodName;
|
||||
|
||||
public RemoteServiceKey(Class<?> serviceInterface, String methodName) {
|
||||
super();
|
||||
this.serviceInterface = serviceInterface;
|
||||
this.methodName = methodName;
|
||||
}
|
||||
|
||||
public String getMethodName() {
|
||||
return methodName;
|
||||
}
|
||||
|
||||
public Class<?> getServiceInterface() {
|
||||
return serviceInterface;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((methodName == null) ? 0 : methodName.hashCode());
|
||||
result = prime * result + ((serviceInterface == null) ? 0 : serviceInterface.getName().hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
RemoteServiceKey other = (RemoteServiceKey) obj;
|
||||
if (methodName == null) {
|
||||
if (other.methodName != null)
|
||||
return false;
|
||||
} else if (!methodName.equals(other.methodName))
|
||||
return false;
|
||||
if (serviceInterface == null) {
|
||||
if (other.serviceInterface != null)
|
||||
return false;
|
||||
} else if (!serviceInterface.equals(other.serviceInterface))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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.remote;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class RemoteServiceMethod {
|
||||
|
||||
private final Object bean;
|
||||
private final Method method;
|
||||
|
||||
public RemoteServiceMethod(Method method, Object bean) {
|
||||
super();
|
||||
this.method = method;
|
||||
this.bean = bean;
|
||||
}
|
||||
|
||||
public Object getBean() {
|
||||
return bean;
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* 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.remote;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class RemoteServiceRequest {
|
||||
|
||||
private String requestId;
|
||||
private String methodName;
|
||||
private Object[] args;
|
||||
private long ackTimeout;
|
||||
private long responseTimeout;
|
||||
private long date;
|
||||
|
||||
|
||||
public RemoteServiceRequest() {
|
||||
}
|
||||
|
||||
public RemoteServiceRequest(String requestId, String methodName, Object[] args, long ackTimeout, long responseTimeout, long date) {
|
||||
super();
|
||||
this.requestId = requestId;
|
||||
this.methodName = methodName;
|
||||
this.args = args;
|
||||
this.ackTimeout = ackTimeout;
|
||||
this.responseTimeout = responseTimeout;
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public long getResponseTimeout() {
|
||||
return responseTimeout;
|
||||
}
|
||||
|
||||
public long getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public long getAckTimeout() {
|
||||
return ackTimeout;
|
||||
}
|
||||
|
||||
public String getRequestId() {
|
||||
return requestId;
|
||||
}
|
||||
|
||||
public Object[] getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
public String getMethodName() {
|
||||
return methodName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RemoteServiceRequest [requestId=" + requestId + ", methodName=" + methodName + ", args="
|
||||
+ Arrays.toString(args) + ", ackTimeout=" + ackTimeout + ", date=" + date + "]";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* 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.remote;
|
||||
|
||||
public class RemoteServiceResponse implements RRemoteServiceResponse {
|
||||
|
||||
private Object result;
|
||||
private Throwable error;
|
||||
|
||||
public RemoteServiceResponse() {
|
||||
}
|
||||
|
||||
public RemoteServiceResponse(Object result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public RemoteServiceResponse(Throwable error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public Throwable getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public Object getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RemoteServiceResponse [result=" + result + ", error=" + error + "]";
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue