Merge remote-tracking branch 'redisson/master' into RedissonReferenceSerializable

pull/904/head
Rui Gu 8 years ago
commit f17a2551b6

@ -4,7 +4,42 @@ Redisson Releases History
Try __ULTRA-FAST__ [Redisson PRO](https://redisson.pro) edition.
## Please take part in [Redisson survey](https://www.surveymonkey.com/r/QXQZH5D)
### 10-May-2017 - versions 2.9.2 and 3.4.2 released
Feature - __Dropwizard metrics integration__ More details [here](https://github.com/redisson/redisson/wiki/14.-Integration-with-frameworks#147-dropwizard-metrics)
Feature - `RLocalCachedMap.preloadCache` method added (thanks to Steve Draper)
Feature - `RGeo.radiusStoreTo` methods added (thanks to Cory Sherman)
Fixed - NoClassDefFoundError exception during using `redisson-all` module
### 27-Apr-2017 - versions 2.9.1 and 3.4.1 released
Fixed - `RLocalCachedMap.getAll` didn't use cache (thanks to Steve Draper)
Fixed - reference to avro module has been removed
### 26-Apr-2017 - versions 2.9.0 and 3.4.0 released
Feature - __`MapReduceService` added__ More details [here](https://github.com/redisson/redisson/wiki/9.-distributed-services/#95-distributed-mapreduce-service)
Feature - `readAllMap` and `readAllMapAsync` methods added to `RMap`
Feature - `readAllKeySet` and `getReadWriteLock` methods added to `RMultimap`
Feature - `RKeys.delete` by objects method added
Feature - `RRemoteService.deregister` method added
Feature - `retryAttempts`, `retryInterval` and `timeout` methods added to `RBatch` object
Feature - `RMapCache.fastPutIfAbsent` with ttl added (thanks to Dobi)
Feature - `EvictionPolicy.WEAK` added for `RLocalCachedMap`
Feature - `LocalCachedMapOptions.invalidationPolicy` introduced for `RLocalCachedMap`
Feature - `expire`, `expireAt`, `move`, `migrate`, `clearExpire`, `renamenx`, `rename`, `remainTimeToLive` methods added to RKey
Improvement - `EvictionPolicy.LRU` optimization for `RLocalCachedMap`
Fixed - `RTopic.onSubscribe` should be invoked after failover process
Fixed - Spring boot with redisson 3.3.2 fails without optional actuator dependency (thanks to Rick Perkowski)
Fixed - `RedissonCacheMap.putIfAbsentAsync` doesn't take in account ttl and minIdleTime params (thanks to Dobi)
Fixed - Spring cache should put NullValue object instead of null
Fixed - Fixed error - No field factory in class Ljava/net/URL
Fixed - Spring cache's method with `@Cacheable(sync=true)` annotation never expires (thanks to Dobi)
Fixed - spring schema file corrected (thanks to Rui Gu)
Fixed - Prevent to set URL.factory to null in case of concurrent URL creation in the URLBuilder (thanks to Björn-Ole Ebers)
Fixed - `RMap.addAndGet` causes bad argument (thanks to Rui Gu)
Fixed - `RedissonSpringCacheManager` creates new cache on each `getCache` call
Fixed - wrong value codec encoder usage for `RedissonLocalCachedMap.fastPutAsync` method
### 21-Mar-2017 - versions 2.8.2 and 3.3.2 released

@ -1,14 +1,14 @@
Redisson: Redis based In-Memory Data Grid for Java.
====
[Quick start](https://github.com/redisson/redisson#quick-start) | [Documentation](https://github.com/redisson/redisson/wiki) | [Javadocs](http://www.javadoc.io/doc/org.redisson/redisson/3.3.2) | [Changelog](https://github.com/redisson/redisson/blob/master/CHANGELOG.md) | [Code examples](https://github.com/redisson/redisson-examples) | [Support chat](https://gitter.im/mrniko/redisson) | [Ultra-fast version](https://redisson.pro)
[Quick start](https://github.com/redisson/redisson#quick-start) | [Documentation](https://github.com/redisson/redisson/wiki) | [Javadocs](http://www.javadoc.io/doc/org.redisson/redisson/3.4.1) | [Changelog](https://github.com/redisson/redisson/blob/master/CHANGELOG.md) | [Code examples](https://github.com/redisson/redisson-examples) | [Support chat](https://gitter.im/mrniko/redisson) | **[Ultra-fast version](https://redisson.pro)**
Based on high-performance async and lock-free Java Redis client and [Netty](http://netty.io) framework.
## Please take part in [Redisson survey](https://www.surveymonkey.com/r/QXQZH5D)
| Stable Release Version | JDK Version compatibility | Release Date |
| ------------- | ------------- | ------------|
| 3.3.2 | 1.8+ | 21.03.2017 |
| 2.8.2 | 1.6, 1.7, 1.8 and Android | 21.03.2017 |
| 3.4.2 | 1.8+ | 10.05.2017 |
| 2.9.2 | 1.6, 1.7, 1.8 and Android | 10.05.2017 |
__NOTE__: Both version lines have same features except `CompletionStage` interface added in 3.x.x
@ -38,8 +38,8 @@ Features
* [Distributed locks and synchronizers](https://github.com/redisson/redisson/wiki/8.-Distributed-locks-and-synchronizers)
Lock, FairLock, MultiLock, RedLock, ReadWriteLock, Semaphore, PermitExpirableSemaphore, CountDownLatch
* [Distributed services](https://github.com/redisson/redisson/wiki/9.-distributed-services)
Remote service, Live Object service, Executor service, Scheduler service
* [Spring Cache](https://github.com/redisson/redisson/wiki/14.-Integration%20with%20frameworks/#141-spring-cache) implementation
Remote service, Live Object service, Executor service, Scheduler service, MapReduce service
* [Spring Cache](https://github.com/redisson/redisson/wiki/14.-Integration%20with%20frameworks/#141-spring-cache) implementation  
* [Hibernate Cache](https://github.com/redisson/redisson/wiki/14.-Integration%20with%20frameworks/#142-hibernate-cache) implementation
* [JCache API (JSR-107)](https://github.com/redisson/redisson/wiki/14.-Integration%20with%20frameworks/#143-jcache-api-jsr-107-implementation) implementation
* [Tomcat Session Manager](https://github.com/redisson/redisson/wiki/14.-Integration%20with%20frameworks#144-tomcat-redis-session-manager) implementation
@ -47,7 +47,7 @@ Features
* [Reactive Streams](https://github.com/redisson/redisson/wiki/3.-operations-execution#32-reactive-way)
* [Redis pipelining](https://github.com/redisson/redisson/wiki/10.-additional-features#102-execution-batches-of-commands) (command batches)
* Supports Android platform
* Supports auto-reconnect
* Supports auto-reconnection
* Supports failed to send command auto-retry
* Supports OSGi
* Supports many popular codecs ([Jackson JSON](https://github.com/FasterXML/jackson), [Avro](http://avro.apache.org/), [Smile](http://wiki.fasterxml.com/SmileFormatSpec), [CBOR](http://cbor.io/), [MsgPack](http://msgpack.org/), [Kryo](https://github.com/EsotericSoftware/kryo), [FST](https://github.com/RuedigerMoeller/fast-serialization), [LZ4](https://github.com/jpountz/lz4-java), [Snappy](https://github.com/xerial/snappy-java) and JDK Serialization)
@ -82,23 +82,23 @@ Quick start
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.3.2</version>
<version>3.4.2</version>
</dependency>
<!-- JDK 1.6+ compatible -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>2.8.2</version>
<version>2.9.2</version>
</dependency>
#### Gradle
// JDK 1.8+ compatible
compile 'org.redisson:redisson:3.3.2'
compile 'org.redisson:redisson:3.4.2'
// JDK 1.6+ compatible
compile 'org.redisson:redisson:2.8.2'
compile 'org.redisson:redisson:2.9.2'
#### Java
@ -123,11 +123,11 @@ RExecutorService executor = redisson.getExecutorService("myExecutorService");
Downloads
===============================
[Redisson 3.3.2](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson&v=3.3.2&e=jar),
[Redisson node 3.3.2](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=3.3.2&e=jar)
[Redisson 3.4.2](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson&v=3.4.2&e=jar),
[Redisson node 3.4.2](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=3.4.2&e=jar)
[Redisson 2.8.2](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson&v=2.8.2&e=jar),
[Redisson node 2.8.2](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=2.8.2&e=jar)
[Redisson 2.9.2](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson&v=2.9.2&e=jar),
[Redisson node 2.9.2](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=2.9.2&e=jar)
### Supported by

@ -3,7 +3,7 @@
<groupId>org.redisson</groupId>
<artifactId>redisson-parent</artifactId>
<version>2.8.3-SNAPSHOT</version>
<version>2.9.3-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Redisson</name>

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-parent</artifactId>
<version>2.8.3-SNAPSHOT</version>
<version>2.9.3-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>
@ -87,7 +87,7 @@
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
<classifier>linux-x86_64</classifier>
<version>4.1.8.Final</version>
<version>4.1.11.Final</version>
</dependency>
<dependency>
<groupId>com.esotericsoftware</groupId>

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-parent</artifactId>
<version>2.8.3-SNAPSHOT</version>
<version>2.9.3-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-tomcat</artifactId>
<version>2.8.3-SNAPSHOT</version>
<version>2.9.3-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-tomcat</artifactId>
<version>2.8.3-SNAPSHOT</version>
<version>2.9.3-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-tomcat</artifactId>
<version>2.8.3-SNAPSHOT</version>
<version>2.9.3-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>

@ -4,7 +4,7 @@
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-parent</artifactId>
<version>2.8.3-SNAPSHOT</version>
<version>2.9.3-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>
@ -34,33 +34,33 @@
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
<version>4.1.8.Final</version>
<version>4.1.11.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-common</artifactId>
<version>4.1.8.Final</version>
<version>4.1.11.Final</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec</artifactId>
<version>4.1.8.Final</version>
<version>4.1.11.Final</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-buffer</artifactId>
<version>4.1.8.Final</version>
<version>4.1.11.Final</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport</artifactId>
<version>4.1.8.Final</version>
<version>4.1.11.Final</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
<version>4.1.8.Final</version>
<version>4.1.11.Final</version>
</dependency>
<dependency>
@ -186,56 +186,54 @@
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>2.6.7</version>
<version>2.7.6</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.6.7</version>
<version>2.7.6</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.7</version>
<version>2.7.6</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-cbor</artifactId>
<version>2.6.7</version>
<version>2.7.6</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-smile</artifactId>
<version>2.6.7</version>
<version>2.7.6</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-avro</artifactId>
<version>2.6.7</version>
<version>2.7.6</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>net.openhft</groupId>
<artifactId>zero-allocation-hashing</artifactId>
<version>0.7</version>
<optional>true</optional>
<version>0.8</version>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.6.8</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jodd</groupId>
<artifactId>jodd-bean</artifactId>
<version>3.7.1</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>

@ -16,6 +16,7 @@
package org.redisson;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@ -33,11 +34,10 @@ import org.redisson.client.protocol.RedisCommands;
import org.redisson.connection.ConnectionListener;
import org.redisson.connection.ConnectionManager;
import org.redisson.connection.RedisClientEntry;
import org.redisson.misc.RPromise;
import org.redisson.misc.URLBuilder;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import org.redisson.misc.URIBuilder;
/**
*
@ -56,7 +56,8 @@ public class RedisNodes<N extends Node> implements NodesGroup<N> {
@Override
public N getNode(String address) {
Collection<N> clients = (Collection<N>) connectionManager.getClients();
InetSocketAddress addr = URLBuilder.toAddress(address);
URI uri = URIBuilder.create(address);
InetSocketAddress addr = new InetSocketAddress(uri.getHost(), uri.getPort());
for (N node : clients) {
if (node.getAddr().equals(addr)) {
return node;
@ -94,17 +95,10 @@ public class RedisNodes<N extends Node> implements NodesGroup<N> {
@Override
public void operationComplete(Future<RedisConnection> future) throws Exception {
if (future.isSuccess()) {
final RedisConnection c = future.getNow();
RPromise<RedisConnection> connectionFuture = connectionManager.newPromise();
connectionManager.getConnectListener().onConnect(connectionFuture, c, null, connectionManager.getConfig());
connectionFuture.addListener(new FutureListener<RedisConnection>() {
@Override
public void operationComplete(Future<RedisConnection> future) throws Exception {
RFuture<String> r = c.async(connectionManager.getConfig().getPingTimeout(), RedisCommands.PING);
result.put(c, r);
latch.countDown();
}
});
RedisConnection c = future.getNow();
RFuture<String> r = c.async(connectionManager.getConfig().getPingTimeout(), RedisCommands.PING);
result.put(c, r);
latch.countDown();
} else {
latch.countDown();
}

@ -137,7 +137,7 @@ public class Redisson implements RedissonClient {
*/
public static RedissonClient create() {
Config config = new Config();
config.useSingleServer().setAddress("127.0.0.1:6379");
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
// config.useMasterSlaveConnection().setMasterAddress("127.0.0.1:6379").addSlaveAddress("127.0.0.1:6389").addSlaveAddress("127.0.0.1:6399");
// config.useSentinelConnection().setMasterName("mymaster").addSentinelAddress("127.0.0.1:26389", "127.0.0.1:26379");
// config.useClusterServers().addNodeAddress("127.0.0.1:7000");
@ -165,7 +165,7 @@ public class Redisson implements RedissonClient {
*/
public static RedissonReactiveClient createReactive() {
Config config = new Config();
config.useSingleServer().setAddress("127.0.0.1:6379");
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
// config.useMasterSlaveConnection().setMasterAddress("127.0.0.1:6379").addSlaveAddress("127.0.0.1:6389").addSlaveAddress("127.0.0.1:6399");
// config.useSentinelConnection().setMasterName("mymaster").addSentinelAddress("127.0.0.1:26389", "127.0.0.1:26379");
// config.useClusterServers().addNodeAddress("127.0.0.1:7000");
@ -253,17 +253,17 @@ public class Redisson implements RedissonClient {
@Override
public <K, V> RLocalCachedMap<K, V> getLocalCachedMap(String name, LocalCachedMapOptions options) {
return new RedissonLocalCachedMap<K, V>(id, connectionManager.getCommandExecutor(), name, options, evictionScheduler, this);
return new RedissonLocalCachedMap<K, V>(connectionManager.getCommandExecutor(), name, options, evictionScheduler, this);
}
@Override
public <K, V> RLocalCachedMap<K, V> getLocalCachedMap(String name, Codec codec, LocalCachedMapOptions options) {
return new RedissonLocalCachedMap<K, V>(id, codec, connectionManager.getCommandExecutor(), name, options, evictionScheduler, this);
return new RedissonLocalCachedMap<K, V>(codec, connectionManager.getCommandExecutor(), name, options, evictionScheduler, this);
}
@Override
public <K, V> RMap<K, V> getMap(String name) {
return new RedissonMap<K, V>(id, connectionManager.getCommandExecutor(), name, this);
return new RedissonMap<K, V>(connectionManager.getCommandExecutor(), name, this);
}
@Override
@ -308,17 +308,17 @@ public class Redisson implements RedissonClient {
@Override
public <K, V> RMapCache<K, V> getMapCache(String name) {
return new RedissonMapCache<K, V>(id, evictionScheduler, connectionManager.getCommandExecutor(), name, this);
return new RedissonMapCache<K, V>(evictionScheduler, connectionManager.getCommandExecutor(), name, this);
}
@Override
public <K, V> RMapCache<K, V> getMapCache(String name, Codec codec) {
return new RedissonMapCache<K, V>(id, codec, evictionScheduler, connectionManager.getCommandExecutor(), name, this);
return new RedissonMapCache<K, V>(codec, evictionScheduler, connectionManager.getCommandExecutor(), name, this);
}
@Override
public <K, V> RMap<K, V> getMap(String name, Codec codec) {
return new RedissonMap<K, V>(id, codec, connectionManager.getCommandExecutor(), name, this);
return new RedissonMap<K, V>(codec, connectionManager.getCommandExecutor(), name, this);
}
@Override

@ -26,6 +26,12 @@ import org.redisson.client.protocol.decoder.ScanObjectEntry;
import io.netty.buffer.ByteBuf;
/**
*
* @author Nikita Koksharov
*
* @param <V> value type
*/
abstract class RedissonBaseIterator<V> implements Iterator<V> {
private List<ByteBuf> firstValues;
@ -36,7 +42,6 @@ abstract class RedissonBaseIterator<V> implements Iterator<V> {
private boolean finished;
private boolean currentElementRemoved;
private boolean removeExecuted;
private V value;
@Override
@ -47,7 +52,6 @@ abstract class RedissonBaseIterator<V> implements Iterator<V> {
free(lastValues);
currentElementRemoved = false;
removeExecuted = false;
client = null;
firstValues = null;
lastValues = null;
@ -58,9 +62,7 @@ abstract class RedissonBaseIterator<V> implements Iterator<V> {
}
finished = false;
}
long prevIterPos;
do {
prevIterPos = nextIterPos;
ListScanResult<ScanObjectEntry> res = iterator(client, nextIterPos);
if (lastValues != null) {
free(lastValues);
@ -76,7 +78,6 @@ abstract class RedissonBaseIterator<V> implements Iterator<V> {
client = null;
firstValues = null;
nextIterPos = 0;
prevIterPos = -1;
}
} else {
if (firstValues.isEmpty()) {
@ -87,38 +88,38 @@ abstract class RedissonBaseIterator<V> implements Iterator<V> {
client = null;
firstValues = null;
nextIterPos = 0;
prevIterPos = -1;
continue;
}
if (res.getPos() == 0) {
free(firstValues);
free(lastValues);
finished = true;
return false;
}
}
} else if (lastValues.removeAll(firstValues)) {
} else if (lastValues.removeAll(firstValues)
|| (lastValues.isEmpty() && nextIterPos == 0)) {
free(firstValues);
free(lastValues);
currentElementRemoved = false;
removeExecuted = false;
client = null;
firstValues = null;
lastValues = null;
nextIterPos = 0;
prevIterPos = -1;
if (tryAgain()) {
continue;
}
finished = true;
return false;
}
}
lastIter = res.getValues().iterator();
nextIterPos = res.getPos();
} while (!lastIter.hasNext() && nextIterPos != prevIterPos);
if (prevIterPos == nextIterPos && !removeExecuted) {
finished = true;
}
} while (!lastIter.hasNext());
}
return lastIter.hasNext();
}
@ -170,7 +171,6 @@ abstract class RedissonBaseIterator<V> implements Iterator<V> {
lastIter.remove();
remove(value);
currentElementRemoved = true;
removeExecuted = true;
}
abstract void remove(V value);

@ -28,6 +28,14 @@ import org.redisson.client.protocol.decoder.ScanObjectEntry;
import io.netty.buffer.ByteBuf;
/**
*
* @author Nikita Koksharov
*
* @param <K> key type
* @param <V> value type
* @param <M> loaded value type
*/
public abstract class RedissonBaseMapIterator<K, V, M> implements Iterator<M> {
private Map<ByteBuf, ByteBuf> firstValues;
@ -38,7 +46,6 @@ public abstract class RedissonBaseMapIterator<K, V, M> implements Iterator<M> {
private boolean finished;
private boolean currentElementRemoved;
private boolean removeExecuted;
protected Map.Entry<ScanObjectEntry, ScanObjectEntry> entry;
@Override
@ -49,7 +56,6 @@ public abstract class RedissonBaseMapIterator<K, V, M> implements Iterator<M> {
free(lastValues);
currentElementRemoved = false;
removeExecuted = false;
client = null;
firstValues = null;
lastValues = null;
@ -60,15 +66,15 @@ public abstract class RedissonBaseMapIterator<K, V, M> implements Iterator<M> {
}
finished = false;
}
long prevIterPos;
do {
prevIterPos = nextIterPos;
MapScanResult<ScanObjectEntry, ScanObjectEntry> res = iterator();
if (lastValues != null) {
free(lastValues);
}
lastValues = convert(res.getMap());
client = res.getRedisClient();
if (nextIterPos == 0 && firstValues == null) {
firstValues = lastValues;
lastValues = null;
@ -76,7 +82,6 @@ public abstract class RedissonBaseMapIterator<K, V, M> implements Iterator<M> {
client = null;
firstValues = null;
nextIterPos = 0;
prevIterPos = -1;
}
} else {
if (firstValues.isEmpty()) {
@ -87,38 +92,38 @@ public abstract class RedissonBaseMapIterator<K, V, M> implements Iterator<M> {
client = null;
firstValues = null;
nextIterPos = 0;
prevIterPos = -1;
continue;
}
if (res.getPos() == 0) {
free(firstValues);
free(lastValues);
finished = true;
return false;
}
}
} else if (lastValues.keySet().removeAll(firstValues.keySet())) {
} else if (lastValues.keySet().removeAll(firstValues.keySet())
|| (lastValues.isEmpty() && nextIterPos == 0)) {
free(firstValues);
free(lastValues);
currentElementRemoved = false;
removeExecuted = false;
client = null;
firstValues = null;
lastValues = null;
nextIterPos = 0;
prevIterPos = -1;
if (tryAgain()) {
continue;
}
finished = true;
return false;
}
}
lastIter = res.getMap().entrySet().iterator();
nextIterPos = res.getPos();
} while (!lastIter.hasNext() && nextIterPos != prevIterPos);
if (prevIterPos == nextIterPos && !removeExecuted) {
finished = true;
}
} while (!lastIter.hasNext());
}
return lastIter.hasNext();
@ -184,7 +189,6 @@ public abstract class RedissonBaseMapIterator<K, V, M> implements Iterator<M> {
lastIter.remove();
removeKey();
currentElementRemoved = true;
removeExecuted = true;
entry = null;
}

@ -102,12 +102,12 @@ public class RedissonBatch implements RBatch {
@Override
public <K, V> RMapAsync<K, V> getMap(String name) {
return new RedissonMap<K, V>(id, executorService, name, null);
return new RedissonMap<K, V>(executorService, name, null);
}
@Override
public <K, V> RMapAsync<K, V> getMap(String name, Codec codec) {
return new RedissonMap<K, V>(id, codec, executorService, name, null);
return new RedissonMap<K, V>(codec, executorService, name, null);
}
@Override
@ -202,12 +202,12 @@ public class RedissonBatch implements RBatch {
@Override
public <K, V> RMapCacheAsync<K, V> getMapCache(String name, Codec codec) {
return new RedissonMapCache<K, V>(id, codec, evictionScheduler, executorService, name, null);
return new RedissonMapCache<K, V>(codec, evictionScheduler, executorService, name, null);
}
@Override
public <K, V> RMapCacheAsync<K, V> getMapCache(String name) {
return new RedissonMapCache<K, V>(id, evictionScheduler, executorService, name, null);
return new RedissonMapCache<K, V>(evictionScheduler, executorService, name, null);
}
@Override

@ -124,6 +124,17 @@ public class RedissonBlockingDeque<V> extends RedissonDeque<V> implements RBlock
return blockingQueue.pollLastAndOfferFirstTo(queueName, timeout, unit);
}
@Override
public V takeLastAndOfferFirstTo(String queueName) throws InterruptedException {
RFuture<V> res = takeLastAndOfferFirstToAsync(queueName);
return res.await().getNow();
}
@Override
public RFuture<V> takeLastAndOfferFirstToAsync(String queueName) {
return pollLastAndOfferFirstToAsync(queueName, 0, TimeUnit.SECONDS);
}
@Override
public int remainingCapacity() {
return Integer.MAX_VALUE;

@ -134,6 +134,17 @@ public class RedissonBlockingQueue<V> extends RedissonQueue<V> implements RBlock
return res.await().getNow();
}
@Override
public V takeLastAndOfferFirstTo(String queueName) throws InterruptedException {
RFuture<V> res = takeLastAndOfferFirstToAsync(queueName);
return res.await().getNow();
}
@Override
public RFuture<V> takeLastAndOfferFirstToAsync(String queueName) {
return pollLastAndOfferFirstToAsync(queueName, 0, TimeUnit.SECONDS);
}
@Override
public int remainingCapacity() {
return Integer.MAX_VALUE;

@ -113,7 +113,7 @@ public class RedissonBloomFilter<T> extends RedissonExpirable implements RBloomF
}
private long[] hash(byte[] state, int iterations, long size) {
long hash1 = LongHashFunction.xx_r39().hashBytes(state);
long hash1 = LongHashFunction.xx().hashBytes(state);
long hash2 = LongHashFunction.farmUo().hashBytes(state);
long[] indexes = new long[iterations];

@ -33,6 +33,7 @@ import org.redisson.command.CommandExecutor;
import org.redisson.connection.decoder.ListDrainToDecoder;
import org.redisson.misc.PromiseDelegator;
import org.redisson.misc.RPromise;
import org.redisson.misc.RedissonPromise;
import org.redisson.pubsub.SemaphorePubSub;
import io.netty.util.concurrent.Future;
@ -136,7 +137,7 @@ public class RedissonBoundedBlockingQueue<V> extends RedissonQueue<V> implements
}
private RPromise<V> wrapTakeFuture(final RFuture<V> takeFuture) {
final RPromise<V> result = new PromiseDelegator<V>(commandExecutor.getConnectionManager().<V>newPromise()) {
final RPromise<V> result = new RedissonPromise<V>() {
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
super.cancel(mayInterruptIfRunning);
@ -215,7 +216,7 @@ public class RedissonBoundedBlockingQueue<V> extends RedissonQueue<V> implements
@Override
public RFuture<V> pollAsync(long timeout, TimeUnit unit) {
RFuture<V> takeFuture = commandExecutor.writeAsync(getName(), codec, RedisCommands.BLPOP_VALUE, getName(), unit.toSeconds(timeout));
RFuture<V> takeFuture = commandExecutor.writeAsync(getName(), codec, RedisCommands.BLPOP_VALUE, getName(), toSeconds(timeout, unit));
return wrapTakeFuture(takeFuture);
}
@ -253,6 +254,17 @@ public class RedissonBoundedBlockingQueue<V> extends RedissonQueue<V> implements
return wrapTakeFuture(takeFuture);
}
@Override
public V takeLastAndOfferFirstTo(String queueName) throws InterruptedException {
RFuture<V> res = takeLastAndOfferFirstToAsync(queueName);
return res.await().getNow();
}
@Override
public RFuture<V> takeLastAndOfferFirstToAsync(String queueName) {
return pollLastAndOfferFirstToAsync(queueName, 0, TimeUnit.SECONDS);
}
@Override
public RFuture<V> pollLastAndOfferFirstToAsync(String queueName, long timeout, TimeUnit unit) {
RFuture<V> takeFuture = commandExecutor.writeAsync(getName(), codec, RedisCommands.BRPOPLPUSH, getName(), queueName, unit.toSeconds(timeout));

@ -25,7 +25,6 @@ import java.util.concurrent.TimeUnit;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RFuture;
import org.redisson.api.RQueue;
import org.redisson.api.RTopic;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.LongCodec;
@ -417,7 +416,7 @@ public class RedissonDelayedQueue<V> extends RedissonExpirable implements RDelay
public RFuture<V> peekAsync() {
return commandExecutor.evalReadAsync(getName(), codec, RedisCommands.EVAL_OBJECT,
"local v = redis.call('lindex', KEYS[1], 0); "
+ "if v ~= nil then "
+ "if v ~= false then "
+ "local randomId, value = struct.unpack('dLc0', v);"
+ "return value; "
+ "end "
@ -429,7 +428,7 @@ public class RedissonDelayedQueue<V> extends RedissonExpirable implements RDelay
public RFuture<V> pollAsync() {
return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_OBJECT,
"local v = redis.call('lpop', KEYS[1]); "
+ "if v ~= nil then "
+ "if v ~= false then "
+ "redis.call('zrem', KEYS[2], v); "
+ "local randomId, value = struct.unpack('dLc0', v);"
+ "return value; "
@ -447,7 +446,7 @@ public class RedissonDelayedQueue<V> extends RedissonExpirable implements RDelay
public RFuture<V> pollLastAndOfferFirstToAsync(String queueName) {
return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_OBJECT,
"local v = redis.call('rpop', KEYS[1]); "
+ "if v ~= nil then "
+ "if v ~= false then "
+ "redis.call('zrem', KEYS[2], v); "
+ "local randomId, value = struct.unpack('dLc0', v);"
+ "redis.call('lpush', KEYS[3], value); "

@ -30,6 +30,7 @@ import org.redisson.api.RGeo;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.GeoEntryCodec;
import org.redisson.client.codec.LongCodec;
import org.redisson.client.codec.ScoredCodec;
import org.redisson.client.protocol.RedisCommand;
import org.redisson.client.protocol.RedisCommand.ValueType;
@ -405,4 +406,64 @@ public class RedissonGeo<V> extends RedissonScoredSortedSet<V> implements RGeo<V
return commandExecutor.readAsync(getName(), codec, command, getName(), member, radius, geoUnit, "WITHCOORD", "COUNT", count, geoOrder);
}
@Override
public long radiusStoreTo(String destName, double longitude, double latitude, double radius, GeoUnit geoUnit) {
return get(radiusStoreToAsync(destName, longitude, latitude, radius, geoUnit));
}
@Override
public RFuture<Long> radiusStoreToAsync(String destName, double longitude, double latitude, double radius, GeoUnit geoUnit) {
return commandExecutor.writeAsync(getName(), LongCodec.INSTANCE, RedisCommands.GEORADIUS_STORE, getName(), convert(longitude), convert(latitude), radius, geoUnit, "STORE", destName);
}
@Override
public long radiusStoreTo(String destName, double longitude, double latitude, double radius, GeoUnit geoUnit, int count) {
return get(radiusStoreToAsync(destName, longitude, latitude, radius, geoUnit, count));
}
@Override
public RFuture<Long> radiusStoreToAsync(String destName, double longitude, double latitude, double radius, GeoUnit geoUnit, int count) {
return commandExecutor.writeAsync(getName(), LongCodec.INSTANCE, RedisCommands.GEORADIUS_STORE, getName(), convert(longitude), convert(latitude), radius, geoUnit, "COUNT", count, "STORE", destName);
}
@Override
public long radiusStoreTo(String destName, double longitude, double latitude, double radius, GeoUnit geoUnit, GeoOrder geoOrder, int count) {
return get(radiusStoreToAsync(destName, longitude, latitude, radius, geoUnit, geoOrder, count));
}
@Override
public RFuture<Long> radiusStoreToAsync(String destName, double longitude, double latitude, double radius, GeoUnit geoUnit, GeoOrder geoOrder, int count) {
return commandExecutor.writeAsync(getName(), LongCodec.INSTANCE, RedisCommands.GEORADIUS_STORE, getName(), convert(longitude), convert(latitude), radius, geoUnit, geoOrder, "COUNT", count, "STORE", destName);
}
@Override
public long radiusStoreTo(String destName, V member, double radius, GeoUnit geoUnit) {
return get(radiusStoreToAsync(destName, member, radius, geoUnit));
}
@Override
public RFuture<Long> radiusStoreToAsync(String destName, V member, double radius, GeoUnit geoUnit) {
return commandExecutor.writeAsync(getName(), codec, RedisCommands.GEORADIUSBYMEMBER_STORE, getName(), member, radius, geoUnit, "STORE", destName);
}
@Override
public long radiusStoreTo(String destName, V member, double radius, GeoUnit geoUnit, int count) {
return get(radiusStoreToAsync(destName, member, radius, geoUnit, count));
}
@Override
public RFuture<Long> radiusStoreToAsync(String destName, V member, double radius, GeoUnit geoUnit, int count) {
return commandExecutor.writeAsync(getName(), codec, RedisCommands.GEORADIUSBYMEMBER_STORE, getName(), member, radius, geoUnit, "COUNT", count, "STORE", destName);
}
@Override
public long radiusStoreTo(String destName, V member, double radius, GeoUnit geoUnit, GeoOrder geoOrder, int count) {
return get(radiusStoreToAsync(destName, member, radius, geoUnit, geoOrder, count));
}
@Override
public RFuture<Long> radiusStoreToAsync(String destName, V member, double radius, GeoUnit geoUnit, GeoOrder geoOrder, int count) {
return commandExecutor.writeAsync(getName(), codec, RedisCommands.GEORADIUSBYMEMBER_STORE, getName(), member, radius, geoUnit, geoOrder, "COUNT", count, "STORE", destName);
}
}

@ -20,6 +20,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@ -230,27 +231,15 @@ public class RedissonKeys implements RKeys {
};
for (MasterSlaveEntry entry : entries) {
RFuture<Collection<String>> findFuture = commandExecutor.readAsync(entry, null, RedisCommands.KEYS, pattern);
findFuture.addListener(new FutureListener<Collection<String>>() {
@Override
public void operationComplete(Future<Collection<String>> future) throws Exception {
if (!future.isSuccess()) {
failed.set(future.cause());
checkExecution(result, failed, count, executed);
return;
}
Collection<String> keys = future.getNow();
if (keys.isEmpty()) {
checkExecution(result, failed, count, executed);
return;
}
RFuture<Long> deleteFuture = deleteAsync(keys.toArray(new String[keys.size()]));
deleteFuture.addListener(listener);
}
});
}
Iterator<String> keysIterator = createKeysIterator(entry, pattern, 10);
Collection<String> keys = new HashSet<String>();
while (keysIterator.hasNext()) {
String key = keysIterator.next();
keys.add(key);
}
RFuture<Long> deleteFuture = deleteAsync(keys.toArray(new String[keys.size()]));
deleteFuture.addListener(listener);
}
return result;
}

@ -208,17 +208,17 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
private int invalidationStatusListenerId;
private volatile long lastInvalidate;
protected RedissonLocalCachedMap(UUID id, CommandAsyncExecutor commandExecutor, String name, LocalCachedMapOptions options, EvictionScheduler evictionScheduler, RedissonClient redisson) {
super(id, commandExecutor, name, redisson);
init(id, name, options, redisson, evictionScheduler);
protected RedissonLocalCachedMap(CommandAsyncExecutor commandExecutor, String name, LocalCachedMapOptions options, EvictionScheduler evictionScheduler, RedissonClient redisson) {
super(commandExecutor, name, redisson);
init(name, options, redisson, evictionScheduler);
}
protected RedissonLocalCachedMap(UUID id, Codec codec, CommandAsyncExecutor connectionManager, String name, LocalCachedMapOptions options, EvictionScheduler evictionScheduler, RedissonClient redisson) {
super(id, codec, connectionManager, name, redisson);
init(id, name, options, redisson, evictionScheduler);
protected RedissonLocalCachedMap(Codec codec, CommandAsyncExecutor connectionManager, String name, LocalCachedMapOptions options, EvictionScheduler evictionScheduler, RedissonClient redisson) {
super(codec, connectionManager, name, redisson);
init(name, options, redisson, evictionScheduler);
}
private void init(UUID id, String name, LocalCachedMapOptions options, RedissonClient redisson, EvictionScheduler evictionScheduler) {
private void init(String name, LocalCachedMapOptions options, RedissonClient redisson, EvictionScheduler evictionScheduler) {
instanceId = generateId();
if (options.getInvalidationPolicy() == InvalidationPolicy.ON_CHANGE
@ -853,7 +853,8 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
Set<K> mapKeys = new HashSet<K>(keys);
for (Iterator<K> iterator = mapKeys.iterator(); iterator.hasNext();) {
K key = iterator.next();
CacheValue value = cache.get(key);
final CacheKey cacheKey = toCacheKey(key);
CacheValue value = cache.get(cacheKey);
if (value != null) {
result.put(key, (V)value.getValue());
iterator.remove();
@ -1084,6 +1085,18 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
return promise;
}
@Override
public void preloadCache() {
// Best-attempt warmup - just enumerate as an uncached map (super) and
// add anything found into the cache. This does not guarantee to find
// entries added during the warmUp, but statistically the cache will have
// few misses after this process
for(Entry<K,V> entry : super.entrySet()) {
CacheKey cacheKey = toCacheKey(entry.getKey());
cache.put(cacheKey, new CacheValue(entry.getKey(), entry.getValue()));
}
}
@Override
public RFuture<Set<Entry<K, V>>> readAllEntrySetAsync() {
final Set<Entry<K, V>> result = new HashSet<Entry<K, V>>();

@ -28,7 +28,6 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.redisson.api.RFuture;
import org.redisson.api.RLock;
@ -47,7 +46,6 @@ import org.redisson.client.protocol.convertor.NumberConvertor;
import org.redisson.client.protocol.decoder.MapScanResult;
import org.redisson.client.protocol.decoder.ScanObjectEntry;
import org.redisson.command.CommandAsyncExecutor;
import org.redisson.command.CommandExecutor;
import org.redisson.connection.decoder.MapGetAllDecoder;
import org.redisson.mapreduce.RedissonMapReduce;
import org.redisson.misc.Hash;
@ -69,18 +67,15 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
static final RedisCommand<Boolean> EVAL_REMOVE_VALUE = new RedisCommand<Boolean>("EVAL", new BooleanReplayConvertor(), 4, ValueType.MAP);
static final RedisCommand<Object> EVAL_PUT = EVAL_REPLACE;
private final UUID id;
private final RedissonClient redisson;
final RedissonClient redisson;
protected RedissonMap(UUID id, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
protected RedissonMap(CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(commandExecutor, name);
this.id = id;
this.redisson = redisson;
}
public RedissonMap(UUID id, Codec codec, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
public RedissonMap(Codec codec, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(codec, commandExecutor, name);
this.id = id;
this.redisson = redisson;
}
@ -92,13 +87,13 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
@Override
public RLock getLock(K key) {
String lockName = getLockName(key);
return new RedissonLock((CommandExecutor)commandExecutor, lockName, id);
return redisson.getLock(lockName);
}
@Override
public RReadWriteLock getReadWriteLock(K key) {
String lockName = getLockName(key);
return new RedissonReadWriteLock((CommandExecutor)commandExecutor, lockName, id);
return redisson.getReadWriteLock(lockName);
}
private String getLockName(Object key) {
@ -127,11 +122,15 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
@Override
public RFuture<Integer> valueSizeAsync(K key) {
checkKey(key);
return commandExecutor.readAsync(getName(), codec, RedisCommands.HSTRLEN, getName(key), key);
}
protected void checkKey(Object key) {
if (key == null) {
throw new NullPointerException("map key can't be null");
}
return commandExecutor.readAsync(getName(), codec, RedisCommands.HSTRLEN, getName(key), key);
}
@Override
@ -146,9 +145,7 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
@Override
public RFuture<Boolean> containsKeyAsync(Object key) {
if (key == null) {
throw new NullPointerException("map key can't be null");
}
checkKey(key);
return commandExecutor.readAsync(getName(key), codec, RedisCommands.HEXISTS, getName(key), key);
}
@ -160,9 +157,7 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
@Override
public RFuture<Boolean> containsValueAsync(Object value) {
if (value == null) {
throw new NullPointerException("map value can't be null");
}
checkValue(value);
return commandExecutor.evalReadAsync(getName(), codec, new RedisCommand<Boolean>("EVAL", new BooleanReplayConvertor(), 4),
"local s = redis.call('hvals', KEYS[1]);" +
@ -296,12 +291,8 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
@Override
public RFuture<V> putIfAbsentAsync(K key, V value) {
if (key == null) {
throw new NullPointerException("map key can't be null");
}
if (value == null) {
throw new NullPointerException("map value can't be null");
}
checkKey(key);
checkValue(key);
return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_PUT,
"if redis.call('hsetnx', KEYS[1], ARGV[1], ARGV[2]) == 1 then "
@ -319,12 +310,8 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
@Override
public RFuture<Boolean> fastPutIfAbsentAsync(K key, V value) {
if (key == null) {
throw new NullPointerException("map key can't be null");
}
if (value == null) {
throw new NullPointerException("map value can't be null");
}
checkKey(key);
checkValue(value);
return commandExecutor.writeAsync(getName(key), codec, RedisCommands.HSETNX, getName(key), key, value);
}
@ -336,12 +323,8 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
@Override
public RFuture<Boolean> removeAsync(Object key, Object value) {
if (key == null) {
throw new NullPointerException("map key can't be null");
}
if (value == null) {
throw new NullPointerException("map value can't be null");
}
checkKey(key);
checkValue(value);
return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_REMOVE_VALUE,
"if redis.call('hget', KEYS[1], ARGV[1]) == ARGV[2] then "
@ -352,6 +335,12 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
Collections.<Object>singletonList(getName(key)), key, value);
}
protected void checkValue(Object value) {
if (value == null) {
throw new NullPointerException("map value can't be null");
}
}
@Override
public boolean replace(K key, V oldValue, V newValue) {
return get(replaceAsync(key, oldValue, newValue));
@ -359,9 +348,7 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
@Override
public RFuture<Boolean> replaceAsync(K key, V oldValue, V newValue) {
if (key == null) {
throw new NullPointerException("map key can't be null");
}
checkKey(key);
if (oldValue == null) {
throw new NullPointerException("map oldValue can't be null");
}
@ -387,12 +374,8 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
@Override
public RFuture<V> replaceAsync(K key, V value) {
if (key == null) {
throw new NullPointerException("map key can't be null");
}
if (value == null) {
throw new NullPointerException("map value can't be null");
}
checkKey(key);
checkValue(value);
return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_REPLACE,
"if redis.call('hexists', KEYS[1], ARGV[1]) == 1 then "
@ -407,21 +390,15 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
@Override
public RFuture<V> getAsync(K key) {
if (key == null) {
throw new NullPointerException("map key can't be null");
}
checkKey(key);
return commandExecutor.readAsync(getName(key), codec, RedisCommands.HGET, getName(key), key);
}
@Override
public RFuture<V> putAsync(K key, V value) {
if (key == null) {
throw new NullPointerException("map key can't be null");
}
if (value == null) {
throw new NullPointerException("map value can't be null");
}
checkKey(key);
checkValue(value);
return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_PUT,
"local v = redis.call('hget', KEYS[1], ARGV[1]); "
@ -433,9 +410,7 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
@Override
public RFuture<V> removeAsync(K key) {
if (key == null) {
throw new NullPointerException("map key can't be null");
}
checkKey(key);
return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_REMOVE,
"local v = redis.call('hget', KEYS[1], ARGV[1]); "
@ -446,12 +421,8 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
@Override
public RFuture<Boolean> fastPutAsync(K key, V value) {
if (key == null) {
throw new NullPointerException("map key can't be null");
}
if (value == null) {
throw new NullPointerException("map value can't be null");
}
checkKey(key);
checkValue(value);
return commandExecutor.writeAsync(getName(key), codec, RedisCommands.HSET, getName(key), key, value);
}
@ -491,12 +462,8 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
@Override
public RFuture<V> addAndGetAsync(K key, Number value) {
if (key == null) {
throw new NullPointerException("map key can't be null");
}
if (value == null) {
throw new NullPointerException("map value can't be null");
}
checkKey(key);
checkValue(value);
byte[] keyState = encodeMapKey(key);
return commandExecutor.writeAsync(getName(key), StringCodec.INSTANCE,

@ -15,6 +15,8 @@
*/
package org.redisson;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
@ -23,19 +25,27 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import org.redisson.api.RFuture;
import org.redisson.api.RMapCache;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
import org.redisson.api.listener.MessageListener;
import org.redisson.api.map.event.EntryCreatedListener;
import org.redisson.api.map.event.EntryEvent;
import org.redisson.api.map.event.EntryExpiredListener;
import org.redisson.api.map.event.EntryRemovedListener;
import org.redisson.api.map.event.EntryUpdatedListener;
import org.redisson.api.map.event.MapEntryListener;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.LongCodec;
import org.redisson.client.codec.MapScanCodec;
import org.redisson.client.codec.StringCodec;
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.NumberConvertor;
import org.redisson.client.protocol.convertor.VoidReplayConvertor;
import org.redisson.client.protocol.decoder.ListMultiDecoder;
import org.redisson.client.protocol.decoder.LongMultiDecoder;
@ -45,16 +55,13 @@ import org.redisson.client.protocol.decoder.MapScanResult;
import org.redisson.client.protocol.decoder.ObjectListDecoder;
import org.redisson.client.protocol.decoder.ObjectMapDecoder;
import org.redisson.client.protocol.decoder.ScanObjectEntry;
import org.redisson.codec.MapCacheEventCodec;
import org.redisson.command.CommandAsyncExecutor;
import org.redisson.connection.decoder.MapGetAllDecoder;
import org.redisson.eviction.EvictionScheduler;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import java.io.IOException;
import java.math.BigDecimal;
import org.redisson.client.codec.StringCodec;
import org.redisson.client.protocol.convertor.NumberConvertor;
/**
* <p>Map-based cache with ability to set TTL for each entry via
@ -77,43 +84,39 @@ import org.redisson.client.protocol.convertor.NumberConvertor;
*/
public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCache<K, V> {
static final RedisCommand<Boolean> EVAL_PUT_IF_ABSENT = new RedisCommand<Boolean>("EVAL", new BooleanReplayConvertor(), 7, ValueType.MAP);
static final RedisCommand<Boolean> EVAL_HSET = new RedisCommand<Boolean>("EVAL", new BooleanReplayConvertor(), 4, ValueType.MAP);
static final RedisCommand<Object> EVAL_REPLACE = new RedisCommand<Object>("EVAL", 6, ValueType.MAP, ValueType.MAP_VALUE);
static final RedisCommand<Boolean> EVAL_REPLACE_VALUE = new RedisCommand<Boolean>("EVAL", new BooleanReplayConvertor(), 7, Arrays.asList(ValueType.MAP_KEY, ValueType.MAP_VALUE, ValueType.MAP_VALUE));
static final RedisCommand<Object> EVAL_REPLACE = new RedisCommand<Object>("EVAL", 7, ValueType.MAP, ValueType.MAP_VALUE);
static final RedisCommand<Void> EVAL_HMSET = new RedisCommand<Void>("EVAL", new VoidReplayConvertor(), 4, ValueType.MAP);
private static final RedisCommand<Object> EVAL_REMOVE = new RedisCommand<Object>("EVAL", 4, ValueType.MAP_KEY, ValueType.MAP_VALUE);
private static final RedisCommand<Boolean> EVAL_REMOVE_VALUE = new RedisCommand<Boolean>("EVAL", new BooleanReplayConvertor(), 5, ValueType.MAP);
private static final RedisCommand<Object> EVAL_PUT_TTL = new RedisCommand<Object>("EVAL", 9, ValueType.MAP, ValueType.MAP_VALUE);
private static final RedisCommand<Object> EVAL_PUT_TTL_IF_ABSENT = new RedisCommand<Object>("EVAL", 10, ValueType.MAP, ValueType.MAP_VALUE);
private static final RedisCommand<Boolean> EVAL_FAST_PUT_TTL = new RedisCommand<Boolean>("EVAL", new BooleanReplayConvertor(), 9, ValueType.MAP, ValueType.MAP_VALUE);
private static final RedisCommand<Boolean> EVAL_FAST_PUT_TTL_IF_ABSENT = new RedisCommand<Boolean>("EVAL", new BooleanReplayConvertor(), 10, ValueType.MAP, ValueType.MAP_VALUE);
private static final RedisCommand<Object> EVAL_REMOVE = new RedisCommand<Object>("EVAL", 7, ValueType.MAP_KEY, ValueType.MAP_VALUE);
private static final RedisCommand<Object> EVAL_PUT_TTL = new RedisCommand<Object>("EVAL", 12, ValueType.MAP, ValueType.MAP_VALUE);
private static final RedisCommand<Object> EVAL_PUT_TTL_IF_ABSENT = new RedisCommand<Object>("EVAL", 11, ValueType.MAP, ValueType.MAP_VALUE);
private static final RedisCommand<Object> EVAL_GET_TTL = new RedisCommand<Object>("EVAL", 7, ValueType.MAP_KEY, ValueType.MAP_VALUE);
private static final RedisCommand<Boolean> EVAL_CONTAINS_KEY = new RedisCommand<Boolean>("EVAL", new BooleanReplayConvertor(), 7, ValueType.MAP_KEY);
static final RedisCommand<Boolean> EVAL_CONTAINS_VALUE = new RedisCommand<Boolean>("EVAL", new BooleanReplayConvertor(), 7, ValueType.MAP_VALUE);
static final RedisCommand<Long> EVAL_FAST_REMOVE = new RedisCommand<Long>("EVAL", 5, ValueType.MAP_KEY);
static final RedisCommand<Long> EVAL_FAST_REMOVE = new RedisCommand<Long>("EVAL", 7, ValueType.MAP_KEY);
static final RedisCommand<Object> EVAL_PUT = new RedisCommand<Object>("EVAL", 6, ValueType.MAP, ValueType.MAP_VALUE);
static final RedisCommand<Object> EVAL_PUT_IF_ABSENT = new RedisCommand<Object>("EVAL", 5, ValueType.MAP, ValueType.MAP_VALUE);
RedissonMapCache(UUID id, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(id, commandExecutor, name, redisson);
RedissonMapCache(CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(commandExecutor, name, redisson);
}
RedissonMapCache(UUID id, Codec codec, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(id, codec, commandExecutor, name, redisson);
RedissonMapCache(Codec codec, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(codec, commandExecutor, name, redisson);
}
public RedissonMapCache(UUID id, EvictionScheduler evictionScheduler, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(id, commandExecutor, name, redisson);
evictionScheduler.schedule(getName(), getTimeoutSetName(), getIdleSetName());
public RedissonMapCache(EvictionScheduler evictionScheduler, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(commandExecutor, name, redisson);
evictionScheduler.schedule(getName(), getTimeoutSetName(), getIdleSetName(), getExpiredChannelName());
}
public RedissonMapCache(UUID id, Codec codec, EvictionScheduler evictionScheduler, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(id, codec, commandExecutor, name, redisson);
evictionScheduler.schedule(getName(), getTimeoutSetName(), getIdleSetName());
public RedissonMapCache(Codec codec, EvictionScheduler evictionScheduler, CommandAsyncExecutor commandExecutor, String name, RedissonClient redisson) {
super(codec, commandExecutor, name, redisson);
evictionScheduler.schedule(getName(), getTimeoutSetName(), getIdleSetName(), getExpiredChannelName());
}
@Override
public RFuture<Boolean> containsKeyAsync(Object key) {
return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_CONTAINS_KEY,
checkKey(key);
return commandExecutor.evalWriteAsync(getName(key), codec, RedisCommands.EVAL_BOOLEAN,
"local value = redis.call('hget', KEYS[1], ARGV[2]); " +
"local expireDate = 92233720368547758; " +
"if value ~= false then " +
@ -139,12 +142,15 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
+ "return 1;" +
"end;" +
"return 0; ",
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key)), System.currentTimeMillis(), key);
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key)),
System.currentTimeMillis(), encodeMapKey(key));
}
@Override
public RFuture<Boolean> containsValueAsync(Object value) {
return commandExecutor.evalWriteAsync(getName(), codec, EVAL_CONTAINS_VALUE,
checkValue(value);
return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN,
"local s = redis.call('hgetall', KEYS[1]); "
+ "for i, v in ipairs(s) do "
+ "if i % 2 == 0 then "
@ -175,7 +181,7 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
+ "end; "
+ "end;" +
"return 0;",
Arrays.<Object>asList(getName(), getTimeoutSetName(), getIdleSetName()), System.currentTimeMillis(), value);
Arrays.<Object>asList(getName(), getTimeoutSetName(), getIdleSetName()), System.currentTimeMillis(), encodeMapValue(value));
}
@Override
@ -244,6 +250,9 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
@Override
public RFuture<V> putIfAbsentAsync(K key, V value, long ttl, TimeUnit ttlUnit, long maxIdleTime, TimeUnit maxIdleUnit) {
checkKey(key);
checkValue(value);
if (ttl < 0) {
throw new IllegalArgumentException("ttl can't be negative");
}
@ -280,23 +289,21 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
+ "if value == false then "
+ "insertable = true; "
+ "else "
+ "if insertable == false then "
+ "local t, val = struct.unpack('dLc0', value); "
+ "local expireDate = 92233720368547758; "
+ "local expireDateScore = redis.call('zscore', KEYS[2], ARGV[5]); "
+ "if expireDateScore ~= false then "
+ "expireDate = tonumber(expireDateScore) "
+ "end; "
+ "if t ~= 0 then "
+ "local expireIdle = redis.call('zscore', KEYS[3], ARGV[5]); "
+ "if expireIdle ~= false then "
+ "expireDate = math.min(expireDate, tonumber(expireIdle)) "
+ "end; "
+ "end; "
+ "if expireDate <= tonumber(ARGV[1]) then "
+ "insertable = true; "
+ "end; "
+ "end; "
+ "local t, val = struct.unpack('dLc0', value); "
+ "local expireDate = 92233720368547758; "
+ "local expireDateScore = redis.call('zscore', KEYS[2], ARGV[5]); "
+ "if expireDateScore ~= false then "
+ "expireDate = tonumber(expireDateScore) "
+ "end; "
+ "if t ~= 0 then "
+ "local expireIdle = redis.call('zscore', KEYS[3], ARGV[5]); "
+ "if expireIdle ~= false then "
+ "expireDate = math.min(expireDate, tonumber(expireIdle)) "
+ "end; "
+ "end; "
+ "if expireDate <= tonumber(ARGV[1]) then "
+ "insertable = true; "
+ "end; "
+ "end; "
+ "if insertable == true then "
@ -315,21 +322,28 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
+ "end; "
// value
+ "local val = struct.pack('dLc0', ARGV[4], string.len(ARGV[6]), ARGV[6]); "
+ "local val = struct.pack('dLc0', tonumber(ARGV[4]), string.len(ARGV[6]), ARGV[6]); "
+ "redis.call('hset', KEYS[1], ARGV[5], val); "
+ "local msg = struct.pack('Lc0Lc0', string.len(ARGV[5]), ARGV[5], string.len(ARGV[6]), ARGV[6]); "
+ "redis.call('publish', KEYS[4], msg); "
+ "return nil;"
+ "else "
+ "local t, val = struct.unpack('dLc0', value); "
+ "redis.call('zadd', KEYS[3], t + ARGV[1], ARGV[5]); "
+ "return val;"
+ "end; ",
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key)), System.currentTimeMillis(), ttlTimeout, maxIdleTimeout, maxIdleDelta, key, value);
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key), getCreatedChannelName()),
System.currentTimeMillis(), ttlTimeout, maxIdleTimeout, maxIdleDelta, key, value);
}
@Override
public RFuture<Boolean> removeAsync(Object key, Object value) {
return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_REMOVE_VALUE,
checkKey(key);
checkValue(value);
return commandExecutor.evalWriteAsync(getName(key), codec, RedisCommands.EVAL_BOOLEAN,
"local value = redis.call('hget', KEYS[1], ARGV[1]); "
+ "if value == false then "
+ "return 0; "
@ -338,15 +352,21 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
+ "if val == ARGV[2] then "
+ "redis.call('zrem', KEYS[2], ARGV[1]); "
+ "redis.call('zrem', KEYS[3], ARGV[1]); "
+ "return redis.call('hdel', KEYS[1], ARGV[1]); "
+ "redis.call('hdel', KEYS[1], ARGV[1]); "
+ "local msg = struct.pack('Lc0Lc0', string.len(ARGV[1]), ARGV[1], string.len(val), val); "
+ "redis.call('publish', KEYS[4], msg); "
+ "return 1; "
+ "else "
+ "return 0 "
+ "return 0; "
+ "end",
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key)), key, value);
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key), getRemovedChannelName()),
encodeMapKey(key), encodeMapValue(value));
}
@Override
public RFuture<V> getAsync(K key) {
checkKey(key);
return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_GET_TTL,
"local value = redis.call('hget', KEYS[1], ARGV[2]); "
+ "if value == false then "
@ -383,23 +403,36 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
@Override
public RFuture<V> putAsync(K key, V value) {
checkKey(key);
checkValue(value);
return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_PUT,
"local v = redis.call('hget', KEYS[1], ARGV[1]); "
+ "local value = struct.pack('dLc0', 0, string.len(ARGV[2]), ARGV[2]); "
+ "redis.call('hset', KEYS[1], ARGV[1], value); "
+ "if v == false then "
+ "local msg = struct.pack('Lc0Lc0', string.len(ARGV[1]), ARGV[1], string.len(ARGV[2]), ARGV[2]); "
+ "redis.call('publish', KEYS[2], msg); "
+ "return nil; "
+ "end; "
+ "local t, val = struct.unpack('dLc0', v); "
+ "local msg = struct.pack('Lc0Lc0Lc0', string.len(ARGV[1]), ARGV[1], string.len(ARGV[2]), ARGV[2], string.len(val), val); "
+ "redis.call('publish', KEYS[3], msg); "
+ "return val; ",
Collections.<Object>singletonList(getName(key)), key, value);
Arrays.<Object>asList(getName(key), getCreatedChannelName(), getUpdatedChannelName()),
key, value);
}
@Override
public RFuture<V> putIfAbsentAsync(K key, V value) {
return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_PUT,
checkKey(key);
checkValue(value);
return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_PUT_IF_ABSENT,
"local value = struct.pack('dLc0', 0, string.len(ARGV[2]), ARGV[2]); "
+ "if redis.call('hsetnx', KEYS[1], ARGV[1], value) == 1 then "
+ "local msg = struct.pack('Lc0Lc0', string.len(ARGV[1]), ARGV[1], string.len(ARGV[2]), ARGV[2]); "
+ "redis.call('publish', KEYS[2], msg); "
+ "return nil;"
+ "else "
+ "local v = redis.call('hget', KEYS[1], ARGV[1]); "
@ -409,7 +442,8 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
+ "local t, val = struct.unpack('dLc0', v); "
+ "return val; "
+ "end",
Collections.<Object>singletonList(getName(key)), key, value);
Arrays.<Object>asList(getName(key), getCreatedChannelName()),
key, value);
}
@Override
@ -419,6 +453,9 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
@Override
public RFuture<V> addAndGetAsync(K key, Number value) {
checkKey(key);
checkValue(value);
byte[] keyState = encodeMapKey(key);
byte[] valueState;
try {
@ -452,14 +489,22 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
+ "end; "
+ "end; "
+ "end; "
+ "local newValue = tonumber(ARGV[3]); "
+ "if expireDate >= tonumber(ARGV[1]) then "
+ "if value ~= false and expireDate > tonumber(ARGV[1]) then "
+ "newValue = tonumber(val) + newValue; "
+ "local msg = struct.pack('Lc0Lc0Lc0', string.len(ARGV[2]), ARGV[2], string.len(newValue), newValue, string.len(val), val); "
+ "redis.call('publish', KEYS[5], msg); "
+ "else "
+ "local msg = struct.pack('Lc0Lc0', string.len(ARGV[2]), ARGV[2], string.len(ARGV[3]), ARGV[3]); "
+ "redis.call('publish', KEYS[4], msg); "
+ "end; "
+ "local newValuePack = struct.pack('dLc0', t + tonumber(ARGV[1]), string.len(newValue), newValue); "
+ "redis.call('hset', KEYS[1], ARGV[2], newValuePack); "
+ "return tostring(newValue); ",
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key)), System.currentTimeMillis(), keyState, valueState);
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key), getCreatedChannelName(), getUpdatedChannelName()),
System.currentTimeMillis(), keyState, valueState);
}
@Override
@ -479,6 +524,9 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
@Override
public RFuture<Boolean> fastPutAsync(K key, V value, long ttl, TimeUnit ttlUnit, long maxIdleTime, TimeUnit maxIdleUnit) {
checkKey(key);
checkValue(value);
if (ttl < 0) {
throw new IllegalArgumentException("ttl can't be negative");
}
@ -509,20 +557,53 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
maxIdleTimeout = System.currentTimeMillis() + maxIdleDelta;
}
return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_FAST_PUT_TTL,
"if tonumber(ARGV[1]) > 0 then "
+ "redis.call('zadd', KEYS[2], ARGV[1], ARGV[4]); "
return commandExecutor.evalWriteAsync(getName(key), codec, RedisCommands.EVAL_BOOLEAN,
"local insertable = false; "
+ "local value = redis.call('hget', KEYS[1], ARGV[5]); "
+ "local t, val;"
+ "if value == false then "
+ "insertable = true; "
+ "else "
+ "t, val = struct.unpack('dLc0', value); "
+ "local expireDate = 92233720368547758; "
+ "local expireDateScore = redis.call('zscore', KEYS[2], ARGV[5]); "
+ "if expireDateScore ~= false then "
+ "expireDate = tonumber(expireDateScore) "
+ "end; "
+ "if t ~= 0 then "
+ "local expireIdle = redis.call('zscore', KEYS[3], ARGV[5]); "
+ "if expireIdle ~= false then "
+ "expireDate = math.min(expireDate, tonumber(expireIdle)) "
+ "end; "
+ "end; "
+ "if expireDate <= tonumber(ARGV[1]) then "
+ "insertable = true; "
+ "end; "
+ "end; " +
"if tonumber(ARGV[2]) > 0 then "
+ "redis.call('zadd', KEYS[2], ARGV[2], ARGV[5]); "
+ "else "
+ "redis.call('zrem', KEYS[2], ARGV[4]); "
+ "redis.call('zrem', KEYS[2], ARGV[5]); "
+ "end; "
+ "if tonumber(ARGV[2]) > 0 then "
+ "redis.call('zadd', KEYS[3], ARGV[2], ARGV[4]); "
+ "if tonumber(ARGV[3]) > 0 then "
+ "redis.call('zadd', KEYS[3], ARGV[3], ARGV[5]); "
+ "else "
+ "redis.call('zrem', KEYS[3], ARGV[4]); "
+ "redis.call('zrem', KEYS[3], ARGV[5]); "
+ "end; "
+ "local value = struct.pack('dLc0', ARGV[3], string.len(ARGV[5]), ARGV[5]); " +
"return redis.call('hset', KEYS[1], ARGV[4], value); ",
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key)), ttlTimeout, maxIdleTimeout, maxIdleDelta, key, value);
+ "local value = struct.pack('dLc0', ARGV[4], string.len(ARGV[6]), ARGV[6]); "
+ "redis.call('hset', KEYS[1], ARGV[5], value); "
+ "if insertable == true then "
+ "local msg = struct.pack('Lc0Lc0', string.len(ARGV[5]), ARGV[5], string.len(ARGV[6]), ARGV[6]); "
+ "redis.call('publish', KEYS[4], msg); "
+ "return 1;"
+ "else "
+ "local msg = struct.pack('Lc0Lc0Lc0', string.len(ARGV[5]), ARGV[5], string.len(ARGV[6]), ARGV[6], string.len(val), val); "
+ "redis.call('publish', KEYS[5], msg); "
+ "return 0;"
+ "end;",
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key), getCreatedChannelName(), getUpdatedChannelName()),
System.currentTimeMillis(), ttlTimeout, maxIdleTimeout, maxIdleDelta, encodeMapKey(key), encodeMapValue(value));
}
@Override
@ -537,6 +618,9 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
@Override
public RFuture<V> putAsync(K key, V value, long ttl, TimeUnit ttlUnit, long maxIdleTime, TimeUnit maxIdleUnit) {
checkKey(key);
checkValue(value);
if (ttl < 0) {
throw new IllegalArgumentException("ttl can't be negative");
}
@ -568,25 +652,56 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
}
return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_PUT_TTL,
"local v = redis.call('hget', KEYS[1], ARGV[4]); "
+ "if tonumber(ARGV[1]) > 0 then "
+ "redis.call('zadd', KEYS[2], ARGV[1], ARGV[4]); "
"local insertable = false; "
+ "local v = redis.call('hget', KEYS[1], ARGV[5]); "
+ "if v == false then "
+ "insertable = true; "
+ "else "
+ "local t, val = struct.unpack('dLc0', v); "
+ "local expireDate = 92233720368547758; "
+ "local expireDateScore = redis.call('zscore', KEYS[2], ARGV[5]); "
+ "if expireDateScore ~= false then "
+ "expireDate = tonumber(expireDateScore) "
+ "end; "
+ "if t ~= 0 then "
+ "local expireIdle = redis.call('zscore', KEYS[3], ARGV[5]); "
+ "if expireIdle ~= false then "
+ "expireDate = math.min(expireDate, tonumber(expireIdle)) "
+ "end; "
+ "end; "
+ "if expireDate <= tonumber(ARGV[1]) then "
+ "insertable = true; "
+ "end; "
+ "end; "
+ "if tonumber(ARGV[2]) > 0 then "
+ "redis.call('zadd', KEYS[2], ARGV[2], ARGV[5]); "
+ "else "
+ "redis.call('zrem', KEYS[2], ARGV[4]); "
+ "redis.call('zrem', KEYS[2], ARGV[5]); "
+ "end; "
+ "if tonumber(ARGV[2]) > 0 then "
+ "redis.call('zadd', KEYS[3], ARGV[2], ARGV[4]); "
+ "if tonumber(ARGV[3]) > 0 then "
+ "redis.call('zadd', KEYS[3], ARGV[3], ARGV[5]); "
+ "else "
+ "redis.call('zrem', KEYS[3], ARGV[4]); "
+ "redis.call('zrem', KEYS[3], ARGV[5]); "
+ "end; "
+ "local value = struct.pack('dLc0', ARGV[3], string.len(ARGV[5]), ARGV[5]); "
+ "redis.call('hset', KEYS[1], ARGV[4], value); "
+ "if v == false then "
+ "local value = struct.pack('dLc0', ARGV[4], string.len(ARGV[6]), ARGV[6]); "
+ "redis.call('hset', KEYS[1], ARGV[5], value); "
+ "if insertable == true then "
+ "local msg = struct.pack('Lc0Lc0', string.len(ARGV[5]), ARGV[5], string.len(ARGV[6]), ARGV[6]); "
+ "redis.call('publish', KEYS[4], msg); "
+ "return nil;"
+ "end; "
+ "local t, val = struct.unpack('dLc0', v); "
+ "local msg = struct.pack('Lc0Lc0Lc0', string.len(ARGV[5]), ARGV[5], string.len(ARGV[6]), ARGV[6], string.len(val), val); "
+ "redis.call('publish', KEYS[5], msg); "
+ "return val",
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key)), ttlTimeout, maxIdleTimeout, maxIdleDelta, key, value);
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key), getCreatedChannelName(), getUpdatedChannelName()),
System.currentTimeMillis(), ttlTimeout, maxIdleTimeout, maxIdleDelta, key, value);
}
String getTimeoutSetNameByKey(Object key) {
@ -613,9 +728,43 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
return prefixName("redisson__idle__set", getName());
}
String getCreatedChannelName(String name) {
return prefixName("redisson_map_cache_created", name);
}
String getCreatedChannelName() {
return prefixName("redisson_map_cache_created", getName());
}
String getUpdatedChannelName(String name) {
return prefixName("redisson_map_cache_updated", name);
}
String getUpdatedChannelName() {
return prefixName("redisson_map_cache_updated", getName());
}
String getExpiredChannelName(String name) {
return prefixName("redisson_map_cache_expired", name);
}
String getExpiredChannelName() {
return prefixName("redisson_map_cache_expired", getName());
}
String getRemovedChannelName(String name) {
return prefixName("redisson_map_cache_removed", name);
}
String getRemovedChannelName() {
return prefixName("redisson_map_cache_removed", getName());
}
@Override
public RFuture<V> removeAsync(K key) {
checkKey(key);
return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_REMOVE,
"local v = redis.call('hget', KEYS[1], ARGV[1]); "
+ "redis.call('zrem', KEYS[2], ARGV[1]); "
@ -623,10 +772,13 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
+ "redis.call('hdel', KEYS[1], ARGV[1]); "
+ "if v ~= false then "
+ "local t, val = struct.unpack('dLc0', v); "
+ "local msg = struct.pack('Lc0Lc0', string.len(ARGV[1]), ARGV[1], string.len(val), val); "
+ "redis.call('publish', KEYS[4], msg); "
+ "return val; "
+ "end; "
+ "return v",
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key)), key);
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key), getRemovedChannelName()),
key);
}
@Override
@ -638,8 +790,17 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
return commandExecutor.evalWriteAsync(getName(), codec, EVAL_FAST_REMOVE,
"redis.call('zrem', KEYS[3], unpack(ARGV)); " +
"redis.call('zrem', KEYS[2], unpack(ARGV)); " +
"for i, key in ipairs(ARGV) do "
+ "local v = redis.call('hget', KEYS[1], key); "
+ "if v ~= false then "
+ "local t, val = struct.unpack('dLc0', v); "
+ "local msg = struct.pack('Lc0Lc0', string.len(key), key, string.len(val), val); "
+ "redis.call('publish', KEYS[4], msg); "
+ "end;" +
"end;" +
"return redis.call('hdel', KEYS[1], unpack(ARGV)); ",
Arrays.<Object>asList(getName(), getTimeoutSetName(), getIdleSetName()), keys);
Arrays.<Object>asList(getName(), getTimeoutSetName(), getIdleSetName(), getRemovedChannelName()),
keys);
}
@Override
@ -730,19 +891,61 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
@Override
public RFuture<Boolean> fastPutAsync(K key, V value) {
return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_HSET,
"local val = struct.pack('dLc0', 0, string.len(ARGV[2]), ARGV[2]); "
+ "return redis.call('hset', KEYS[1], ARGV[1], val); ",
Collections.<Object>singletonList(getName(key)), key, value);
checkKey(key);
checkValue(value);
return commandExecutor.evalWriteAsync(getName(key), codec, RedisCommands.EVAL_BOOLEAN,
"local insertable = false; "
+ "local v = redis.call('hget', KEYS[1], ARGV[2]); "
+ "if v == false then "
+ "insertable = true; "
+ "else "
+ "local t, val = struct.unpack('dLc0', v); "
+ "local expireDate = 92233720368547758; "
+ "local expireDateScore = redis.call('zscore', KEYS[2], ARGV[2]); "
+ "if expireDateScore ~= false then "
+ "expireDate = tonumber(expireDateScore) "
+ "end; "
+ "if t ~= 0 then "
+ "local expireIdle = redis.call('zscore', KEYS[3], ARGV[2]); "
+ "if expireIdle ~= false then "
+ "expireDate = math.min(expireDate, tonumber(expireIdle)) "
+ "end; "
+ "end; "
+ "if expireDate <= tonumber(ARGV[1]) then "
+ "insertable = true; "
+ "end; "
+ "end; " +
"local val = struct.pack('dLc0', 0, string.len(ARGV[3]), ARGV[3]); "
+ "redis.call('hset', KEYS[1], ARGV[2], val); "
+ "if insertable == true then "
+ "local msg = struct.pack('Lc0Lc0', string.len(ARGV[2]), ARGV[2], string.len(ARGV[3]), ARGV[3]); "
+ "redis.call('publish', KEYS[4], msg); "
+ "return 1;"
+ "else "
+ "local t, val = struct.unpack('dLc0', v); "
+ "local msg = struct.pack('Lc0Lc0Lc0', string.len(ARGV[2]), ARGV[2], string.len(ARGV[3]), ARGV[3], string.len(val), val); "
+ "redis.call('publish', KEYS[5], msg); "
+ "return 0;"
+ "end;",
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key), getCreatedChannelName(), getUpdatedChannelName()),
System.currentTimeMillis(), encodeMapKey(key), encodeMapValue(value));
}
@Override
public RFuture<Boolean> fastPutIfAbsentAsync(K key, V value) {
return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_PUT_IF_ABSENT,
checkKey(key);
checkValue(value);
return commandExecutor.evalWriteAsync(getName(key), codec, RedisCommands.EVAL_BOOLEAN,
"local value = redis.call('hget', KEYS[1], ARGV[2]); "
+ "if value == false then "
+ "local val = struct.pack('dLc0', 0, string.len(ARGV[3]), ARGV[3]); "
+ "redis.call('hset', KEYS[1], ARGV[2], val); "
+ "local msg = struct.pack('Lc0Lc0', string.len(ARGV[2]), ARGV[2], string.len(ARGV[3]), ARGV[3]); "
+ "redis.call('publish', KEYS[4], msg); "
+ "return 1; "
+ "end; "
+ "local t, val = struct.unpack('dLc0', value); "
@ -770,8 +973,12 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
+ "redis.call('zrem', KEYS[3], ARGV[2]); "
+ "local val = struct.pack('dLc0', 0, string.len(ARGV[3]), ARGV[3]); "
+ "redis.call('hset', KEYS[1], ARGV[2], val); "
+ "local msg = struct.pack('Lc0Lc0', string.len(ARGV[2]), ARGV[2], string.len(ARGV[3]), ARGV[3]); "
+ "redis.call('publish', KEYS[4], msg); "
+ "return 1; ",
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key)), System.currentTimeMillis(), key, value);
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key), getCreatedChannelName()),
System.currentTimeMillis(), encodeMapKey(key), encodeMapValue(value));
}
@Override
@ -786,6 +993,9 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
@Override
public RFuture<Boolean> fastPutIfAbsentAsync(K key, V value, long ttl, TimeUnit ttlUnit, long maxIdleTime, TimeUnit maxIdleUnit) {
checkKey(key);
checkValue(value);
if (ttl < 0) {
throw new IllegalArgumentException("ttl can't be negative");
}
@ -816,7 +1026,7 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
maxIdleTimeout = System.currentTimeMillis() + maxIdleDelta;
}
return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_FAST_PUT_TTL_IF_ABSENT,
return commandExecutor.evalWriteAsync(getName(key), codec, RedisCommands.EVAL_BOOLEAN,
"local insertable = false; "
+ "local value = redis.call('hget', KEYS[1], ARGV[5]); "
+ "if value == false then "
@ -860,16 +1070,28 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
+ "local val = struct.pack('dLc0', ARGV[4], string.len(ARGV[6]), ARGV[6]); "
+ "redis.call('hset', KEYS[1], ARGV[5], val); "
+ "local msg = struct.pack('Lc0Lc0', string.len(ARGV[5]), ARGV[5], string.len(ARGV[6]), ARGV[6]); "
+ "redis.call('publish', KEYS[4], msg); "
+ "return 1; "
+ "else "
+ "return 0; "
+ "end; ",
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key)), System.currentTimeMillis(), ttlTimeout, maxIdleTimeout, maxIdleDelta, key, value);
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key), getCreatedChannelName()),
System.currentTimeMillis(), ttlTimeout, maxIdleTimeout, maxIdleDelta, encodeMapKey(key), encodeMapValue(value));
}
@Override
public RFuture<Boolean> replaceAsync(K key, V oldValue, V newValue) {
return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_REPLACE_VALUE,
checkKey(key);
if (oldValue == null) {
throw new NullPointerException("map old value can't be null");
}
if (newValue == null) {
throw new NullPointerException("map new value can't be null");
}
return commandExecutor.evalWriteAsync(getName(key), codec, RedisCommands.EVAL_BOOLEAN,
"local v = redis.call('hget', KEYS[1], ARGV[2]); "
+ "if v == false then "
+ "return 0;"
@ -893,16 +1115,23 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
+ "end; "
+ "end; "
+ "if expireDate > tonumber(ARGV[1]) and val == ARGV[3] then "
+ "local msg = struct.pack('Lc0Lc0Lc0', string.len(ARGV[2]), ARGV[2], string.len(ARGV[4]), ARGV[4], string.len(ARGV[3]), ARGV[3]); "
+ "redis.call('publish', KEYS[4], msg); "
+ "local value = struct.pack('dLc0', t, string.len(ARGV[4]), ARGV[4]); "
+ "redis.call('hset', KEYS[1], ARGV[2], value); "
+ "return 1; "
+ "end; "
+ "return 0; ",
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key)), System.currentTimeMillis(), key, oldValue, newValue);
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key), getUpdatedChannelName()),
System.currentTimeMillis(), encodeMapKey(key), encodeMapValue(oldValue), encodeMapValue(newValue));
}
@Override
public RFuture<V> replaceAsync(K key, V value) {
checkKey(key);
checkValue(value);
return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_REPLACE,
"local v = redis.call('hget', KEYS[1], ARGV[2]); "
+ "if v ~= false then "
@ -912,11 +1141,16 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
+ "end; "
+ "local value = struct.pack('dLc0', t, string.len(ARGV[3]), ARGV[3]); "
+ "redis.call('hset', KEYS[1], ARGV[2], value); "
+ "local msg = struct.pack('Lc0Lc0Lc0', string.len(ARGV[2]), ARGV[2], string.len(ARGV[3]), ARGV[3], string.len(val), val); "
+ "redis.call('publish', KEYS[3], msg); "
+ "return val; "
+ "else "
+ "return nil; "
+ "end",
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key)), System.currentTimeMillis(), key, value);
Arrays.<Object>asList(getName(key), getTimeoutSetNameByKey(key), getUpdatedChannelName()),
System.currentTimeMillis(), key, value);
}
@Override
@ -927,6 +1161,13 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
List<Object> params = new ArrayList<Object>(map.size()*2);
for (java.util.Map.Entry<? extends K, ? extends V> t : map.entrySet()) {
if (t.getKey() == null) {
throw new NullPointerException("map key can't be null");
}
if (t.getValue() == null) {
throw new NullPointerException("map value can't be null");
}
params.add(t.getKey());
params.add(t.getValue());
}
@ -936,10 +1177,85 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
+ "if i % 2 == 0 then "
+ "local val = struct.pack('dLc0', 0, string.len(value), value); "
+ "ARGV[i] = val; "
+ "local key = ARGV[i-1];"
+ "local msg = struct.pack('Lc0Lc0', string.len(key), key, string.len(value), value); "
+ "redis.call('publish', KEYS[2], msg); "
+ "end;"
+ "end;"
+ "return redis.call('hmset', KEYS[1], unpack(ARGV)); ",
Collections.<Object>singletonList(getName()), params.toArray());
Arrays.<Object>asList(getName(), getCreatedChannelName()), params.toArray());
}
@Override
public int addListener(final MapEntryListener listener) {
if (listener == null) {
throw new NullPointerException();
}
if (listener instanceof EntryRemovedListener) {
RTopic<List<Object>> topic = redisson.getTopic(getRemovedChannelName(), new MapCacheEventCodec(codec));
return topic.addListener(new MessageListener<List<Object>>() {
@Override
public void onMessage(String channel, List<Object> msg) {
System.out.println("channel: " + channel);
System.out.println("msg: " + msg);
EntryEvent<K, V> event = new EntryEvent<K, V>(RedissonMapCache.this, EntryEvent.Type.REMOVED, (K)msg.get(0), (V)msg.get(1), null);
((EntryRemovedListener<K, V>) listener).onRemoved(event);
}
});
}
if (listener instanceof EntryCreatedListener) {
RTopic<List<Object>> topic = redisson.getTopic(getCreatedChannelName(), new MapCacheEventCodec(codec));
return topic.addListener(new MessageListener<List<Object>>() {
@Override
public void onMessage(String channel, List<Object> msg) {
EntryEvent<K, V> event = new EntryEvent<K, V>(RedissonMapCache.this, EntryEvent.Type.CREATED, (K)msg.get(0), (V)msg.get(1), null);
((EntryCreatedListener<K, V>) listener).onCreated(event);
}
});
}
if (listener instanceof EntryUpdatedListener) {
RTopic<List<Object>> topic = redisson.getTopic(getUpdatedChannelName(), new MapCacheEventCodec(codec));
return topic.addListener(new MessageListener<List<Object>>() {
@Override
public void onMessage(String channel, List<Object> msg) {
EntryEvent<K, V> event = new EntryEvent<K, V>(RedissonMapCache.this, EntryEvent.Type.UPDATED, (K)msg.get(0), (V)msg.get(1), (V)msg.get(2));
((EntryUpdatedListener<K, V>) listener).onUpdated(event);
}
});
}
if (listener instanceof EntryExpiredListener) {
RTopic<List<Object>> topic = redisson.getTopic(getExpiredChannelName(), new MapCacheEventCodec(codec));
return topic.addListener(new MessageListener<List<Object>>() {
@Override
public void onMessage(String channel, List<Object> msg) {
EntryEvent<K, V> event = new EntryEvent<K, V>(RedissonMapCache.this, EntryEvent.Type.EXPIRED, (K)msg.get(0), (V)msg.get(1), null);
((EntryExpiredListener<K, V>) listener).onExpired(event);
}
});
}
throw new IllegalArgumentException("Wrong listener type " + listener.getClass());
}
@Override
public void removeListener(int listenerId) {
RTopic<List<Object>> removedTopic = redisson.getTopic(getRemovedChannelName(), new MapCacheEventCodec(codec));
removedTopic.removeListener(listenerId);
RTopic<List<Object>> createdTopic = redisson.getTopic(getCreatedChannelName(), new MapCacheEventCodec(codec));
createdTopic.removeListener(listenerId);
RTopic<List<Object>> updatedTopic = redisson.getTopic(getUpdatedChannelName(), new MapCacheEventCodec(codec));
updatedTopic.removeListener(listenerId);
RTopic<List<Object>> expiredTopic = redisson.getTopic(getExpiredChannelName(), new MapCacheEventCodec(codec));
expiredTopic.removeListener(listenerId);
}
@Override

@ -117,6 +117,10 @@ public class RedissonReadLock extends RedissonLock implements RLock {
"redis.call('pexpire', KEYS[1], maxRemainTime); " +
"return 0; " +
"end;" +
"if mode == 'write' then " +
"return 0;" +
"end; " +
"end; " +
"redis.call('del', KEYS[1]); " +

@ -101,6 +101,12 @@ public class RedissonRemoteService extends BaseRemoteService implements RRemoteS
}
}
@Override
public int getFreeWorkers(Class<?> remoteInterface) {
Set<RFuture<RemoteServiceRequest>> futuresSet = futures.get(remoteInterface);
return futuresSet.size();
}
@Override
public <T> void register(Class<T> remoteInterface, T object, int workers) {
register(remoteInterface, object, workers, commandExecutor.getConnectionManager().getExecutor());

@ -144,6 +144,27 @@ public class RedissonScoredSortedSet<V> extends RedissonExpirable implements RSc
return commandExecutor.readAsync(getName(), codec, RedisCommands.ZRANGE_SINGLE, getName(), -1, -1);
}
@Override
public Double firstScore() {
return get(firstScoreAsync());
}
@Override
public RFuture<Double> firstScoreAsync() {
return commandExecutor.readAsync(getName(), codec, RedisCommands.ZRANGE_SINGLE_SCORE, getName(), 0, 0, "WITHSCORES");
}
@Override
public Double lastScore() {
return get(lastScoreAsync());
}
@Override
public RFuture<Double> lastScoreAsync() {
return commandExecutor.readAsync(getName(), codec, RedisCommands.ZRANGE_SINGLE_SCORE, getName(), -1, -1, "WITHSCORES");
}
@Override
public RFuture<Boolean> addAsync(double score, V object) {
return commandExecutor.writeAsync(getName(), codec, RedisCommands.ZADD_BOOL, getName(), BigDecimal.valueOf(score).toPlainString(), object);

@ -26,7 +26,7 @@ import java.util.Map;
*/
public interface Node extends NodeAsync {
enum InfoSection {ALL, DEFAULT, SERVER, CLIENTS, MEMORY, PERSISTENCE, STATS, REPLICATION, CPU, COMMANDSTATS, CLUSTER, KEYSPACE}
public enum InfoSection {ALL, DEFAULT, SERVER, CLIENTS, MEMORY, PERSISTENCE, STATS, REPLICATION, CPU, COMMANDSTATS, CLUSTER, KEYSPACE}
Map<String, String> info(InfoSection section);

@ -17,6 +17,6 @@ package org.redisson.api;
public enum NodeType {
MASTER, SLAVE
MASTER, SLAVE, SENTINEL
}

@ -43,7 +43,7 @@ public interface NodesGroup<N extends Node> {
void removeConnectionListener(int listenerId);
/**
* Get Redis node by address in format: <code>host:port</code>
* Get Redis node by address in format: <code>redis://host:port</code>
*
* @param address of node
* @return node

@ -44,4 +44,6 @@ public interface RBlockingQueue<V> extends BlockingQueue<V>, RQueue<V>, RBlockin
V pollLastAndOfferFirstTo(String queueName, long timeout, TimeUnit unit) throws InterruptedException;
V takeLastAndOfferFirstTo(String queueName) throws InterruptedException;
}

@ -94,6 +94,8 @@ public interface RBlockingQueueAsync<V> extends RQueueAsync<V> {
RFuture<V> pollLastAndOfferFirstToAsync(String queueName, long timeout, TimeUnit unit);
RFuture<V> takeLastAndOfferFirstToAsync(String queueName);
/**
* Retrieves and removes the head of this queue in async mode, waiting up to the
* specified wait time if necessary for an element to become available.

@ -445,4 +445,104 @@ public interface RGeo<V> extends RScoredSortedSet<V>, RGeoAsync<V> {
*/
Map<V, GeoPosition> radiusWithPosition(V member, double radius, GeoUnit geoUnit, GeoOrder geoOrder, int count);
/**
* Finds 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.
* Store result to <code>destName</code>.
*
* @param destName - Geo object destination
* @param longitude - longitude of object
* @param latitude - latitude of object
* @param radius - radius in geo units
* @param geoUnit - geo unit
* @return length of result
*/
long radiusStoreTo(String destName, double longitude, double latitude, double radius, GeoUnit geoUnit);
/**
* Finds 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 and limited by count
* Store result to <code>destName</code>.
*
* @param destName - Geo object destination
* @param longitude - longitude of object
* @param latitude - latitude of object
* @param radius - radius in geo units
* @param geoUnit - geo unit
* @param count - result limit
* @return length of result
*/
long radiusStoreTo(String destName, double longitude, double latitude, double radius, GeoUnit geoUnit, int count);
/**
* Finds 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 with <code>GeoOrder</code>
* and limited by count
* Store result to <code>destName</code>.
*
* @param destName - Geo object destination
* @param longitude - longitude of object
* @param latitude - latitude of object
* @param radius - radius in geo units
* @param geoUnit - geo unit
* @param geoOrder - order of result
* @param count - result limit
* @return length of result
*/
long radiusStoreTo(String destName, double longitude, double latitude, double radius, GeoUnit geoUnit, GeoOrder geoOrder, int count);
/**
* Finds 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.
* Store result to <code>destName</code>.
*
* @param destName - Geo object destination
* @param member - object
* @param radius - radius in geo units
* @param geoUnit - geo unit
* @return length of result
*/
long radiusStoreTo(String destName, V member, double radius, GeoUnit geoUnit);
/**
* Finds 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 and limited by count
* Store result to <code>destName</code>.
*
* @param destName - Geo object destination
* @param member - object
* @param radius - radius in geo units
* @param geoUnit - geo unit
* @param count - result limit
* @return length of result
*/
long radiusStoreTo(String destName, V member, double radius, GeoUnit geoUnit, int count);
/**
* Finds 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 with <code>GeoOrder</code>
* Store result to <code>destName</code>.
*
* @param destName - Geo object destination
* @param member - object
* @param radius - radius in geo units
* @param geoUnit - geo unit
* @param geoOrder - geo order
* @param count - result limit
* @return length of result
*/
long radiusStoreTo(String destName, V member, double radius, GeoUnit geoUnit, GeoOrder geoOrder, int count);
}

@ -443,4 +443,104 @@ public interface RGeoAsync<V> extends RScoredSortedSetAsync<V> {
*/
RFuture<Map<V, GeoPosition>> radiusWithPositionAsync(V member, double radius, GeoUnit geoUnit, GeoOrder geoOrder, int count);
/**
* Finds 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.
* Store result to <code>destName</code>.
*
* @param destName - Geo object destination
* @param longitude - longitude of object
* @param latitude - latitude of object
* @param radius - radius in geo units
* @param geoUnit - geo unit
* @return length of result
*/
RFuture<Long> radiusStoreToAsync(String destName, double longitude, double latitude, double radius, GeoUnit geoUnit);
/**
* Finds 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 and limited by count
* Store result to <code>destName</code>.
*
* @param destName - Geo object destination
* @param longitude - longitude of object
* @param latitude - latitude of object
* @param radius - radius in geo units
* @param geoUnit - geo unit
* @param count - result limit
* @return length of result
*/
RFuture<Long> radiusStoreToAsync(String destName, double longitude, double latitude, double radius, GeoUnit geoUnit, int count);
/**
* Finds 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 with <code>GeoOrder</code>
* and limited by count
* Store result to <code>destName</code>.
*
* @param destName - Geo object destination
* @param longitude - longitude of object
* @param latitude - latitude of object
* @param radius - radius in geo units
* @param geoUnit - geo unit
* @param geoOrder - order of result
* @param count - result limit
* @return length of result
*/
RFuture<Long> radiusStoreToAsync(String destName, double longitude, double latitude, double radius, GeoUnit geoUnit, GeoOrder geoOrder, int count);
/**
* Finds 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.
* Store result to <code>destName</code>.
*
* @param destName - Geo object destination
* @param member - object
* @param radius - radius in geo units
* @param geoUnit - geo unit
* @return length of result
*/
RFuture<Long> radiusStoreToAsync(String destName, V member, double radius, GeoUnit geoUnit);
/**
* Finds 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 and limited by count
* Store result to <code>destName</code>.
*
* @param destName - Geo object destination
* @param member - object
* @param radius - radius in geo units
* @param geoUnit - geo unit
* @param count - result limit
* @return length of result
*/
RFuture<Long> radiusStoreToAsync(String destName, V member, double radius, GeoUnit geoUnit, int count);
/**
* Finds 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 with <code>GeoOrder</code>
* Store result to <code>destName</code>.
*
* @param destName - Geo object destination
* @param member - object
* @param radius - radius in geo units
* @param geoUnit - geo unit
* @param geoOrder - geo order
* @param count - result limit
* @return length of result
*/
RFuture<Long> radiusStoreToAsync(String destName, V member, double radius, GeoUnit geoUnit, GeoOrder geoOrder, int count);
}

@ -41,6 +41,7 @@ public interface RKeysAsync {
* @param host - destination host
* @param port - destination port
* @param database - destination database
* @return void
*/
RFuture<Void> migrateAsync(String name, String host, int port, int database);
@ -89,6 +90,7 @@ public interface RKeysAsync {
*
* @param currentName - current name of object
* @param newName - new name of object
* @return void
*/
RFuture<Void> renameAsync(String currentName, String newName);

@ -27,5 +27,10 @@ package org.redisson.api;
* @param <V> map value
*/
public interface RLocalCachedMap<K, V> extends RMap<K, V>, RDestroyable {
/**
* Pre-warm the cached values. Not guaranteed to load ALL values, but statistically
* will preload approximately all (all if no concurrent mutating activity)
* Intended for use with no-eviction caches where entire maps are locally cached
*/
public void preloadCache();
}

@ -17,6 +17,8 @@ package org.redisson.api;
import java.util.concurrent.TimeUnit;
import org.redisson.api.map.event.MapEntryListener;
/**
* <p>Map-based cache with ability to set TTL for each entry via
* {@link #put(Object, Object, long, TimeUnit)} or {@link #putIfAbsent(Object, Object, long, TimeUnit)}
@ -130,6 +132,7 @@ public interface RMapCache<K, V> extends RMap<K, V>, RMapCacheAsync<K, V> {
* @param ttl - time to live for key\value entry.
* If <code>0</code> then stores infinitely.
* @param ttlUnit - time unit
*
* @return <code>true</code> if key is a new key in the hash and value was set.
* <code>false</code> if key already exists in the hash and the value was updated.
*/
@ -177,6 +180,7 @@ public interface RMapCache<K, V> extends RMap<K, V>, RMapCacheAsync<K, V> {
* @param ttl - time to live for key\value entry.
* If <code>0</code> then stores infinitely.
* @param ttlUnit - time unit
*
* @return <code>true</code> if key is a new key in the hash and value was set.
* <code>false</code> if key already exists in the hash
*/
@ -218,4 +222,24 @@ public interface RMapCache<K, V> extends RMap<K, V>, RMapCacheAsync<K, V> {
@Override
int size();
/**
* Adds map entry listener
*
* @see org.redisson.api.map.event.EntryCreatedListener
* @see org.redisson.api.map.event.EntryUpdatedListener
* @see org.redisson.api.map.event.EntryRemovedListener
* @see org.redisson.api.map.event.EntryExpiredListener
*
* @param listener - entry listener
* @return listener id
*/
int addListener(MapEntryListener listener);
/**
* Removes map entry listener
*
* @param listenerId - listener id
*/
void removeListener(int listenerId);
}

@ -134,7 +134,9 @@ public interface RMapCacheAsync<K, V> extends RMapAsync<K, V> {
* @param ttl - time to live for key\value entry.
* If <code>0</code> then stores infinitely.
* @param unit - time unit
* @return <code>true</code> if value has been set successfully
*
* @return <code>true</code> if key is a new key in the hash and value was set.
* <code>false</code> if key already exists in the hash and the value was updated.
*/
RFuture<Boolean> fastPutAsync(K key, V value, long ttl, TimeUnit unit);
@ -160,7 +162,8 @@ public interface RMapCacheAsync<K, V> extends RMapAsync<K, V> {
* if <code>maxIdleTime</code> and <code>ttl</code> params are equal to <code>0</code>
* then entry stores infinitely.
* @return <code>true</code> if value has been set successfully
* @return <code>true</code> if key is a new key in the hash and value was set.
* <code>false</code> if key already exists in the hash and the value was updated.
*/
RFuture<Boolean> fastPutAsync(K key, V value, long ttl, TimeUnit ttlUnit, long maxIdleTime, TimeUnit maxIdleUnit);
@ -186,7 +189,8 @@ public interface RMapCacheAsync<K, V> extends RMapAsync<K, V> {
* if <code>maxIdleTime</code> and <code>ttl</code> params are equal to <code>0</code>
* then entry stores infinitely.
*
* @return previous associated value
* @return <code>true</code> if key is a new key in the hash and value was set.
* <code>false</code> if key already exists in the hash
*/
RFuture<Boolean> fastPutIfAbsentAsync(K key, V value, long ttl, TimeUnit ttlUnit, long maxIdleTime, TimeUnit maxIdleUnit);

@ -57,6 +57,14 @@ import java.util.concurrent.TimeUnit;
*/
public interface RRemoteService {
/**
* Returns free workers amount available for tasks
*
* @param remoteInterface - remote service interface
* @return workers amount
*/
int getFreeWorkers(Class<?> remoteInterface);
/**
* Register remote service with single worker
*

@ -53,6 +53,10 @@ public interface RScoredSortedSet<V> extends RScoredSortedSetAsync<V>, Iterable<
V last();
Double firstScore();
Double lastScore();
Long addAll(Map<V, Double> objects);
int removeRangeByScore(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive);

@ -38,6 +38,10 @@ public interface RScoredSortedSetAsync<V> extends RExpirableAsync, RSortableAsyn
RFuture<V> lastAsync();
RFuture<Double> firstScoreAsync();
RFuture<Double> lastScoreAsync();
RFuture<Long> addAllAsync(Map<V, Double> objects);
RFuture<Integer> removeRangeByScoreAsync(double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive);

@ -0,0 +1,29 @@
/**
* Copyright 2016 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 org.redisson.api.map.event;
/**
*
* @author Nikita Koksharov
*
* @param <K> key type
* @param <V> value type
*/
public interface EntryCreatedListener<K, V> extends MapEntryListener {
void onCreated(EntryEvent<K, V> event);
}

@ -0,0 +1,66 @@
/**
* Copyright 2016 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 org.redisson.api.map.event;
import org.redisson.api.RMapCache;
/**
*
* @author Nikita Koksharov
*
* @param <K> key type
* @param <V> value type
*/
public class EntryEvent<K, V> {
public enum Type {CREATED, UPDATED, REMOVED, EXPIRED}
private RMapCache<K, V> source;
private Type type;
private K key;
private V value;
private V oldValue;
public EntryEvent(RMapCache<K, V> source, Type type, K key, V value, V oldValue) {
super();
this.source = source;
this.type = type;
this.key = key;
this.value = value;
this.oldValue = oldValue;
}
public RMapCache<K, V> getSource() {
return source;
}
public Type getType() {
return type;
}
public K getKey() {
return key;
}
public V getOldValue() {
return oldValue;
}
public V getValue() {
return value;
}
}

@ -0,0 +1,29 @@
/**
* Copyright 2016 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 org.redisson.api.map.event;
/**
*
* @author Nikita Koksharov
*
* @param <K> key type
* @param <V> value type
*/
public interface EntryExpiredListener<K, V> extends MapEntryListener {
void onExpired(EntryEvent<K, V> event);
}

@ -0,0 +1,29 @@
/**
* Copyright 2016 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 org.redisson.api.map.event;
/**
*
* @author Nikita Koksharov
*
* @param <K> key type
* @param <V> value type
*/
public interface EntryRemovedListener<K, V> extends MapEntryListener {
void onRemoved(EntryEvent<K, V> event);
}

@ -0,0 +1,29 @@
/**
* Copyright 2016 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 org.redisson.api.map.event;
/**
*
* @author Nikita Koksharov
*
* @param <K> key type
* @param <V> value type
*/
public interface EntryUpdatedListener<K, V> extends MapEntryListener {
void onUpdated(EntryEvent<K, V> event);
}

@ -13,15 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.connection;
package org.redisson.api.map.event;
import org.redisson.api.NodeType;
import org.redisson.client.RedisConnection;
import org.redisson.config.MasterSlaveServersConfig;
import org.redisson.misc.RPromise;
import java.util.EventListener;
public interface ConnectionInitializer {
<T extends RedisConnection> void onConnect(RPromise<T> connectionFuture, T conn, NodeType nodeType, MasterSlaveServersConfig config);
/**
*
* @author Nikita Koksharov
*
*/
public interface MapEntryListener extends EventListener {
}

@ -93,8 +93,8 @@ public interface RCollectionMapReduce<VIn, KOut, VOut> extends RMapReduceExecuto
/**
* Defines timeout for MapReduce process
*
* @param timeout
* @param unit
* @param timeout for process
* @param unit of timeout
* @return self instance
*/
RCollectionMapReduce<VIn, KOut, VOut> timeout(long timeout, TimeUnit unit);

@ -88,8 +88,8 @@ public interface RMapReduce<KIn, VIn, KOut, VOut> extends RMapReduceExecutor<VIn
* Defines timeout for MapReduce process.
* <code>0</code> means infinity timeout.
*
* @param timeout
* @param unit
* @param timeout for process
* @param unit of timeout
* @return self instance
*/
RMapReduce<KIn, VIn, KOut, VOut> timeout(long timeout, TimeUnit unit);

@ -16,28 +16,21 @@
package org.redisson.client;
import java.net.InetSocketAddress;
import java.net.URL;
import java.util.Map;
import java.net.URI;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.redisson.api.RFuture;
import org.redisson.client.handler.CommandBatchEncoder;
import org.redisson.client.handler.CommandDecoder;
import org.redisson.client.handler.CommandEncoder;
import org.redisson.client.handler.CommandsQueue;
import org.redisson.client.handler.ConnectionWatchdog;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.client.handler.RedisChannelInitializer;
import org.redisson.client.handler.RedisChannelInitializer.Type;
import org.redisson.misc.RPromise;
import org.redisson.misc.RedissonPromise;
import org.redisson.misc.URLBuilder;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.group.ChannelGroup;
@ -51,6 +44,7 @@ import io.netty.util.Timer;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.redisson.misc.URIBuilder;
/**
* Low-level Redis client
@ -61,6 +55,7 @@ import io.netty.util.concurrent.GlobalEventExecutor;
public class RedisClient {
private final Bootstrap bootstrap;
private final Bootstrap pubSubBootstrap;
private final InetSocketAddress addr;
private final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
@ -68,55 +63,116 @@ public class RedisClient {
private final long commandTimeout;
private Timer timer;
private boolean hasOwnGroup;
private RedisClientConfig config;
public static RedisClient create(RedisClientConfig config) {
if (config.getTimer() == null) {
config.setTimer(new HashedWheelTimer());
}
return new RedisClient(config);
}
private RedisClient(RedisClientConfig config) {
this.config = config;
this.executor = config.getExecutor();
this.timer = config.getTimer();
addr = new InetSocketAddress(config.getAddress().getHost(), config.getAddress().getPort());
bootstrap = createBootstrap(config, Type.PLAIN);
pubSubBootstrap = createBootstrap(config, Type.PUBSUB);
this.commandTimeout = config.getCommandTimeout();
}
private Bootstrap createBootstrap(RedisClientConfig config, Type type) {
Bootstrap bootstrap = new Bootstrap()
.channel(config.getSocketChannelClass())
.group(config.getGroup())
.remoteAddress(addr);
bootstrap.handler(new RedisChannelInitializer(bootstrap, config, this, channels, type));
bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, config.getConnectTimeout());
return bootstrap;
}
/*
* Use {@link #create(RedisClientConfig)}
*
*/
@Deprecated
public RedisClient(String address) {
this(URLBuilder.create(address));
this(URIBuilder.create(address));
}
public RedisClient(URL address) {
/*
* Use {@link #create(RedisClientConfig)}
*
*/
@Deprecated
public RedisClient(URI address) {
this(new HashedWheelTimer(), Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2), new NioEventLoopGroup(), address);
hasOwnGroup = true;
}
public RedisClient(Timer timer, ExecutorService executor, EventLoopGroup group, URL address) {
/*
* Use {@link #create(RedisClientConfig)}
*
*/
@Deprecated
public RedisClient(Timer timer, ExecutorService executor, EventLoopGroup group, URI address) {
this(timer, executor, group, address.getHost(), address.getPort());
}
/*
* Use {@link #create(RedisClientConfig)}
*
*/
@Deprecated
public RedisClient(String host, int port) {
this(new HashedWheelTimer(), Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2), new NioEventLoopGroup(), NioSocketChannel.class, host, port, 10000, 10000);
hasOwnGroup = true;
}
/*
* Use {@link #create(RedisClientConfig)}
*
*/
@Deprecated
public RedisClient(Timer timer, ExecutorService executor, EventLoopGroup group, String host, int port) {
this(timer, executor, group, NioSocketChannel.class, host, port, 10000, 10000);
}
/*
* Use {@link #create(RedisClientConfig)}
*
*/
@Deprecated
public RedisClient(String host, int port, int connectTimeout, int commandTimeout) {
this(new HashedWheelTimer(), Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2), new NioEventLoopGroup(), NioSocketChannel.class, host, port, connectTimeout, commandTimeout);
}
/*
* Use {@link #create(RedisClientConfig)}
*
*/
@Deprecated
public RedisClient(final Timer timer, ExecutorService executor, EventLoopGroup group, Class<? extends SocketChannel> socketChannelClass, String host, int port,
int connectTimeout, int commandTimeout) {
if (timer == null) {
throw new NullPointerException("timer param can't be null");
}
this.executor = executor;
this.timer = timer;
addr = new InetSocketAddress(host, port);
bootstrap = new Bootstrap().channel(socketChannelClass).group(group).remoteAddress(addr);
bootstrap.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addFirst(new ConnectionWatchdog(bootstrap, channels, timer),
CommandEncoder.INSTANCE,
CommandBatchEncoder.INSTANCE,
new CommandsQueue(),
new CommandDecoder(RedisClient.this.executor));
}
});
RedisClientConfig config = new RedisClientConfig();
config.setTimer(timer).setExecutor(executor).setGroup(group).setSocketChannelClass(socketChannelClass)
.setAddress(host, port).setConnectTimeout(connectTimeout).setCommandTimeout(commandTimeout);
this.config = config;
this.executor = config.getExecutor();
this.timer = config.getTimer();
addr = new InetSocketAddress(config.getAddress().getHost(), config.getAddress().getPort());
bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeout);
this.commandTimeout = commandTimeout;
bootstrap = createBootstrap(config, Type.PLAIN);
pubSubBootstrap = createBootstrap(config, Type.PUBSUB);
this.commandTimeout = config.getCommandTimeout();
}
@ -128,15 +184,17 @@ public class RedisClient {
return commandTimeout;
}
public Bootstrap getBootstrap() {
return bootstrap;
public EventLoopGroup getEventLoopGroup() {
return bootstrap.config().group();
}
public RedisClientConfig getConfig() {
return config;
}
public RedisConnection connect() {
try {
ChannelFuture future = bootstrap.connect();
future.syncUninterruptibly();
return new RedisConnection(this, future.channel());
return connectAsync().syncUninterruptibly().getNow();
} catch (Exception e) {
throw new RedisConnectionException("Unable to connect to: " + addr, e);
}
@ -149,16 +207,27 @@ public class RedisClient {
@Override
public void operationComplete(final ChannelFuture future) throws Exception {
if (future.isSuccess()) {
final RedisConnection c = new RedisConnection(RedisClient.this, future.channel());
bootstrap.group().execute(new Runnable() {
public void run() {
if (!f.trySuccess(c)) {
c.closeAsync();
}
final RedisConnection c = RedisConnection.getFrom(future.channel());
c.getConnectionPromise().addListener(new FutureListener<RedisConnection>() {
@Override
public void operationComplete(final Future<RedisConnection> future) throws Exception {
bootstrap.config().group().execute(new Runnable() {
@Override
public void run() {
if (future.isSuccess()) {
if (!f.trySuccess(c)) {
c.closeAsync();
}
} else {
f.tryFailure(future.cause());
c.closeAsync();
}
}
});
}
});
} else {
bootstrap.group().execute(new Runnable() {
bootstrap.config().group().execute(new Runnable() {
public void run() {
f.tryFailure(future.cause());
}
@ -171,9 +240,7 @@ public class RedisClient {
public RedisPubSubConnection connectPubSub() {
try {
ChannelFuture future = bootstrap.connect();
future.syncUninterruptibly();
return new RedisPubSubConnection(this, future.channel());
return connectPubSubAsync().syncUninterruptibly().getNow();
} catch (Exception e) {
throw new RedisConnectionException("Unable to connect to: " + addr, e);
}
@ -181,21 +248,32 @@ public class RedisClient {
public RFuture<RedisPubSubConnection> connectPubSubAsync() {
final RPromise<RedisPubSubConnection> f = new RedissonPromise<RedisPubSubConnection>();
ChannelFuture channelFuture = bootstrap.connect();
ChannelFuture channelFuture = pubSubBootstrap.connect();
channelFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(final ChannelFuture future) throws Exception {
if (future.isSuccess()) {
final RedisPubSubConnection c = new RedisPubSubConnection(RedisClient.this, future.channel());
bootstrap.group().execute(new Runnable() {
public void run() {
if (!f.trySuccess(c)) {
c.closeAsync();
}
final RedisPubSubConnection c = RedisPubSubConnection.getFrom(future.channel());
c.<RedisPubSubConnection>getConnectionPromise().addListener(new FutureListener<RedisPubSubConnection>() {
@Override
public void operationComplete(final Future<RedisPubSubConnection> future) throws Exception {
bootstrap.config().group().execute(new Runnable() {
@Override
public void run() {
if (future.isSuccess()) {
if (!f.trySuccess(c)) {
c.closeAsync();
}
} else {
f.tryFailure(future.cause());
c.closeAsync();
}
}
});
}
});
} else {
bootstrap.group().execute(new Runnable() {
bootstrap.config().group().execute(new Runnable() {
public void run() {
f.tryFailure(future.cause());
}
@ -216,7 +294,7 @@ public class RedisClient {
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
bootstrap.group().shutdownGracefully();
bootstrap.config().group().shutdownGracefully();
}
}
@ -231,28 +309,6 @@ public class RedisClient {
return channels.close();
}
@Deprecated
public Map<String, String> serverInfo() {
try {
return serverInfoAsync().sync().get();
} catch (Exception e) {
throw new RedisConnectionException("Unable to retrieve server into from: " + addr, e);
}
}
@Deprecated
public RFuture<Map<String, String>> serverInfoAsync() {
final RedisConnection connection = connect();
RFuture<Map<String, String>> async = connection.async(RedisCommands.INFO_SERVER);
async.addListener(new FutureListener<Map<String, String>>() {
@Override
public void operationComplete(Future<Map<String, String>> future) throws Exception {
connection.closeAsync();
}
});
return async;
}
@Override
public String toString() {
return "[addr=" + addr + "]";

@ -0,0 +1,206 @@
/**
* Copyright 2016 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 org.redisson.client;
import java.net.URI;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.redisson.config.SslProvider;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.Timer;
import org.redisson.misc.URIBuilder;
/**
*
* @author Nikita Koksharov
*
*/
public class RedisClientConfig {
private URI address;
private Timer timer;
private ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
private EventLoopGroup group = new NioEventLoopGroup();
private Class<? extends SocketChannel> socketChannelClass = NioSocketChannel.class;
private int connectTimeout = 10000;
private int commandTimeout = 10000;
private String password;
private int database;
private String clientName;
private boolean readOnly;
private boolean sslEnableEndpointIdentification = true;
private SslProvider sslProvider = SslProvider.JDK;
private URI sslTruststore;
private String sslTruststorePassword;
private URI sslKeystore;
private String sslKeystorePassword;
public RedisClientConfig setAddress(String host, int port) {
this.address = URIBuilder.create("redis://" + host + ":" + port);
return this;
}
public RedisClientConfig setAddress(String address) {
this.address = URIBuilder.create(address);
return this;
}
public RedisClientConfig setAddress(URI address) {
this.address = address;
return this;
}
public URI getAddress() {
return address;
}
public Timer getTimer() {
return timer;
}
public RedisClientConfig setTimer(Timer timer) {
this.timer = timer;
return this;
}
public ExecutorService getExecutor() {
return executor;
}
public RedisClientConfig setExecutor(ExecutorService executor) {
this.executor = executor;
return this;
}
public EventLoopGroup getGroup() {
return group;
}
public RedisClientConfig setGroup(EventLoopGroup group) {
this.group = group;
return this;
}
public Class<? extends SocketChannel> getSocketChannelClass() {
return socketChannelClass;
}
public RedisClientConfig setSocketChannelClass(Class<? extends SocketChannel> socketChannelClass) {
this.socketChannelClass = socketChannelClass;
return this;
}
public int getConnectTimeout() {
return connectTimeout;
}
public RedisClientConfig setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
return this;
}
public int getCommandTimeout() {
return commandTimeout;
}
public RedisClientConfig setCommandTimeout(int commandTimeout) {
this.commandTimeout = commandTimeout;
return this;
}
public SslProvider getSslProvider() {
return sslProvider;
}
public RedisClientConfig setSslProvider(SslProvider sslMode) {
this.sslProvider = sslMode;
return this;
}
public URI getSslTruststore() {
return sslTruststore;
}
public RedisClientConfig setSslTruststore(URI sslTruststore) {
this.sslTruststore = sslTruststore;
return this;
}
public URI getSslKeystore() {
return sslKeystore;
}
public RedisClientConfig setSslKeystore(URI sslKeystore) {
this.sslKeystore = sslKeystore;
return this;
}
public String getSslKeystorePassword() {
return sslKeystorePassword;
}
public RedisClientConfig setSslKeystorePassword(String sslKeystorePassword) {
this.sslKeystorePassword = sslKeystorePassword;
return this;
}
public String getSslTruststorePassword() {
return sslTruststorePassword;
}
public RedisClientConfig setSslTruststorePassword(String sslTruststorePassword) {
this.sslTruststorePassword = sslTruststorePassword;
return this;
}
public boolean isSslEnableEndpointIdentification() {
return sslEnableEndpointIdentification;
}
public RedisClientConfig setSslEnableEndpointIdentification(boolean enableEndpointIdentification) {
this.sslEnableEndpointIdentification = enableEndpointIdentification;
return this;
}
public String getPassword() {
return password;
}
public RedisClientConfig setPassword(String password) {
this.password = password;
return this;
}
public int getDatabase() {
return database;
}
public RedisClientConfig setDatabase(int database) {
this.database = database;
return this;
}
public String getClientName() {
return clientName;
}
public RedisClientConfig setClientName(String clientName) {
this.clientName = clientName;
return this;
}
public boolean isReadOnly() {
return readOnly;
}
public RedisClientConfig setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
return this;
}
}

@ -15,6 +15,8 @@
*/
package org.redisson.client;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@ -25,6 +27,7 @@ import org.redisson.client.handler.CommandsQueue;
import org.redisson.client.protocol.CommandData;
import org.redisson.client.protocol.CommandsData;
import org.redisson.client.protocol.QueueCommand;
import org.redisson.client.protocol.QueueCommandHolder;
import org.redisson.client.protocol.RedisCommand;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.client.protocol.RedisStrictCommand;
@ -49,15 +52,16 @@ public class RedisConnection implements RedisCommands {
final RedisClient redisClient;
private volatile boolean fastReconnect;
private volatile RPromise<Void> fastReconnect;
private volatile boolean closed;
volatile Channel channel;
private ReconnectListener reconnectListener;
private RPromise<?> connectionPromise;
private long lastUsageTime;
public RedisConnection(RedisClient redisClient, Channel channel) {
this(redisClient);
public <C> RedisConnection(RedisClient redisClient, Channel channel, RPromise<C> connectionPromise) {
this.redisClient = redisClient;
this.connectionPromise = connectionPromise;
updateChannel(channel);
lastUsageTime = System.currentTimeMillis();
@ -67,6 +71,10 @@ public class RedisConnection implements RedisCommands {
this.redisClient = redisClient;
}
public <C extends RedisConnection> RPromise<C> getConnectionPromise() {
return (RPromise<C>) connectionPromise;
}
public static <C extends RedisConnection> C getFrom(Channel channel) {
return (C) channel.attr(RedisConnection.CONNECTION).get();
}
@ -87,14 +95,6 @@ public class RedisConnection implements RedisCommands {
this.lastUsageTime = lastUsageTime;
}
public void setReconnectListener(ReconnectListener reconnectListener) {
this.reconnectListener = reconnectListener;
}
public ReconnectListener getReconnectListener() {
return reconnectListener;
}
public boolean isOpen() {
return channel.isOpen();
}
@ -182,12 +182,12 @@ public class RedisConnection implements RedisCommands {
timeout = redisClient.getCommandTimeout();
}
if (redisClient.getBootstrap().group().isShuttingDown()) {
if (redisClient.getEventLoopGroup().isShuttingDown()) {
RedissonShutdownException cause = new RedissonShutdownException("Redisson is shutdown");
return RedissonPromise.newFailedFuture(cause);
}
final ScheduledFuture<?> scheduledFuture = redisClient.getBootstrap().group().next().schedule(new Runnable() {
final ScheduledFuture<?> scheduledFuture = redisClient.getEventLoopGroup().schedule(new Runnable() {
@Override
public void run() {
RedisTimeoutException ex = new RedisTimeoutException("Command execution timeout for " + redisClient.getAddr());
@ -219,16 +219,18 @@ public class RedisConnection implements RedisCommands {
}
public boolean isFastReconnect() {
return fastReconnect;
return fastReconnect != null;
}
public void clearFastReconnect() {
fastReconnect = false;
fastReconnect.trySuccess(null);
fastReconnect = null;
}
public ChannelFuture forceFastReconnectAsync() {
fastReconnect = true;
return channel.close();
public RFuture<Void> forceFastReconnectAsync() {
fastReconnect = new RedissonPromise<Void>();
channel.close();
return fastReconnect;
}
/**

@ -33,6 +33,7 @@ import org.redisson.client.protocol.pubsub.PubSubPatternMessage;
import org.redisson.client.protocol.pubsub.PubSubPatternMessageDecoder;
import org.redisson.client.protocol.pubsub.PubSubStatusMessage;
import org.redisson.client.protocol.pubsub.PubSubType;
import org.redisson.misc.RPromise;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
@ -53,8 +54,8 @@ public class RedisPubSubConnection extends RedisConnection {
final Set<String> unsubscibedChannels = new HashSet<String>();
final Set<String> punsubscibedChannels = new HashSet<String>();
public RedisPubSubConnection(RedisClient redisClient, Channel channel) {
super(redisClient, channel);
public RedisPubSubConnection(RedisClient redisClient, Channel channel, RPromise<RedisPubSubConnection> connectionPromise) {
super(redisClient, channel, connectionPromise);
}
public void addListener(RedisPubSubListener listener) {

@ -17,6 +17,7 @@ package org.redisson.client;
import java.net.InetSocketAddress;
import java.net.URI;
import org.redisson.misc.URIBuilder;
/**
*
@ -27,12 +28,12 @@ public class RedisRedirectException extends RedisException {
private static final long serialVersionUID = 181505625075250011L;
private int slot;
private URI url;
private final int slot;
private final URI url;
public RedisRedirectException(int slot, String url) {
this.slot = slot;
this.url = URI.create("//" + url);
this.url = URIBuilder.create("//" + url);
}
public int getSlot() {

@ -19,7 +19,10 @@ import org.redisson.client.protocol.Decoder;
import org.redisson.client.protocol.Encoder;
/**
* Redis codec interface
* Redis codec interface.
* <p>
* It's required for implementation to have two constructors
* default and with ClassLoader object as parameter.
*
* @author Nikita Koksharov
*

@ -0,0 +1,108 @@
/**
* Copyright 2016 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 org.redisson.client.handler;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.redisson.api.RFuture;
import org.redisson.client.RedisClient;
import org.redisson.client.RedisClientConfig;
import org.redisson.client.RedisConnection;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.misc.RPromise;
import org.redisson.misc.RedissonPromise;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
/**
*
* @author Nikita Koksharov
*
*/
public abstract class BaseConnectionHandler<C extends RedisConnection> extends ChannelInboundHandlerAdapter {
final RedisClient redisClient;
final RPromise<C> connectionPromise = new RedissonPromise<C>();
C connection;
public BaseConnectionHandler(RedisClient redisClient) {
super();
this.redisClient = redisClient;
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
if (connection == null) {
connection = createConnection(ctx);
}
super.channelRegistered(ctx);
}
abstract C createConnection(ChannelHandlerContext ctx);
@Override
public void channelActive(final ChannelHandlerContext ctx) throws Exception {
final AtomicInteger commandsCounter = new AtomicInteger();
List<RFuture<Object>> futures = new ArrayList<RFuture<Object>>();
RedisClientConfig config = redisClient.getConfig();
if (config.getPassword() != null) {
RFuture<Object> future = connection.async(RedisCommands.AUTH, config.getPassword());
futures.add(future);
}
if (config.getDatabase() != 0) {
RFuture<Object> future = connection.async(RedisCommands.SELECT, config.getDatabase());
futures.add(future);
}
if (config.getClientName() != null) {
RFuture<Object> future = connection.async(RedisCommands.CLIENT_SETNAME, config.getClientName());
futures.add(future);
}
if (config.isReadOnly()) {
RFuture<Object> future = connection.async(RedisCommands.READONLY);
futures.add(future);
}
if (futures.isEmpty()) {
connectionPromise.trySuccess(connection);
return;
}
commandsCounter.set(futures.size());
for (RFuture<Object> future : futures) {
future.addListener(new FutureListener<Object>() {
@Override
public void operationComplete(Future<Object> future) throws Exception {
if (!future.isSuccess()) {
connection.closeAsync();
connectionPromise.tryFailure(future.cause());
return;
}
if (commandsCounter.decrementAndGet() == 0) {
BaseConnectionHandler.super.channelActive(ctx);
connectionPromise.trySuccess(connection);
}
}
});
}
}
}

@ -16,15 +16,15 @@
package org.redisson.client.handler;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import org.redisson.client.RedisConnection;
import org.redisson.client.RedisException;
import org.redisson.client.RedisPubSubConnection;
import org.redisson.client.codec.Codec;
import org.redisson.client.protocol.CommandData;
import org.redisson.misc.RPromise;
import org.redisson.misc.RedissonPromise;
import org.redisson.client.protocol.QueueCommandHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -70,7 +70,6 @@ public class ConnectionWatchdog extends ChannelInboundHandlerAdapter {
if (!connection.isClosed()) {
if (connection.isFastReconnect()) {
tryReconnect(connection, 1);
connection.clearFastReconnect();
} else {
reconnect(connection, 1);
}
@ -81,7 +80,7 @@ public class ConnectionWatchdog extends ChannelInboundHandlerAdapter {
private void reconnect(final RedisConnection connection, final int attempts){
int timeout = 2 << attempts;
if (bootstrap.group().isShuttingDown()) {
if (bootstrap.config().group().isShuttingDown()) {
return;
}
@ -94,51 +93,48 @@ public class ConnectionWatchdog extends ChannelInboundHandlerAdapter {
}
private void tryReconnect(final RedisConnection connection, final int nextAttempt) {
if (connection.isClosed() || bootstrap.group().isShuttingDown()) {
if (connection.isClosed() || bootstrap.config().group().isShuttingDown()) {
return;
}
log.debug("reconnecting {} to {} ", connection, connection.getRedisClient().getAddr(), connection);
bootstrap.connect().addListener(new ChannelFutureListener() {
try {
bootstrap.connect().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(final ChannelFuture future) throws Exception {
if (connection.isClosed() || bootstrap.group().isShuttingDown()) {
return;
}
try {
if (future.isSuccess()) {
log.debug("{} connected to {}", connection, connection.getRedisClient().getAddr());
reconnect(connection, future.channel());
@Override
public void operationComplete(final ChannelFuture future) throws Exception {
if (connection.isClosed() || bootstrap.config().group().isShuttingDown()) {
return;
}
} catch (RedisException e) {
log.warn("Can't connect " + connection + " to " + connection.getRedisClient().getAddr(), e);
}
reconnect(connection, nextAttempt);
}
});
}
private void reconnect(final RedisConnection connection, final Channel channel) {
if (connection.getReconnectListener() != null) {
// new connection used only for channel init
RedisConnection rc = new RedisConnection(connection.getRedisClient(), channel);
RPromise<RedisConnection> connectionFuture = new RedissonPromise<RedisConnection>();
connection.getReconnectListener().onReconnect(rc, connectionFuture);
connectionFuture.addListener(new FutureListener<RedisConnection>() {
@Override
public void operationComplete(Future<RedisConnection> future) throws Exception {
if (future.isSuccess()) {
refresh(connection, channel);
final Channel channel = future.channel();
RedisConnection c = RedisConnection.getFrom(channel);
c.getConnectionPromise().addListener(new FutureListener<RedisConnection>() {
@Override
public void operationComplete(Future<RedisConnection> future) throws Exception {
if (future.isSuccess()) {
if (connection.isFastReconnect()) {
connection.clearFastReconnect();
}
log.debug("{} connected to {}, command: {}", connection, connection.getRedisClient().getAddr(), connection.getCurrentCommand());
refresh(connection, channel);
} else {
log.warn("Can't connect " + connection + " to " + connection.getRedisClient().getAddr(), future.cause());
}
}
});
return;
}
reconnect(connection, nextAttempt);
}
});
} else {
refresh(connection, channel);
} catch (RejectedExecutionException e) {
// skip
}
}
@ -155,26 +151,28 @@ public class ConnectionWatchdog extends ChannelInboundHandlerAdapter {
}
private void refresh(RedisConnection connection, Channel channel) {
CommandData<?, ?> commandData = connection.getCurrentCommand();
CommandData<?, ?> currentCommand = connection.getCurrentCommand();
connection.updateChannel(channel);
reattachBlockingQueue(connection, commandData);
reattachBlockingQueue(connection, currentCommand);
reattachPubSub(connection);
}
private void reattachBlockingQueue(RedisConnection connection, final CommandData<?, ?> commandData) {
if (commandData == null
|| !commandData.isBlockingCommand()
|| commandData.getPromise().isDone()) {
private void reattachBlockingQueue(RedisConnection connection, CommandData<?, ?> currentCommand) {
if (currentCommand == null
|| !currentCommand.isBlockingCommand()
|| currentCommand.getPromise().isDone()) {
return;
}
ChannelFuture future = connection.send(commandData);
log.debug("blocking queue sent " + connection);
ChannelFuture future = connection.send(currentCommand);
final CommandData<?, ?> cd = currentCommand;
future.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
log.error("Can't reconnect blocking queue to new connection. {}", commandData);
log.error("Can't reconnect blocking queue to new connection. {}", cd);
}
}
});

@ -0,0 +1,186 @@
/**
* Copyright 2016 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 org.redisson.client.handler;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.TrustManagerFactory;
import org.redisson.client.RedisClient;
import org.redisson.client.RedisClientConfig;
import org.redisson.client.RedisConnection;
import org.redisson.config.SslProvider;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.group.ChannelGroup;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.ssl.SslHandshakeCompletionEvent;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
/**
*
* @author Nikita Koksharov
*
*/
public class RedisChannelInitializer extends ChannelInitializer<Channel> {
public enum Type {PUBSUB, PLAIN}
private final RedisClientConfig config;
private final RedisClient redisClient;
private final ChannelGroup channels;
private final Bootstrap bootstrap;
private final Type type;
public RedisChannelInitializer(Bootstrap bootstrap, RedisClientConfig config, RedisClient redisClient, ChannelGroup channels, Type type) {
super();
this.bootstrap = bootstrap;
this.config = config;
this.redisClient = redisClient;
this.channels = channels;
this.type = type;
}
@Override
protected void initChannel(Channel ch) throws Exception {
initSsl(config, ch);
if (type == Type.PLAIN) {
ch.pipeline().addLast(new RedisConnectionHandler(redisClient));
} else {
ch.pipeline().addLast(new RedisPubSubConnectionHandler(redisClient));
}
ch.pipeline().addLast(
new ConnectionWatchdog(bootstrap, channels, config.getTimer()),
CommandEncoder.INSTANCE,
CommandBatchEncoder.INSTANCE,
new CommandsQueue(),
new CommandDecoder(config.getExecutor()));
}
private void initSsl(final RedisClientConfig config, Channel ch) throws KeyStoreException, IOException,
NoSuchAlgorithmException, CertificateException, SSLException, UnrecoverableKeyException {
if (!"rediss".equals(config.getAddress().getScheme())) {
return;
}
io.netty.handler.ssl.SslProvider provided = io.netty.handler.ssl.SslProvider.JDK;
if (config.getSslProvider() == SslProvider.OPENSSL) {
provided = io.netty.handler.ssl.SslProvider.OPENSSL;
}
SslContextBuilder sslContextBuilder = SslContextBuilder.forClient().sslProvider(provided);
if (config.getSslTruststore() != null) {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream stream = config.getSslTruststore().toURL().openStream();
try {
char[] password = null;
if (config.getSslTruststorePassword() != null) {
password = config.getSslTruststorePassword().toCharArray();
}
keyStore.load(stream, password);
} finally {
stream.close();
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
sslContextBuilder.trustManager(trustManagerFactory);
}
if (config.getSslKeystore() != null){
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream stream = config.getSslKeystore().toURL().openStream();
char[] password = null;
if (config.getSslKeystorePassword() != null) {
password = config.getSslKeystorePassword().toCharArray();
}
try {
keyStore.load(stream, password);
} finally {
stream.close();
}
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, password);
sslContextBuilder.keyManager(keyManagerFactory);
}
SSLParameters sslParams = new SSLParameters();
if (config.isSslEnableEndpointIdentification()) {
sslParams.setEndpointIdentificationAlgorithm("HTTPS");
} else {
if (config.getSslTruststore() == null) {
sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE);
}
}
SslContext sslContext = sslContextBuilder.build();
SSLEngine sslEngine = sslContext.newEngine(ch.alloc(), config.getAddress().getHost(), config.getAddress().getPort());
sslEngine.setSSLParameters(sslParams);
SslHandler sslHandler = new SslHandler(sslEngine);
ch.pipeline().addLast(sslHandler);
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
volatile boolean sslInitDone;
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
if (sslInitDone) {
super.channelActive(ctx);
}
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (!sslInitDone && (evt instanceof SslHandshakeCompletionEvent)) {
SslHandshakeCompletionEvent e = (SslHandshakeCompletionEvent) evt;
if (e.isSuccess()) {
sslInitDone = true;
ctx.fireChannelActive();
} else {
RedisConnection connection = RedisConnection.getFrom(ctx.channel());
connection.getConnectionPromise().tryFailure(e.cause());
}
}
super.userEventTriggered(ctx, evt);
}
});
}
}

@ -0,0 +1,39 @@
/**
* Copyright 2016 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 org.redisson.client.handler;
import org.redisson.client.RedisClient;
import org.redisson.client.RedisConnection;
import io.netty.channel.ChannelHandlerContext;
/**
*
* @author Nikita Koksharov
*
*/
public class RedisConnectionHandler extends BaseConnectionHandler<RedisConnection> {
public RedisConnectionHandler(RedisClient redisClient) {
super(redisClient);
}
@Override
RedisConnection createConnection(ChannelHandlerContext ctx) {
return new RedisConnection(redisClient, ctx.channel(), connectionPromise);
}
}

@ -0,0 +1,39 @@
/**
* Copyright 2016 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 org.redisson.client.handler;
import org.redisson.client.RedisClient;
import org.redisson.client.RedisPubSubConnection;
import io.netty.channel.ChannelHandlerContext;
/**
*
* @author Nikita Koksharov
*
*/
public class RedisPubSubConnectionHandler extends BaseConnectionHandler<RedisPubSubConnection> {
public RedisPubSubConnectionHandler(RedisClient redisClient) {
super(redisClient);
}
@Override
RedisPubSubConnection createConnection(ChannelHandlerContext ctx) {
return new RedisPubSubConnection(redisClient, ctx.channel(), connectionPromise);
}
}

@ -97,7 +97,7 @@ public class CommandData<T, R> implements QueueCommand {
}
public boolean isBlockingCommand() {
return RedisCommands.BLOCKING_COMMANDS.contains(command.getName()) && !promise.isDone();
return RedisCommands.BLOCKING_COMMANDS.contains(command.getName());
}
}

@ -70,4 +70,9 @@ public class CommandsData implements QueueCommand {
return promise.tryFailure(cause);
}
@Override
public String toString() {
return "CommandsData [commands=" + commands + "]";
}
}

@ -43,4 +43,9 @@ public class QueueCommandHolder {
return sended.compareAndSet(false, true);
}
@Override
public String toString() {
return "QueueCommandHolder [command=" + command + "]";
}
}

@ -45,10 +45,12 @@ import org.redisson.client.protocol.decoder.KeyValueObjectDecoder;
import org.redisson.client.protocol.decoder.ListResultReplayDecoder;
import org.redisson.client.protocol.decoder.ListScanResult;
import org.redisson.client.protocol.decoder.ListScanResultReplayDecoder;
import org.redisson.client.protocol.decoder.Long2MultiDecoder;
import org.redisson.client.protocol.decoder.MapScanResult;
import org.redisson.client.protocol.decoder.MapScanResultReplayDecoder;
import org.redisson.client.protocol.decoder.NestedMultiDecoder;
import org.redisson.client.protocol.decoder.ObjectFirstResultReplayDecoder;
import org.redisson.client.protocol.decoder.ObjectFirstScoreReplayDecoder;
import org.redisson.client.protocol.decoder.ObjectListReplayDecoder;
import org.redisson.client.protocol.decoder.ObjectMapEntryReplayDecoder;
import org.redisson.client.protocol.decoder.ObjectMapReplayDecoder;
@ -76,6 +78,8 @@ public interface RedisCommands {
RedisCommand<Double> GEODIST = new RedisCommand<Double>("GEODIST", new DoubleReplayConvertor(), 2, Arrays.asList(ValueType.OBJECT, ValueType.OBJECT, ValueType.STRING));
RedisCommand<List<Object>> GEORADIUS = new RedisCommand<List<Object>>("GEORADIUS", new ObjectListReplayDecoder<Object>());
RedisCommand<List<Object>> GEORADIUSBYMEMBER = new RedisCommand<List<Object>>("GEORADIUSBYMEMBER", new ObjectListReplayDecoder<Object>(), 2);
RedisCommand<Object> GEORADIUS_STORE = new RedisCommand<Object>("GEORADIUS", new Long2MultiDecoder());
RedisCommand<Object> GEORADIUSBYMEMBER_STORE = new RedisCommand<Object>("GEORADIUSBYMEMBER", new Long2MultiDecoder(), 2);
RedisStrictCommand<Integer> KEYSLOT = new RedisStrictCommand<Integer>("CLUSTER", "KEYSLOT", new IntegerReplayConvertor());
RedisStrictCommand<RType> TYPE = new RedisStrictCommand<RType>("TYPE", new TypeConvertor());
@ -111,6 +115,7 @@ public interface RedisCommands {
RedisCommand<Integer> ZREVRANK_INT = new RedisCommand<Integer>("ZREVRANK", new IntegerReplayConvertor(), 2);
RedisStrictCommand<Long> ZRANK = new RedisStrictCommand<Long>("ZRANK", 2);
RedisCommand<Object> ZRANGE_SINGLE = new RedisCommand<Object>("ZRANGE", new ObjectFirstResultReplayDecoder<Object>());
RedisStrictCommand<Double> ZRANGE_SINGLE_SCORE = new RedisStrictCommand<Double>("ZRANGE", new ObjectFirstScoreReplayDecoder());
RedisCommand<List<Object>> ZRANGE = new RedisCommand<List<Object>>("ZRANGE", new ObjectListReplayDecoder<Object>());
RedisStrictCommand<Integer> ZREMRANGEBYRANK = new RedisStrictCommand<Integer>("ZREMRANGEBYRANK", new IntegerReplayConvertor());
RedisStrictCommand<Integer> ZREMRANGEBYSCORE = new RedisStrictCommand<Integer>("ZREMRANGEBYSCORE", new IntegerReplayConvertor());

@ -46,7 +46,7 @@ public class ClusterNodesDecoder implements Decoder<List<ClusterNodeInfo>> {
String nodeId = params[0];
node.setNodeId(nodeId);
String addr = params[1];
String addr = "redis://" + params[1].split("@")[0];
node.setAddress(addr);
String flags = params[2];

@ -15,6 +15,11 @@
*/
package org.redisson.client.protocol.decoder;
/**
*
* @author Nikita Koksharov
*
*/
public interface DecoderState {
DecoderState copy();

@ -23,8 +23,12 @@ import org.redisson.client.codec.DoubleCodec;
import org.redisson.client.handler.State;
import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil;
/**
*
* @author Nikita Koksharov
*
*/
public class GeoDistanceDecoder implements MultiDecoder<List<Object>> {
private final ThreadLocal<Integer> pos = new ThreadLocal<Integer>();

@ -26,6 +26,11 @@ import org.redisson.client.handler.State;
import io.netty.buffer.ByteBuf;
/**
*
* @author Nikita Koksharov
*
*/
public class GeoDistanceMapDecoder implements MultiDecoder<Map<Object, Object>> {
private final ThreadLocal<Integer> pos = new ThreadLocal<Integer>();

@ -23,6 +23,11 @@ import org.redisson.client.handler.State;
import io.netty.buffer.ByteBuf;
/**
*
* @author Nikita Koksharov
*
*/
public class GeoMapReplayDecoder implements MultiDecoder<Map<Object, Object>> {
@Override

@ -24,6 +24,11 @@ import org.redisson.client.handler.State;
import io.netty.buffer.ByteBuf;
/**
*
* @author Nikita Koksharov
*
*/
public class GeoPositionDecoder implements MultiDecoder<GeoPosition> {
@Override

@ -25,6 +25,11 @@ import org.redisson.client.handler.State;
import io.netty.buffer.ByteBuf;
/**
*
* @author Nikita Koksharov
*
*/
public class GeoPositionMapDecoder implements MultiDecoder<Map<Object, Object>> {
private final List<Object> args;

@ -15,6 +15,13 @@
*/
package org.redisson.client.protocol.decoder;
/**
*
* @author Nikita Koksharov
*
* @param <K> key type
* @param <V> value type
*/
public class KeyValueMessage<K, V> {
private K key;

@ -22,6 +22,11 @@ import org.redisson.client.handler.State;
import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil;
/**
*
* @author Nikita Koksharov
*
*/
public class KeyValueObjectDecoder implements MultiDecoder<Object> {
@Override

@ -21,6 +21,11 @@ import org.redisson.client.handler.State;
import io.netty.buffer.ByteBuf;
/**
*
* @author Nikita Koksharov
*
*/
public class ListFirstObjectDecoder implements MultiDecoder<Object> {
@Override

@ -21,6 +21,11 @@ import org.redisson.client.handler.State;
import io.netty.buffer.ByteBuf;
/**
*
* @author Nikita Koksharov
*
*/
public class ListIteratorReplayDecoder implements MultiDecoder<ListIteratorResult<Object>> {
@Override

@ -15,6 +15,12 @@
*/
package org.redisson.client.protocol.decoder;
/**
*
* @author Nikita Koksharov
*
* @param <V> value type
*/
public class ListIteratorResult<V> {
private final V element;

@ -22,6 +22,12 @@ import org.redisson.client.handler.State;
import io.netty.buffer.ByteBuf;
/**
*
* @author Nikita Koksharov
*
* @param <T> type
*/
public class ListMultiDecoder<T> implements MultiDecoder<Object> {
private final MultiDecoder<?>[] decoders;

@ -24,6 +24,11 @@ import org.redisson.client.handler.State;
import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil;
/**
*
* @author Nikita Koksharov
*
*/
public class ListResultReplayDecoder implements MultiDecoder<List<Map<Object, Object>>> {
@Override

@ -20,6 +20,12 @@ import java.util.List;
import org.redisson.RedisClientResult;
/**
*
* @author Nikita Koksharov
*
* @param <V> value type
*/
public class ListScanResult<V> implements RedisClientResult {
private final Long pos;

@ -22,6 +22,11 @@ import org.redisson.client.handler.State;
import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil;
/**
*
* @author Nikita Koksharov
*
*/
public class ListScanResultReplayDecoder implements MultiDecoder<ListScanResult<Object>> {
@Override

@ -0,0 +1,37 @@
/**
* Copyright 2016 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 org.redisson.client.protocol.decoder;
import java.util.List;
import org.redisson.client.handler.State;
/**
*
* @author Nikita Koksharov
*
*/
public class Long2MultiDecoder extends LongMultiDecoder {
@Override
public Object decode(List<Object> parts, State state) {
if (parts.isEmpty()) {
return 0L;
}
return null;
}
}

@ -23,6 +23,11 @@ import org.redisson.client.handler.State;
import io.netty.buffer.ByteBuf;
/**
*
* @author Nikita Koksharov
*
*/
public class LongMultiDecoder implements MultiDecoder<Object> {
@Override

@ -18,6 +18,13 @@ package org.redisson.client.protocol.decoder;
import java.util.List;
import java.util.Map;
/**
*
* @author Nikita Koksharov
*
* @param <K> key type
* @param <V> value type
*/
public class MapCacheScanResult<K, V> extends MapScanResult<K, V> {
private final List<K> idleKeys;

@ -23,6 +23,11 @@ import org.redisson.client.handler.State;
import io.netty.buffer.ByteBuf;
/**
*
* @author Nikita Koksharov
*
*/
public class MapCacheScanResultReplayDecoder implements MultiDecoder<MapCacheScanResult<Object, Object>> {
@Override

@ -20,6 +20,13 @@ import java.util.Map;
import org.redisson.RedisClientResult;
/**
*
* @author Nikita Koksharov
*
* @param <K> key type
* @param <V> value type
*/
public class MapScanResult<K, V> implements RedisClientResult {
private final Long pos;

@ -23,6 +23,11 @@ import org.redisson.client.handler.State;
import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil;
/**
*
* @author Nikita Koksharov
*
*/
public class MapScanResultReplayDecoder implements MultiDecoder<MapScanResult<Object, Object>> {
@Override

@ -21,6 +21,12 @@ import org.redisson.client.handler.State;
import io.netty.buffer.ByteBuf;
/**
*
* @author Nikita Koksharov
*
* @param <T> type
*/
public class ObjectFirstResultReplayDecoder<T> implements MultiDecoder<T> {
@Override

@ -0,0 +1,49 @@
/**
* Copyright 2016 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 org.redisson.client.protocol.decoder;
import java.math.BigDecimal;
import java.util.List;
import org.redisson.client.handler.State;
import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil;
/**
*
* @author Nikita Koksharov
*
*
*/
public class ObjectFirstScoreReplayDecoder implements MultiDecoder<Double> {
@Override
public Object decode(ByteBuf buf, State state) {
return new BigDecimal(buf.toString(CharsetUtil.UTF_8)).doubleValue();
}
@Override
public Double decode(List<Object> parts, State state) {
return (Double) parts.get(1);
}
@Override
public boolean isApplicable(int paramNum, State state) {
return paramNum % 2 != 0;
}
}

@ -23,6 +23,12 @@ import org.redisson.client.handler.State;
import io.netty.buffer.ByteBuf;
/**
*
* @author Nikita Koksharov
*
* @param <T> type
*/
public class ObjectListDecoder<T> implements MultiDecoder<List<T>> {
private Codec codec;

@ -17,6 +17,11 @@ package org.redisson.client.protocol.decoder;
import io.netty.buffer.ByteBuf;
/**
*
* @author Nikita Koksharov
*
*/
public class ScanObjectEntry {
private final ByteBuf buf;

@ -25,6 +25,12 @@ import org.redisson.client.protocol.ScoredEntry;
import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil;
/**
*
* @author Nikita Koksharov
*
* @param <T> type
*/
public class ScoredSortedSetReplayDecoder<T> implements MultiDecoder<List<ScoredEntry<T>>> {
@Override

@ -22,6 +22,12 @@ import org.redisson.client.handler.State;
import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil;
/**
*
* @author Nikita Koksharov
*
* @param <T> type
*/
public class ScoredSortedSetScanDecoder<T> extends ObjectListReplayDecoder<T> {
@Override

@ -22,6 +22,11 @@ import org.redisson.client.handler.State;
import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil;
/**
*
* @author Nikita Koksharov
*
*/
public class ScoredSortedSetScanReplayDecoder implements MultiDecoder<ListScanResult<Object>> {
@Override

@ -28,6 +28,11 @@ import org.redisson.cluster.ClusterSlotRange;
import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil;
/**
*
* @author Nikita Koksharov
*
*/
public class SlotsDecoder implements MultiDecoder<Object> {
@Override

@ -21,6 +21,11 @@ import org.redisson.client.protocol.Decoder;
import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil;
/**
*
* @author Nikita Koksharov
*
*/
public class StringDataDecoder implements Decoder<String> {
@Override

@ -23,6 +23,11 @@ import org.redisson.client.handler.State;
import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil;
/**
*
* @author Nikita Koksharov
*
*/
public class StringListReplayDecoder implements MultiDecoder<List<String>> {
@Override

@ -24,6 +24,11 @@ import org.redisson.client.protocol.Decoder;
import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil;
/**
*
* @author Nikita Koksharov
*
*/
public class StringMapDataDecoder implements Decoder<Map<String, String>> {
@Override

@ -21,6 +21,11 @@ import org.redisson.client.protocol.Decoder;
import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil;
/**
*
* @author Nikita Koksharov
*
*/
public class StringReplayDecoder implements Decoder<String> {
@Override

@ -22,6 +22,12 @@ import org.redisson.client.handler.State;
import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil;
/**
*
* @author Nikita Koksharov
*
* @param <T> type
*/
public class TTLMapValueReplayDecoder<T> implements MultiDecoder<List<T>> {
@Override

@ -1,42 +0,0 @@
/**
* Copyright 2016 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 org.redisson.cluster;
import org.redisson.api.NodeType;
import org.redisson.client.RedisConnection;
import org.redisson.client.RedisException;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.config.MasterSlaveServersConfig;
import org.redisson.connection.DefaultConnectionListener;
import org.redisson.connection.FutureConnectionListener;
public class ClusterConnectionListener extends DefaultConnectionListener {
private final boolean readFromSlaves;
public ClusterConnectionListener(boolean readFromSlaves) {
this.readFromSlaves = readFromSlaves;
}
@Override
public void doConnect(MasterSlaveServersConfig config, NodeType serverMode, FutureConnectionListener<? extends RedisConnection> connectionListener) throws RedisException {
super.doConnect(config, serverMode, connectionListener);
if (serverMode == NodeType.SLAVE && readFromSlaves) {
connectionListener.addCommand(RedisCommands.READONLY);
}
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save