You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
583 lines
19 KiB
583 lines
19 KiB
/** |
|
* Copyright 2018 Nikita Koksharov |
|
* |
|
* 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 com.fr.third.org.redisson; |
|
|
|
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.Set; |
|
|
|
import com.fr.third.org.redisson.api.RFuture; |
|
import com.fr.third.org.redisson.api.RLock; |
|
import com.fr.third.org.redisson.api.RSet; |
|
import com.fr.third.org.redisson.api.RedissonClient; |
|
import com.fr.third.org.redisson.api.SortOrder; |
|
import com.fr.third.org.redisson.api.mapreduce.RCollectionMapReduce; |
|
import com.fr.third.org.redisson.client.RedisClient; |
|
import com.fr.third.org.redisson.client.codec.Codec; |
|
import com.fr.third.org.redisson.client.codec.ScanCodec; |
|
import com.fr.third.org.redisson.client.protocol.RedisCommands; |
|
import com.fr.third.org.redisson.client.protocol.decoder.ListScanResult; |
|
import com.fr.third.org.redisson.client.protocol.decoder.ScanObjectEntry; |
|
import com.fr.third.org.redisson.misc.Hash; |
|
import com.fr.third.org.redisson.misc.RedissonPromise; |
|
import com.fr.third.org.redisson.api.RFuture; |
|
import com.fr.third.org.redisson.api.RLock; |
|
import com.fr.third.org.redisson.api.RSet; |
|
import com.fr.third.org.redisson.api.RedissonClient; |
|
import com.fr.third.org.redisson.api.SortOrder; |
|
import com.fr.third.org.redisson.api.mapreduce.RCollectionMapReduce; |
|
import com.fr.third.org.redisson.client.RedisClient; |
|
import com.fr.third.org.redisson.client.codec.Codec; |
|
import com.fr.third.org.redisson.client.codec.ScanCodec; |
|
import com.fr.third.org.redisson.client.protocol.RedisCommands; |
|
import com.fr.third.org.redisson.client.protocol.decoder.ListScanResult; |
|
import com.fr.third.org.redisson.client.protocol.decoder.ScanObjectEntry; |
|
import com.fr.third.org.redisson.command.CommandAsyncExecutor; |
|
import com.fr.third.org.redisson.mapreduce.RedissonCollectionMapReduce; |
|
import com.fr.third.org.redisson.misc.Hash; |
|
import com.fr.third.org.redisson.misc.RedissonPromise; |
|
|
|
import io.netty.buffer.ByteBuf; |
|
|
|
/** |
|
* Distributed and concurrent implementation of {@link java.util.Set} |
|
* |
|
* @author Nikita Koksharov |
|
* |
|
* @param <V> value |
|
*/ |
|
public class RedissonSet<V> extends RedissonExpirable implements RSet<V>, ScanIterator { |
|
|
|
RedissonClient redisson; |
|
|
|
protected RedissonSet(CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) { |
|
super(commandExecutor, name); |
|
this.redisson = redisson; |
|
} |
|
|
|
public RedissonSet(Codec codec, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) { |
|
super(codec, commandExecutor, name); |
|
this.redisson = redisson; |
|
} |
|
|
|
@Override |
|
public <KOut, VOut> RCollectionMapReduce<V, KOut, VOut> mapReduce() { |
|
return new RedissonCollectionMapReduce<V, KOut, VOut>(this, redisson, commandExecutor.getConnectionManager()); |
|
} |
|
|
|
@Override |
|
public int size() { |
|
return get(sizeAsync()); |
|
} |
|
|
|
@Override |
|
public RFuture<Integer> sizeAsync() { |
|
return commandExecutor.readAsync(getName(), codec, RedisCommands.SCARD_INT, getName()); |
|
} |
|
|
|
@Override |
|
public boolean isEmpty() { |
|
return size() == 0; |
|
} |
|
|
|
@Override |
|
public boolean contains(Object o) { |
|
return get(containsAsync(o)); |
|
} |
|
|
|
@Override |
|
public RFuture<Boolean> containsAsync(Object o) { |
|
return commandExecutor.readAsync(getName(o), codec, RedisCommands.SISMEMBER, getName(o), encode(o)); |
|
} |
|
|
|
@Override |
|
public ListScanResult<ScanObjectEntry> scanIterator(String name, RedisClient client, long startPos, String pattern) { |
|
if (pattern == null) { |
|
RFuture<ListScanResult<ScanObjectEntry>> f = commandExecutor.readAsync(client, name, new ScanCodec(codec), RedisCommands.SSCAN, name, startPos); |
|
return get(f); |
|
} |
|
|
|
RFuture<ListScanResult<ScanObjectEntry>> f = commandExecutor.readAsync(client, name, new ScanCodec(codec), RedisCommands.SSCAN, name, startPos, "MATCH", pattern); |
|
return get(f); |
|
} |
|
|
|
@Override |
|
public Iterator<V> iterator(final String pattern) { |
|
return new RedissonBaseIterator<V>() { |
|
|
|
@Override |
|
ListScanResult<ScanObjectEntry> iterator(RedisClient client, long nextIterPos) { |
|
return scanIterator(getName(), client, nextIterPos, pattern); |
|
} |
|
|
|
@Override |
|
void remove(V value) { |
|
RedissonSet.this.remove(value); |
|
} |
|
|
|
}; |
|
} |
|
|
|
@Override |
|
public Iterator<V> iterator() { |
|
return iterator(null); |
|
} |
|
|
|
@Override |
|
public RFuture<Set<V>> readAllAsync() { |
|
return commandExecutor.readAsync(getName(), codec, RedisCommands.SMEMBERS, getName()); |
|
} |
|
|
|
@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 RFuture<Boolean> addAsync(V e) { |
|
return commandExecutor.writeAsync(getName(e), codec, RedisCommands.SADD_SINGLE, getName(e), encode(e)); |
|
} |
|
|
|
@Override |
|
public V removeRandom() { |
|
return get(removeRandomAsync()); |
|
} |
|
|
|
@Override |
|
public RFuture<V> removeRandomAsync() { |
|
return commandExecutor.writeAsync(getName(), codec, RedisCommands.SPOP_SINGLE, getName()); |
|
} |
|
|
|
@Override |
|
public Set<V> removeRandom(int amount) { |
|
return get(removeRandomAsync(amount)); |
|
} |
|
|
|
@Override |
|
public RFuture<Set<V>> removeRandomAsync(int amount) { |
|
return commandExecutor.writeAsync(getName(), codec, RedisCommands.SPOP, getName(), amount); |
|
} |
|
|
|
@Override |
|
public V random() { |
|
return get(randomAsync()); |
|
} |
|
|
|
@Override |
|
public RFuture<V> randomAsync() { |
|
return commandExecutor.writeAsync(getName(), codec, RedisCommands.SRANDMEMBER_SINGLE, getName()); |
|
} |
|
|
|
@Override |
|
public RFuture<Boolean> removeAsync(Object o) { |
|
return commandExecutor.writeAsync(getName(o), codec, RedisCommands.SREM_SINGLE, getName(o), encode(o)); |
|
} |
|
|
|
@Override |
|
public boolean remove(Object value) { |
|
return get(removeAsync((V)value)); |
|
} |
|
|
|
@Override |
|
public RFuture<Boolean> moveAsync(String destination, V member) { |
|
return commandExecutor.writeAsync(getName(member), codec, RedisCommands.SMOVE, getName(member), destination, encode(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 RFuture<Boolean> containsAllAsync(Collection<?> c) { |
|
if (c.isEmpty()) { |
|
return RedissonPromise.newSucceededFuture(true); |
|
} |
|
|
|
String tempName = suffixName(getName(), "redisson_temp"); |
|
|
|
return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN, |
|
"redis.call('sadd', KEYS[2], unpack(ARGV)); " |
|
+ "local size = redis.call('sdiff', KEYS[2], KEYS[1]);" |
|
+ "redis.call('del', KEYS[2]); " |
|
+ "return #size == 0 and 1 or 0; ", |
|
Arrays.<Object>asList(getName(), tempName), encode(c).toArray()); |
|
} |
|
|
|
@Override |
|
public boolean addAll(Collection<? extends V> c) { |
|
return get(addAllAsync(c)); |
|
} |
|
|
|
@Override |
|
public RFuture<Boolean> addAllAsync(Collection<? extends V> c) { |
|
if (c.isEmpty()) { |
|
return RedissonPromise.newSucceededFuture(false); |
|
} |
|
|
|
List<Object> args = new ArrayList<Object>(c.size() + 1); |
|
args.add(getName()); |
|
encode(args, c); |
|
return commandExecutor.writeAsync(getName(), codec, RedisCommands.SADD_BOOL, args.toArray()); |
|
} |
|
|
|
@Override |
|
public boolean retainAll(Collection<?> c) { |
|
return get(retainAllAsync(c)); |
|
} |
|
|
|
@Override |
|
public RFuture<Boolean> retainAllAsync(Collection<?> c) { |
|
if (c.isEmpty()) { |
|
return deleteAsync(); |
|
} |
|
|
|
String tempName = suffixName(getName(), "redisson_temp"); |
|
|
|
return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN, |
|
"redis.call('sadd', KEYS[2], unpack(ARGV)); " |
|
+ "local prevSize = redis.call('scard', KEYS[1]); " |
|
+ "local size = redis.call('sinterstore', KEYS[1], KEYS[1], KEYS[2]);" |
|
+ "redis.call('del', KEYS[2]); " |
|
+ "return size ~= prevSize and 1 or 0; ", |
|
Arrays.<Object>asList(getName(), tempName), |
|
encode(c).toArray()); |
|
} |
|
|
|
@Override |
|
public RFuture<Boolean> removeAllAsync(Collection<?> c) { |
|
if (c.isEmpty()) { |
|
return RedissonPromise.newSucceededFuture(false); |
|
} |
|
|
|
List<Object> args = new ArrayList<Object>(c.size() + 1); |
|
args.add(getName()); |
|
encode(args, c); |
|
|
|
return commandExecutor.writeAsync(getName(), codec, RedisCommands.SREM_SINGLE, args.toArray()); |
|
} |
|
|
|
@Override |
|
public boolean removeAll(Collection<?> c) { |
|
return get(removeAllAsync(c)); |
|
} |
|
|
|
@Override |
|
public int union(String... names) { |
|
return get(unionAsync(names)); |
|
} |
|
|
|
@Override |
|
public RFuture<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 RFuture<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 int diff(String... names) { |
|
return get(diffAsync(names)); |
|
} |
|
|
|
@Override |
|
public RFuture<Integer> diffAsync(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.SDIFFSTORE_INT, args.toArray()); |
|
} |
|
|
|
@Override |
|
public Set<V> readDiff(String... names) { |
|
return get(readDiffAsync(names)); |
|
} |
|
|
|
@Override |
|
public RFuture<Set<V>> readDiffAsync(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.SDIFF, args.toArray()); |
|
} |
|
|
|
@Override |
|
public int intersection(String... names) { |
|
return get(intersectionAsync(names)); |
|
} |
|
|
|
@Override |
|
public RFuture<Integer> intersectionAsync(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.SINTERSTORE_INT, args.toArray()); |
|
} |
|
|
|
@Override |
|
public Set<V> readIntersection(String... names) { |
|
return get(readIntersectionAsync(names)); |
|
} |
|
|
|
@Override |
|
public RFuture<Set<V>> readIntersectionAsync(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.SINTER, args.toArray()); |
|
} |
|
|
|
@Override |
|
public void clear() { |
|
delete(); |
|
} |
|
|
|
@Override |
|
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 Set<V> readSort(SortOrder order) { |
|
return get(readSortAsync(order)); |
|
} |
|
|
|
@Override |
|
public RFuture<Set<V>> readSortAsync(SortOrder order) { |
|
return commandExecutor.readAsync(getName(), codec, RedisCommands.SORT_SET, getName(), order); |
|
} |
|
|
|
@Override |
|
public Set<V> readSort(SortOrder order, int offset, int count) { |
|
return get(readSortAsync(order, offset, count)); |
|
} |
|
|
|
@Override |
|
public RFuture<Set<V>> readSortAsync(SortOrder order, int offset, int count) { |
|
return commandExecutor.readAsync(getName(), codec, RedisCommands.SORT_SET, getName(), "LIMIT", offset, count, order); |
|
} |
|
|
|
@Override |
|
public Set<V> readSort(String byPattern, SortOrder order) { |
|
return get(readSortAsync(byPattern, order)); |
|
} |
|
|
|
@Override |
|
public RFuture<Set<V>> readSortAsync(String byPattern, SortOrder order) { |
|
return commandExecutor.readAsync(getName(), codec, RedisCommands.SORT_SET, getName(), "BY", byPattern, order); |
|
} |
|
|
|
@Override |
|
public Set<V> readSort(String byPattern, SortOrder order, int offset, int count) { |
|
return get(readSortAsync(byPattern, order, offset, count)); |
|
} |
|
|
|
@Override |
|
public RFuture<Set<V>> readSortAsync(String byPattern, SortOrder order, int offset, int count) { |
|
return commandExecutor.readAsync(getName(), codec, RedisCommands.SORT_SET, getName(), "BY", byPattern, "LIMIT", offset, count, order); |
|
} |
|
|
|
@Override |
|
public <T> Collection<T> readSort(String byPattern, List<String> getPatterns, SortOrder order) { |
|
return (Collection<T>)get(readSortAsync(byPattern, getPatterns, order)); |
|
} |
|
|
|
@Override |
|
public <T> RFuture<Collection<T>> readSortAsync(String byPattern, List<String> getPatterns, SortOrder order) { |
|
return readSortAsync(byPattern, getPatterns, order, -1, -1); |
|
} |
|
|
|
@Override |
|
public <T> Collection<T> readSort(String byPattern, List<String> getPatterns, SortOrder order, int offset, int count) { |
|
return (Collection<T>)get(readSortAsync(byPattern, getPatterns, order, offset, count)); |
|
} |
|
|
|
@Override |
|
public <T> RFuture<Collection<T>> readSortAsync(String byPattern, List<String> getPatterns, SortOrder order, int offset, int count) { |
|
List<Object> params = new ArrayList<Object>(); |
|
params.add(getName()); |
|
if (byPattern != null) { |
|
params.add("BY"); |
|
params.add(byPattern); |
|
} |
|
if (offset != -1 && count != -1) { |
|
params.add("LIMIT"); |
|
} |
|
if (offset != -1) { |
|
params.add(offset); |
|
} |
|
if (count != -1) { |
|
params.add(count); |
|
} |
|
for (String pattern : getPatterns) { |
|
params.add("GET"); |
|
params.add(pattern); |
|
} |
|
params.add(order); |
|
|
|
return commandExecutor.readAsync(getName(), codec, RedisCommands.SORT_SET, params.toArray()); |
|
} |
|
|
|
@Override |
|
public int sortTo(String destName, SortOrder order) { |
|
return get(sortToAsync(destName, order)); |
|
} |
|
|
|
@Override |
|
public RFuture<Integer> sortToAsync(String destName, SortOrder order) { |
|
return sortToAsync(destName, null, Collections.<String>emptyList(), order, -1, -1); |
|
} |
|
|
|
@Override |
|
public int sortTo(String destName, SortOrder order, int offset, int count) { |
|
return get(sortToAsync(destName, order, offset, count)); |
|
} |
|
|
|
@Override |
|
public RFuture<Integer> sortToAsync(String destName, SortOrder order, int offset, int count) { |
|
return sortToAsync(destName, null, Collections.<String>emptyList(), order, offset, count); |
|
} |
|
|
|
@Override |
|
public int sortTo(String destName, String byPattern, SortOrder order, int offset, int count) { |
|
return get(sortToAsync(destName, byPattern, order, offset, count)); |
|
} |
|
|
|
@Override |
|
public int sortTo(String destName, String byPattern, SortOrder order) { |
|
return get(sortToAsync(destName, byPattern, order)); |
|
} |
|
|
|
@Override |
|
public RFuture<Integer> sortToAsync(String destName, String byPattern, SortOrder order) { |
|
return sortToAsync(destName, byPattern, Collections.<String>emptyList(), order, -1, -1); |
|
} |
|
|
|
@Override |
|
public RFuture<Integer> sortToAsync(String destName, String byPattern, SortOrder order, int offset, int count) { |
|
return sortToAsync(destName, byPattern, Collections.<String>emptyList(), order, offset, count); |
|
} |
|
|
|
@Override |
|
public int sortTo(String destName, String byPattern, List<String> getPatterns, SortOrder order) { |
|
return get(sortToAsync(destName, byPattern, getPatterns, order)); |
|
} |
|
|
|
@Override |
|
public RFuture<Integer> sortToAsync(String destName, String byPattern, List<String> getPatterns, SortOrder order) { |
|
return sortToAsync(destName, byPattern, getPatterns, order, -1, -1); |
|
} |
|
|
|
@Override |
|
public int sortTo(String destName, String byPattern, List<String> getPatterns, SortOrder order, int offset, int count) { |
|
return get(sortToAsync(destName, byPattern, getPatterns, order, offset, count)); |
|
} |
|
|
|
@Override |
|
public RFuture<Integer> sortToAsync(String destName, String byPattern, List<String> getPatterns, SortOrder order, int offset, int count) { |
|
List<Object> params = new ArrayList<Object>(); |
|
params.add(getName()); |
|
if (byPattern != null) { |
|
params.add("BY"); |
|
params.add(byPattern); |
|
} |
|
if (offset != -1 && count != -1) { |
|
params.add("LIMIT"); |
|
} |
|
if (offset != -1) { |
|
params.add(offset); |
|
} |
|
if (count != -1) { |
|
params.add(count); |
|
} |
|
for (String pattern : getPatterns) { |
|
params.add("GET"); |
|
params.add(pattern); |
|
} |
|
params.add(order); |
|
params.add("STORE"); |
|
params.add(destName); |
|
|
|
return commandExecutor.writeAsync(getName(), codec, RedisCommands.SORT_TO, params.toArray()); |
|
} |
|
|
|
private String getLockName(Object value) { |
|
ByteBuf state = encode(value); |
|
try { |
|
return suffixName(getName(value), Hash.hash128toBase64(state) + ":lock"); |
|
} finally { |
|
state.release(); |
|
} |
|
} |
|
|
|
@Override |
|
public RLock getLock(V value) { |
|
String lockName = getLockName(value); |
|
return new RedissonLock(commandExecutor, lockName); |
|
} |
|
|
|
}
|
|
|