diff --git a/.travis.yml b/.travis.yml index d911a0161..855eb09e8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -224,4 +224,4 @@ before_script: - export REDIS_VERSION="$(redis-cli INFO SERVER | sed -n 2p)" - echo $REDIS_VERSION - redis-cli SHUTDOWN NOSAVE -script: mvn -Dtest=$REDISSON_TEST -Dsurefire.rerunFailingTestsCount=5 -DargLine="-Xmx2g -DredisBinary=$REDIS_BIN/redis-server -DtravisEnv=true" -Punit-test clean test -e -X +script: mvn -Dtest=$REDISSON_TEST -Dsurefire.rerunFailingTestsCount=5 -DargLine="-Xmx2g -DredisBinary=$REDIS_BIN/redis-server -DtravisEnv=true -XX:SoftRefLRUPolicyMSPerMB=0" -Punit-test clean test -e -X diff --git a/CHANGELOG.md b/CHANGELOG.md index ecbff49b5..1bdad30ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ Redisson Releases History Try __ULTRA-FAST__ [Redisson PRO](https://redisson.pro) edition. +####04-Mar-2017 - versions 2.8.1 and 3.3.1 released + +Feature - Cache with SoftReference support added for `RLocalCachedMap` +Feature - `Config.subscriptionMode` setting added +Improvement - errors handling during RBatch execution +Fixed - StackOverflowException in URLBuilder +Fixed - TomcatSessionManager can't be used in Tomcat if Redisson has been deployed in web application +Fixed - skip cluster nodes with the "handshake" flag (thanks to @dcheckoway) + ####19-Feb-2017 - versions 2.8.0 and 3.3.0 released Feature - __`RClusteredLocalCachedMap` object added__ More details [here](https://github.com/redisson/redisson/wiki/7.-distributed-collections#713-map-data-partitioning) diff --git a/README.md b/README.md index fb9eede53..21d1f0691 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,16 @@ -Redis based In-Memory Data Grid for Java. Redisson. +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.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) Based on high-performance async and lock-free Java Redis client and [Netty](http://netty.io) framework. Redis 2.8+ compatible. | Stable Release Version | JDK Version compatibility | Release Date | | ------------- | ------------- | ------------| -| 3.3.0 | 1.8+ | 19.02.2017 | -| 2.8.0 | 1.6, 1.7, 1.8 and Android | 19.02.2017 | +| 3.3.1 | 1.8+ | 03.03.2017 | +| 2.8.1 | 1.6, 1.7, 1.8 and Android | 03.03.2017 | -__NOTE__: Both version lines have same features except `CompletionStage` interface supported by 3.x.x line - -Please read [documentation](https://github.com/redisson/redisson/wiki) for more details. -Redisson [releases history](https://github.com/redisson/redisson/blob/master/CHANGELOG.md) -Checkout more [code examples](https://github.com/redisson/redisson-examples) -Browse [javadocs](http://www.javadoc.io/doc/org.redisson/redisson/3.2.4) - -Licensed under the Apache License 2.0. - -Welcome to support chat [![Join the chat at https://gitter.im/mrniko/redisson](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mrniko/redisson?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +__NOTE__: Both version lines have same features except `CompletionStage` interface added in 3.x.x Features @@ -42,7 +34,7 @@ Features Object holder, Binary stream holder, Geospatial holder, BitSet, AtomicLong, AtomicDouble, PublishSubscribe, Bloom filter, HyperLogLog * [Distributed collections](https://github.com/redisson/redisson/wiki/7.-Distributed-collections) - Map, Multimap, Set, List, SortedSet, ScoredSortedSet, LexSortedSet, Queue, Deque, Blocking Queue, Bounded Blocking Queue, Blocking Deque, Delayed Queue + Map, Multimap, Set, List, SortedSet, ScoredSortedSet, LexSortedSet, Queue, Deque, Blocking Queue, Bounded Blocking Queue, Blocking Deque, Delayed Queue, Priority Queue, Priority Deque * [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) @@ -63,7 +55,7 @@ Features Who uses Redisson ================================ -[Electronic Arts](http://ea.com), [Baidu](http://baidu.com), [New Relic Synthetics](https://newrelic.com/synthetics), [National Australia Bank](https://www.nab.com.au/), [Brookhaven National Laboratory](http://bnl.gov/), [Singtel](http://singtel.com), [Infor](http://www.infor.com/), [Setronica](http://setronica.com/), [Monits](http://monits.com/), [Netflix Dyno client] (https://github.com/Netflix/dyno), [武林Q传](http://www.nbrpg.com/), [Ocous](http://www.ocous.com/), [Invaluable](http://www.invaluable.com/), [Clover](https://www.clover.com/) , [Apache Karaf Decanter](https://karaf.apache.org/projects.html#decanter), [Atmosphere Framework](http://async-io.org/), [BrandsEye](http://brandseye.com), [Datorama](http://datorama.com/), [BrightCloud](http://brightcloud.com/), [Azar](http://azarlive.com/), [Snapfish](http://snapfish.com), [Crimson Hexagon](http://www.crimsonhexagon.com) +[Electronic Arts](http://ea.com), [Baidu](http://baidu.com), [New Relic Synthetics](https://newrelic.com/synthetics), [Brookhaven National Laboratory](http://bnl.gov/), [Singtel](http://singtel.com), [Infor](http://www.infor.com/), [Setronica](http://setronica.com/), [Monits](http://monits.com/), [Netflix Dyno client] (https://github.com/Netflix/dyno), [武林Q传](http://www.nbrpg.com/), [Ocous](http://www.ocous.com/), [Invaluable](http://www.invaluable.com/), [Clover](https://www.clover.com/) , [Apache Karaf Decanter](https://karaf.apache.org/projects.html#decanter), [Atmosphere Framework](http://async-io.org/), [BrandsEye](http://brandseye.com), [Datorama](http://datorama.com/), [BrightCloud](http://brightcloud.com/), [Azar](http://azarlive.com/), [Snapfish](http://snapfish.com), [Crimson Hexagon](http://www.crimsonhexagon.com), [Quby](http://quby.com/), [Base CRM](http://getbase.com) Articles ================================ @@ -90,23 +82,23 @@ Quick start org.redisson redisson - 3.3.0 + 3.3.1 org.redisson redisson - 2.8.0 + 2.8.1 #### Gradle // JDK 1.8+ compatible - compile 'org.redisson:redisson:3.3.0' + compile 'org.redisson:redisson:3.3.1' // JDK 1.6+ compatible - compile 'org.redisson:redisson:2.8.0' + compile 'org.redisson:redisson:2.8.1' #### Java @@ -131,11 +123,11 @@ RExecutorService executor = redisson.getExecutorService("myExecutorService"); Downloads =============================== -[Redisson 3.3.0](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson&v=3.3.0&e=jar), -[Redisson node 3.3.0](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=3.3.0&e=jar) +[Redisson 3.3.1](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson&v=3.3.1&e=jar), +[Redisson node 3.3.1](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=3.3.1&e=jar) -[Redisson 2.8.0](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson&v=2.8.0&e=jar), -[Redisson node 2.8.0](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=2.8.0&e=jar) +[Redisson 2.8.1](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson&v=2.8.1&e=jar), +[Redisson node 2.8.1](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=2.8.1&e=jar) ### Supported by diff --git a/pom.xml b/pom.xml index 960251c48..d819cfc88 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.redisson redisson-parent - 2.8.1-SNAPSHOT + 2.8.2-SNAPSHOT pom Redisson diff --git a/redisson-all/pom.xml b/redisson-all/pom.xml index f2b1e9fa2..9d40c05a9 100644 --- a/redisson-all/pom.xml +++ b/redisson-all/pom.xml @@ -4,7 +4,7 @@ org.redisson redisson-parent - 2.8.1-SNAPSHOT + 2.8.2-SNAPSHOT ../ diff --git a/redisson-tomcat/README.md b/redisson-tomcat/README.md index ff6bad985..eca0ff286 100644 --- a/redisson-tomcat/README.md +++ b/redisson-tomcat/README.md @@ -1,13 +1,14 @@ Redis based Tomcat Session Manager === -Implements non-sticky session management backed by Redis. -Supports Tomcat 6.x, 7.x, 8.x +Stores session of Apache Tomcat in Redis and allows to distribute requests across a cluster of Tomcat servers. Implements non-sticky session management backed by Redis. + +Supports Apache Tomcat 6.x, 7.x, 8.x Advantages === -Current implementation differs from any other Tomcat Session Manager in terms of efficient storage and optimized writes. Each session attribute is written into Redis during each `HttpSession.setAttribute` invocation. While other solutions serialize whole session each time. +Current implementation differs from any other Redis based Tomcat Session Manager in terms of efficient storage and optimized writes. Each session attribute is written into Redis during each `HttpSession.setAttribute` invocation. While other solutions serialize whole session each time. Usage === @@ -21,22 +22,22 @@ Usage 2. Copy two jars into `TOMCAT_BASE/lib` directory: 1. __For JDK 1.8+__ - [redisson-all-3.3.0.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=3.3.0&e=jar) + [redisson-all-3.3.1.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=3.3.1&e=jar) for Tomcat 6.x - [redisson-tomcat-6-3.3.0.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-6&v=3.3.0&e=jar) + [redisson-tomcat-6-3.3.1.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-6&v=3.3.1&e=jar) for Tomcat 7.x - [redisson-tomcat-7-3.3.0.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-7&v=3.3.0&e=jar) + [redisson-tomcat-7-3.3.1.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-7&v=3.3.1&e=jar) for Tomcat 8.x - [redisson-tomcat-8-3.3.0.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-8&v=3.3.0&e=jar) + [redisson-tomcat-8-3.3.1.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-8&v=3.3.1&e=jar) 1. __For JDK 1.6+__ - [redisson-all-2.8.0.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=2.8.0&e=jar) + [redisson-all-2.8.1.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-all&v=2.8.1&e=jar) for Tomcat 6.x - [redisson-tomcat-6-2.8.0.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-6&v=2.8.0&e=jar) + [redisson-tomcat-6-2.8.1.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-6&v=2.8.1&e=jar) for Tomcat 7.x - [redisson-tomcat-7-2.8.0.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-7&v=2.8.0&e=jar) + [redisson-tomcat-7-2.8.1.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-7&v=2.8.1&e=jar) for Tomcat 8.x - [redisson-tomcat-8-2.8.0.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-8&v=2.8.0&e=jar) + [redisson-tomcat-8-2.8.1.jar](https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=org.redisson&a=redisson-tomcat-8&v=2.8.1&e=jar) diff --git a/redisson-tomcat/pom.xml b/redisson-tomcat/pom.xml index 93169d2af..32d8f3c10 100644 --- a/redisson-tomcat/pom.xml +++ b/redisson-tomcat/pom.xml @@ -4,7 +4,7 @@ org.redisson redisson-parent - 2.8.1-SNAPSHOT + 2.8.2-SNAPSHOT ../ diff --git a/redisson-tomcat/redisson-tomcat-6/pom.xml b/redisson-tomcat/redisson-tomcat-6/pom.xml index 3e87203a9..3a77822e7 100644 --- a/redisson-tomcat/redisson-tomcat-6/pom.xml +++ b/redisson-tomcat/redisson-tomcat-6/pom.xml @@ -4,7 +4,7 @@ org.redisson redisson-tomcat - 2.8.1-SNAPSHOT + 2.8.2-SNAPSHOT ../ diff --git a/redisson-tomcat/redisson-tomcat-6/src/main/java/org/redisson/tomcat/RedissonSessionManager.java b/redisson-tomcat/redisson-tomcat-6/src/main/java/org/redisson/tomcat/RedissonSessionManager.java index 6399320c0..693f0350b 100644 --- a/redisson-tomcat/redisson-tomcat-6/src/main/java/org/redisson/tomcat/RedissonSessionManager.java +++ b/redisson-tomcat/redisson-tomcat-6/src/main/java/org/redisson/tomcat/RedissonSessionManager.java @@ -143,11 +143,11 @@ public class RedissonSessionManager extends ManagerBase implements Lifecycle { public void start() throws LifecycleException { Config config = null; try { - config = Config.fromJSON(new File(configPath)); + config = Config.fromJSON(new File(configPath), getClass().getClassLoader()); } catch (IOException e) { // trying next format try { - config = Config.fromYAML(new File(configPath)); + config = Config.fromYAML(new File(configPath), getClass().getClassLoader()); } catch (IOException e1) { log.error("Can't parse json config " + configPath, e); throw new LifecycleException("Can't parse yaml config " + configPath, e1); diff --git a/redisson-tomcat/redisson-tomcat-7/pom.xml b/redisson-tomcat/redisson-tomcat-7/pom.xml index e6b67b474..e5bb47f02 100644 --- a/redisson-tomcat/redisson-tomcat-7/pom.xml +++ b/redisson-tomcat/redisson-tomcat-7/pom.xml @@ -4,7 +4,7 @@ org.redisson redisson-tomcat - 2.8.1-SNAPSHOT + 2.8.2-SNAPSHOT ../ diff --git a/redisson-tomcat/redisson-tomcat-7/src/main/java/org/redisson/tomcat/RedissonSessionManager.java b/redisson-tomcat/redisson-tomcat-7/src/main/java/org/redisson/tomcat/RedissonSessionManager.java index 87d41f81a..910863228 100644 --- a/redisson-tomcat/redisson-tomcat-7/src/main/java/org/redisson/tomcat/RedissonSessionManager.java +++ b/redisson-tomcat/redisson-tomcat-7/src/main/java/org/redisson/tomcat/RedissonSessionManager.java @@ -19,13 +19,10 @@ import java.io.File; import java.io.IOException; import org.apache.catalina.Context; -import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleException; -import org.apache.catalina.LifecycleListener; import org.apache.catalina.LifecycleState; import org.apache.catalina.Session; import org.apache.catalina.session.ManagerBase; -import org.apache.catalina.util.LifecycleSupport; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.redisson.Redisson; @@ -39,12 +36,10 @@ import org.redisson.config.Config; * @author Nikita Koksharov * */ -public class RedissonSessionManager extends ManagerBase implements Lifecycle { +public class RedissonSessionManager extends ManagerBase { private final Log log = LogFactory.getLog(RedissonSessionManager.class); - protected LifecycleSupport lifecycle = new LifecycleSupport(this); - private RedissonClient redisson; private String configPath; @@ -61,11 +56,6 @@ public class RedissonSessionManager extends ManagerBase implements Lifecycle { return RedissonSessionManager.class.getSimpleName(); } - @Override - public int getRejectedSessions() { - return 0; - } - @Override public void load() throws ClassNotFoundException, IOException { } @@ -74,21 +64,6 @@ public class RedissonSessionManager extends ManagerBase implements Lifecycle { public void unload() throws IOException { } - @Override - public void addLifecycleListener(LifecycleListener listener) { - lifecycle.addLifecycleListener(listener); - } - - @Override - public LifecycleListener[] findLifecycleListeners() { - return lifecycle.findLifecycleListeners(); - } - - @Override - public void removeLifecycleListener(LifecycleListener listener) { - lifecycle.removeLifecycleListener(listener); - } - @Override public Session createSession(String sessionId) { RedissonSession session = (RedissonSession) createEmptySession(); @@ -146,11 +121,11 @@ public class RedissonSessionManager extends ManagerBase implements Lifecycle { super.startInternal(); Config config = null; try { - config = Config.fromJSON(new File(configPath)); + config = Config.fromJSON(new File(configPath), getClass().getClassLoader()); } catch (IOException e) { // trying next format try { - config = Config.fromYAML(new File(configPath)); + config = Config.fromYAML(new File(configPath), getClass().getClassLoader()); } catch (IOException e1) { log.error("Can't parse json config " + configPath, e); throw new LifecycleException("Can't parse yaml config " + configPath, e1); diff --git a/redisson-tomcat/redisson-tomcat-8/pom.xml b/redisson-tomcat/redisson-tomcat-8/pom.xml index 448561403..dd91cca2c 100644 --- a/redisson-tomcat/redisson-tomcat-8/pom.xml +++ b/redisson-tomcat/redisson-tomcat-8/pom.xml @@ -4,7 +4,7 @@ org.redisson redisson-tomcat - 2.8.1-SNAPSHOT + 2.8.2-SNAPSHOT ../ diff --git a/redisson-tomcat/redisson-tomcat-8/src/main/java/org/redisson/tomcat/RedissonSessionManager.java b/redisson-tomcat/redisson-tomcat-8/src/main/java/org/redisson/tomcat/RedissonSessionManager.java index 87d41f81a..786a8b725 100644 --- a/redisson-tomcat/redisson-tomcat-8/src/main/java/org/redisson/tomcat/RedissonSessionManager.java +++ b/redisson-tomcat/redisson-tomcat-8/src/main/java/org/redisson/tomcat/RedissonSessionManager.java @@ -18,14 +18,10 @@ package org.redisson.tomcat; import java.io.File; import java.io.IOException; -import org.apache.catalina.Context; -import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleException; -import org.apache.catalina.LifecycleListener; import org.apache.catalina.LifecycleState; import org.apache.catalina.Session; import org.apache.catalina.session.ManagerBase; -import org.apache.catalina.util.LifecycleSupport; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.redisson.Redisson; @@ -39,12 +35,10 @@ import org.redisson.config.Config; * @author Nikita Koksharov * */ -public class RedissonSessionManager extends ManagerBase implements Lifecycle { +public class RedissonSessionManager extends ManagerBase { private final Log log = LogFactory.getLog(RedissonSessionManager.class); - protected LifecycleSupport lifecycle = new LifecycleSupport(this); - private RedissonClient redisson; private String configPath; @@ -61,11 +55,6 @@ public class RedissonSessionManager extends ManagerBase implements Lifecycle { return RedissonSessionManager.class.getSimpleName(); } - @Override - public int getRejectedSessions() { - return 0; - } - @Override public void load() throws ClassNotFoundException, IOException { } @@ -74,21 +63,6 @@ public class RedissonSessionManager extends ManagerBase implements Lifecycle { public void unload() throws IOException { } - @Override - public void addLifecycleListener(LifecycleListener listener) { - lifecycle.addLifecycleListener(listener); - } - - @Override - public LifecycleListener[] findLifecycleListeners() { - return lifecycle.findLifecycleListeners(); - } - - @Override - public void removeLifecycleListener(LifecycleListener listener) { - lifecycle.removeLifecycleListener(listener); - } - @Override public Session createSession(String sessionId) { RedissonSession session = (RedissonSession) createEmptySession(); @@ -96,7 +70,7 @@ public class RedissonSessionManager extends ManagerBase implements Lifecycle { session.setNew(true); session.setValid(true); session.setCreationTime(System.currentTimeMillis()); - session.setMaxInactiveInterval(((Context) getContainer()).getSessionTimeout() * 60); + session.setMaxInactiveInterval(getContext().getSessionTimeout() * 60); if (sessionId == null) { sessionId = generateSessionId(); @@ -146,11 +120,11 @@ public class RedissonSessionManager extends ManagerBase implements Lifecycle { super.startInternal(); Config config = null; try { - config = Config.fromJSON(new File(configPath)); + config = Config.fromJSON(new File(configPath), getClass().getClassLoader()); } catch (IOException e) { // trying next format try { - config = Config.fromYAML(new File(configPath)); + config = Config.fromYAML(new File(configPath), getClass().getClassLoader()); } catch (IOException e1) { log.error("Can't parse json config " + configPath, e); throw new LifecycleException("Can't parse yaml config " + configPath, e1); diff --git a/redisson/pom.xml b/redisson/pom.xml index 32222c03e..958504053 100644 --- a/redisson/pom.xml +++ b/redisson/pom.xml @@ -4,7 +4,7 @@ org.redisson redisson-parent - 2.8.1-SNAPSHOT + 2.8.2-SNAPSHOT ../ @@ -147,30 +147,35 @@ lz4 1.3.0 provided + true org.msgpack jackson-dataformat-msgpack - 0.8.7 + 0.8.11 provided + true org.xerial.snappy snappy-java 1.1.2.6 provided + true de.ruedigermoeller fst 2.47 provided + true com.esotericsoftware kryo 3.0.3 provided + true org.slf4j @@ -181,68 +186,77 @@ com.fasterxml.jackson.dataformat jackson-dataformat-yaml - 2.7.6 + 2.8.7 com.fasterxml.jackson.core jackson-core - 2.7.6 + 2.8.7 com.fasterxml.jackson.core jackson-databind - 2.7.6 + 2.8.7 com.fasterxml.jackson.dataformat jackson-dataformat-cbor - 2.7.6 + 2.8.7 provided + true com.fasterxml.jackson.dataformat jackson-dataformat-smile - 2.7.6 + 2.8.7 provided + true com.fasterxml.jackson.dataformat jackson-dataformat-avro - 2.7.6 + 2.8.7 provided + true net.openhft zero-allocation-hashing - 0.5 + 0.7 + true net.bytebuddy byte-buddy - 1.4.26 + 1.6.8 + true org.jodd jodd-bean 3.7.1 + true org.springframework spring-context [3.1,) provided + true org.springframework spring-context-support [3.1,) provided + true org.springframework.session spring-session 1.2.2.RELEASE provided + true @@ -428,6 +442,7 @@ true JAVADOC_STYLE + XML_STYLE true true diff --git a/redisson/src/main/java/org/redisson/Redisson.java b/redisson/src/main/java/org/redisson/Redisson.java index 7594e69e7..2fc4a4c2d 100755 --- a/redisson/src/main/java/org/redisson/Redisson.java +++ b/redisson/src/main/java/org/redisson/Redisson.java @@ -357,7 +357,13 @@ public class Redisson implements RedissonClient { } @Override + @Deprecated public RScheduledExecutorService getExecutorService(Codec codec, String name) { + return getExecutorService(name, codec); + } + + @Override + public RScheduledExecutorService getExecutorService(String name, Codec codec) { return new RedissonExecutorService(codec, connectionManager.getCommandExecutor(), this, name); } diff --git a/redisson/src/main/java/org/redisson/RedissonBucket.java b/redisson/src/main/java/org/redisson/RedissonBucket.java index 66c852174..2371d0ef1 100644 --- a/redisson/src/main/java/org/redisson/RedissonBucket.java +++ b/redisson/src/main/java/org/redisson/RedissonBucket.java @@ -137,7 +137,7 @@ public class RedissonBucket extends RedissonExpirable implements RBucket { throw new IllegalArgumentException("Value can't be null"); } - return commandExecutor.writeAsync(getName(), codec, RedisCommands.SETEX, getName(), timeUnit.toSeconds(timeToLive), value); + return commandExecutor.writeAsync(getName(), codec, RedisCommands.PSETEX, getName(), timeUnit.toMillis(timeToLive), value); } @Override diff --git a/redisson/src/main/java/org/redisson/RedissonDelayedQueue.java b/redisson/src/main/java/org/redisson/RedissonDelayedQueue.java index bd9caf60d..595cdc7f2 100644 --- a/redisson/src/main/java/org/redisson/RedissonDelayedQueue.java +++ b/redisson/src/main/java/org/redisson/RedissonDelayedQueue.java @@ -30,6 +30,7 @@ import org.redisson.api.RTopic; import org.redisson.client.codec.Codec; import org.redisson.client.codec.LongCodec; 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.VoidReplayConvertor; @@ -286,18 +287,19 @@ public class RedissonDelayedQueue extends RedissonExpirable implements RDelay } protected RFuture removeAsync(Object o, int count) { - return commandExecutor.evalWriteAsync(getName(), codec, new RedisCommand("EVAL", new BooleanReplayConvertor(), 4), + return commandExecutor.evalWriteAsync(getName(), codec, new RedisCommand("EVAL", new BooleanReplayConvertor(), 5), "local s = redis.call('llen', KEYS[1]);" + "for i = 0, s-1, 1 do " + "local v = redis.call('lindex', KEYS[1], i);" + "local randomId, value = struct.unpack('dLc0', v);" + "if ARGV[1] == value then " + + "redis.call('zrem', KEYS[2], v);" + "redis.call('lrem', KEYS[1], 1, v);" + "return 1;" + "end; " + "end;" + "return 0;", - Collections.singletonList(getQueueName()), o); + Arrays.asList(getQueueName(), getTimeoutSetName()), o); } @Override @@ -338,7 +340,7 @@ public class RedissonDelayedQueue extends RedissonExpirable implements RDelay return newSucceededFuture(false); } - return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_BOOLEAN_WITH_VALUES, + return commandExecutor.evalWriteAsync(getName(), codec, new RedisCommand("EVAL", new BooleanReplayConvertor(), 5, ValueType.OBJECTS), "local result = 0;" + "local s = redis.call('llen', KEYS[1]);" + "local i = 0;" + @@ -351,6 +353,7 @@ public class RedissonDelayedQueue extends RedissonExpirable implements RDelay + "result = 1; " + "i = i - 1; " + "s = s - 1; " + + "redis.call('zrem', KEYS[2], v);" + "redis.call('lrem', KEYS[1], 0, v); " + "break; " + "end; " @@ -358,7 +361,7 @@ public class RedissonDelayedQueue extends RedissonExpirable implements RDelay + "i = i + 1;" + "end; " + "return result;", - Collections.singletonList(getQueueName()), c.toArray()); + Arrays.asList(getQueueName(), getTimeoutSetName()), c.toArray()); } @Override diff --git a/redisson/src/main/java/org/redisson/RedissonFairLock.java b/redisson/src/main/java/org/redisson/RedissonFairLock.java index 6b250f8bc..eb34f1cfc 100644 --- a/redisson/src/main/java/org/redisson/RedissonFairLock.java +++ b/redisson/src/main/java/org/redisson/RedissonFairLock.java @@ -173,19 +173,6 @@ public class RedissonFairLock extends RedissonLock implements RLock { throw new IllegalArgumentException(); } - @Override - public void unlock() { - Boolean opStatus = get(unlockInnerAsync(Thread.currentThread().getId())); - - if (opStatus == null) { - throw new IllegalMonitorStateException("attempt to unlock lock, not locked by current thread by node id: " - + id + " thread-id: " + Thread.currentThread().getId()); - } - if (opStatus) { - cancelExpirationRenewal(); - } - } - @Override protected RFuture unlockInnerAsync(long threadId) { return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, @@ -218,15 +205,14 @@ public class RedissonFairLock extends RedissonLock implements RLock { "if (counter > 0) then " + "redis.call('pexpire', KEYS[1], ARGV[2]); " + "return 0; " + - "else " + - "redis.call('del', KEYS[1]); " + - "local nextThreadId = redis.call('lindex', KEYS[2], 0); " + - "if nextThreadId ~= false then " + - "redis.call('publish', KEYS[4] .. ':' .. nextThreadId, ARGV[1]); " + - "end; " + - "return 1; "+ "end; " + - "return nil;", + + "redis.call('del', KEYS[1]); " + + "local nextThreadId = redis.call('lindex', KEYS[2], 0); " + + "if nextThreadId ~= false then " + + "redis.call('publish', KEYS[4] .. ':' .. nextThreadId, ARGV[1]); " + + "end; " + + "return 1; ", Arrays.asList(getName(), getThreadsQueueName(), getTimeoutSetName(), getChannelName()), LockPubSub.unlockMessage, internalLockLeaseTime, getLockName(threadId), System.currentTimeMillis()); } diff --git a/redisson/src/main/java/org/redisson/RedissonLiveObjectService.java b/redisson/src/main/java/org/redisson/RedissonLiveObjectService.java index 92a83e3d0..6a9c69c5d 100644 --- a/redisson/src/main/java/org/redisson/RedissonLiveObjectService.java +++ b/redisson/src/main/java/org/redisson/RedissonLiveObjectService.java @@ -629,12 +629,18 @@ public class RedissonLiveObjectService implements RLiveObjectService { .and(ElementMatchers.isGetter().or(ElementMatchers.isSetter()) .or(ElementMatchers.named("isPhantom")) .or(ElementMatchers.named("delete")))) - .intercept(MethodDelegation.to( - new LiveObjectInterceptor(redisson, codecProvider, entityClass, - getRIdFieldName(entityClass))) - .appendParameterBinder(FieldProxy.Binder + .intercept(MethodDelegation.withDefaultConfiguration() + .withBinders(FieldProxy.Binder .install(LiveObjectInterceptor.Getter.class, - LiveObjectInterceptor.Setter.class))) + LiveObjectInterceptor.Setter.class)) + .to(new LiveObjectInterceptor(redisson, codecProvider, entityClass, + getRIdFieldName(entityClass)))) +// .intercept(MethodDelegation.to( +// new LiveObjectInterceptor(redisson, codecProvider, entityClass, +// getRIdFieldName(entityClass))) +// .appendParameterBinder(FieldProxy.Binder +// .install(LiveObjectInterceptor.Getter.class, +// LiveObjectInterceptor.Setter.class))) .implement(RLiveObject.class) .method(ElementMatchers.isAnnotatedWith(RFieldAccessor.class) .and(ElementMatchers.named("get") diff --git a/redisson/src/main/java/org/redisson/RedissonLocalCachedMap.java b/redisson/src/main/java/org/redisson/RedissonLocalCachedMap.java index 14ae25699..a9c45fc07 100644 --- a/redisson/src/main/java/org/redisson/RedissonLocalCachedMap.java +++ b/redisson/src/main/java/org/redisson/RedissonLocalCachedMap.java @@ -38,6 +38,11 @@ import org.redisson.api.RFuture; import org.redisson.api.RLocalCachedMap; import org.redisson.api.RTopic; import org.redisson.api.listener.MessageListener; +import org.redisson.cache.Cache; +import org.redisson.cache.LFUCacheMap; +import org.redisson.cache.LRUCacheMap; +import org.redisson.cache.NoneCacheMap; +import org.redisson.cache.SoftCacheMap; import org.redisson.client.codec.Codec; import org.redisson.client.codec.LongCodec; import org.redisson.client.codec.StringCodec; @@ -48,11 +53,7 @@ import org.redisson.client.protocol.convertor.NumberConvertor; import org.redisson.client.protocol.decoder.ObjectMapEntryReplayDecoder; import org.redisson.client.protocol.decoder.ObjectSetReplayDecoder; import org.redisson.command.CommandAsyncExecutor; -import org.redisson.misc.Cache; import org.redisson.misc.Hash; -import org.redisson.misc.LFUCacheMap; -import org.redisson.misc.LRUCacheMap; -import org.redisson.misc.NoneCacheMap; import org.redisson.misc.RPromise; import io.netty.util.concurrent.Future; @@ -73,14 +74,14 @@ public class RedissonLocalCachedMap extends RedissonMap implements R public static class LocalCachedMapInvalidate implements Serializable { private byte[] excludedId; - private byte[] keyHash; + private List keyHashes; public LocalCachedMapInvalidate() { } - public LocalCachedMapInvalidate(byte[] excludedId, byte[] keyHash) { + public LocalCachedMapInvalidate(byte[] excludedId, byte[]... keyHash) { super(); - this.keyHash = keyHash; + this.keyHashes = Arrays.asList(keyHash); this.excludedId = excludedId; } @@ -88,8 +89,8 @@ public class RedissonLocalCachedMap extends RedissonMap implements R return excludedId; } - public byte[] getKeyHash() { - return keyHash; + public Collection getKeyHashes() { + return keyHashes; } } @@ -215,6 +216,9 @@ public class RedissonLocalCachedMap extends RedissonMap implements R if (options.getEvictionPolicy() == EvictionPolicy.LFU) { cache = new LFUCacheMap(options.getCacheSize(), options.getTimeToLiveInMillis(), options.getMaxIdleInMillis()); } + if (options.getEvictionPolicy() == EvictionPolicy.SOFT) { + cache = new SoftCacheMap(options.getTimeToLiveInMillis(), options.getMaxIdleInMillis()); + } invalidationTopic = new RedissonTopic(commandExecutor, suffixName(name, "topic")); if (options.isInvalidateEntryOnChange()) { @@ -227,8 +231,10 @@ public class RedissonLocalCachedMap extends RedissonMap implements R if (msg instanceof LocalCachedMapInvalidate) { LocalCachedMapInvalidate invalidateMsg = (LocalCachedMapInvalidate)msg; if (!Arrays.equals(invalidateMsg.getExcludedId(), instanceId)) { - CacheKey key = new CacheKey(invalidateMsg.getKeyHash()); - cache.remove(key); + for (byte[] keyHash : invalidateMsg.getKeyHashes()) { + CacheKey key = new CacheKey(keyHash); + cache.remove(key); + } } } } @@ -722,27 +728,30 @@ public class RedissonLocalCachedMap extends RedissonMap implements R } List params = new ArrayList(map.size()*3); - List msgs = new ArrayList(map.size()); params.add(invalidateEntryOnChange); params.add(map.size()*2); + byte[][] hashes = new byte[map.size()][]; + int i = 0; for (java.util.Map.Entry t : map.entrySet()) { byte[] mapKey = encodeMapKey(t.getKey()); byte[] mapValue = encodeMapValue(t.getValue()); params.add(mapKey); params.add(mapValue); CacheKey cacheKey = toCacheKey(mapKey); - byte[] msgEncoded = encode(new LocalCachedMapInvalidate(instanceId, cacheKey.getKeyHash())); - msgs.add(msgEncoded); + hashes[i] = cacheKey.getKeyHash(); + i++; } - params.addAll(msgs); + + byte[] msgEncoded = encode(new LocalCachedMapInvalidate(instanceId, hashes)); + params.add(msgEncoded); final RPromise result = newPromise(); RFuture future = commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_VOID, "redis.call('hmset', KEYS[1], unpack(ARGV, 3, tonumber(ARGV[2]) + 2));" + "if ARGV[1] == '1' then " - + "for i = tonumber(ARGV[2]) + 3, #ARGV, 1 do " - + "redis.call('publish', KEYS[2], ARGV[i]); " - + "end; " +// + "for i = tonumber(ARGV[2]) + 3, #ARGV, 1 do " + + "redis.call('publish', KEYS[2], ARGV[#ARGV]); " +// + "end; " + "end;", Arrays.asList(getName(), invalidationTopic.getChannelNames().get(0)), params.toArray()); diff --git a/redisson/src/main/java/org/redisson/RedissonReadLock.java b/redisson/src/main/java/org/redisson/RedissonReadLock.java index 8fb3b1202..ebe650c3a 100644 --- a/redisson/src/main/java/org/redisson/RedissonReadLock.java +++ b/redisson/src/main/java/org/redisson/RedissonReadLock.java @@ -65,11 +65,16 @@ public class RedissonReadLock extends RedissonLock implements RLock { "if (mode == false) then " + "redis.call('hset', KEYS[1], 'mode', 'read'); " + "redis.call('hset', KEYS[1], ARGV[2], 1); " + + "redis.call('set', KEYS[1] .. ':' .. ARGV[2] .. ':rwlock_timeout:1', 1); " + + "redis.call('pexpire', KEYS[1] .. ':' .. ARGV[2] .. ':rwlock_timeout:1', ARGV[1]); " + "redis.call('pexpire', KEYS[1], ARGV[1]); " + "return nil; " + "end; " + "if (mode == 'read') or (mode == 'write' and redis.call('hexists', KEYS[1], ARGV[3]) == 1) then " + - "redis.call('hincrby', KEYS[1], ARGV[2], 1); " + + "local ind = redis.call('hincrby', KEYS[1], ARGV[2], 1); " + + "local key = KEYS[1] .. ':' .. ARGV[2] .. ':rwlock_timeout:' .. ind;" + + "redis.call('set', key, 1); " + + "redis.call('pexpire', key, ARGV[1]); " + "redis.call('pexpire', KEYS[1], ARGV[1]); " + "return nil; " + "end;" + @@ -78,43 +83,48 @@ public class RedissonReadLock extends RedissonLock implements RLock { } @Override - public void unlock() { - Boolean opStatus = commandExecutor.evalWrite(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, - "local mode = redis.call('hget', KEYS[1], 'mode'); " + - "if (mode == false) then " + - "redis.call('publish', KEYS[2], ARGV[1]); " + - "return 1; " + - "end; " + -// "if (mode == 'read') then " + - "local lockExists = redis.call('hexists', KEYS[1], ARGV[3]); " + - "if (lockExists == 0) then " + - "return nil;" + - "else " + - "local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " + - "if (counter > 0) then " + - "redis.call('pexpire', KEYS[1], ARGV[2]); " + - "return 0; " + - "else " + - "redis.call('hdel', KEYS[1], ARGV[3]); " + - "if (redis.call('hlen', KEYS[1]) == 1) then " + - "redis.call('del', KEYS[1]); " + - "redis.call('publish', KEYS[2], ARGV[1]); " + - "end; " + - "return 1; "+ - "end; " + - "end; " + -// "end; " + - "return nil; ", - Arrays.asList(getName(), getChannelName()), LockPubSub.unlockMessage, internalLockLeaseTime, getLockName(Thread.currentThread().getId())); - if (opStatus == null) { - throw new IllegalMonitorStateException("attempt to unlock read lock, not locked by current thread by node id: " - + id + " thread-id: " + Thread.currentThread().getId()); - } - if (opStatus) { - cancelExpirationRenewal(); - } + protected RFuture unlockInnerAsync(long threadId) { + return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, + "local mode = redis.call('hget', KEYS[1], 'mode'); " + + "if (mode == false) then " + + "redis.call('publish', KEYS[2], ARGV[1]); " + + "return 1; " + + "end; " + + "local lockExists = redis.call('hexists', KEYS[1], ARGV[2]); " + + "if (lockExists == 0) then " + + "return nil;" + + "end; " + + + "local counter = redis.call('hincrby', KEYS[1], ARGV[2], -1); " + + "if (counter == 0) then " + + "redis.call('hdel', KEYS[1], ARGV[2]); " + + "end;" + + "redis.call('del', KEYS[1] .. ':' .. ARGV[2] .. ':rwlock_timeout:' .. (counter+1)); " + + "if (redis.call('hlen', KEYS[1]) > 1) then " + + "local maxRemainTime = -3; " + + "local keys = redis.call('hkeys', KEYS[1]); " + + "for n, key in ipairs(keys) do " + + "counter = tonumber(redis.call('hget', KEYS[1], key)); " + + "if type(counter) == 'number' then " + + "for i=counter, 1, -1 do " + + "local remainTime = redis.call('pttl', KEYS[1] .. ':' .. key .. ':rwlock_timeout:' .. i); " + + "maxRemainTime = math.max(remainTime, maxRemainTime);" + + "end; " + + "end; " + + "end; " + + + "if maxRemainTime > 0 then " + + "redis.call('pexpire', KEYS[1], maxRemainTime); " + + "return 0; " + + "end;" + + "end; " + + + "redis.call('del', KEYS[1]); " + + "redis.call('publish', KEYS[2], ARGV[1]); " + + "return 1; ", + Arrays.asList(getName(), getChannelName()), LockPubSub.unlockMessage, getLockName(threadId)); } - + @Override public Condition newCondition() { throw new UnsupportedOperationException(); @@ -127,9 +137,8 @@ public class RedissonReadLock extends RedissonLock implements RLock { "redis.call('del', KEYS[1]); " + "redis.call('publish', KEYS[2], ARGV[1]); " + "return 1; " + - "else " + - "return 0; " + - "end;", + "end; " + + "return 0; ", Arrays.asList(getName(), getChannelName()), LockPubSub.unlockMessage); result.addListener(new FutureListener() { diff --git a/redisson/src/main/java/org/redisson/RedissonRemoteService.java b/redisson/src/main/java/org/redisson/RedissonRemoteService.java index 10c03290e..a6eb65a2f 100644 --- a/redisson/src/main/java/org/redisson/RedissonRemoteService.java +++ b/redisson/src/main/java/org/redisson/RedissonRemoteService.java @@ -113,10 +113,10 @@ public class RedissonRemoteService extends BaseRemoteService implements RRemoteS @Override public void operationComplete(Future future) throws Exception { if (!future.isSuccess()) { - log.error("Can't process the remote service request.", future.cause()); if (future.cause() instanceof RedissonShutdownException) { return; } + log.error("Can't process the remote service request.", future.cause()); // re-subscribe after a failed takeAsync subscribe(remoteInterface, requestQueue, executor); return; diff --git a/redisson/src/main/java/org/redisson/RedissonWriteLock.java b/redisson/src/main/java/org/redisson/RedissonWriteLock.java index 8b64ef31e..4a56e5807 100644 --- a/redisson/src/main/java/org/redisson/RedissonWriteLock.java +++ b/redisson/src/main/java/org/redisson/RedissonWriteLock.java @@ -81,46 +81,39 @@ public class RedissonWriteLock extends RedissonLock implements RLock { } @Override - public void unlock() { - Boolean opStatus = commandExecutor.evalWrite(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, - "local mode = redis.call('hget', KEYS[1], 'mode'); " + - "if (mode == false) then " + - "redis.call('publish', KEYS[2], ARGV[1]); " + - "return 1; " + - "end;" + - "if (mode == 'write') then " + - "local lockExists = redis.call('hexists', KEYS[1], ARGV[3]); " + - "if (lockExists == 0) then " + - "return nil;" + - "else " + - "local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " + - "if (counter > 0) then " + - "redis.call('pexpire', KEYS[1], ARGV[2]); " + - "return 0; " + - "else " + - "redis.call('hdel', KEYS[1], ARGV[3]); " + - "if (redis.call('hlen', KEYS[1]) == 1) then " + - "redis.call('del', KEYS[1]); " + - "redis.call('publish', KEYS[2], ARGV[1]); " + - "else " + - // has unlocked read-locks - "redis.call('hset', KEYS[1], 'mode', 'read'); " + - "end; " + - "return 1; "+ - "end; " + - "end; " + - "end; " - + "return nil;", - Arrays.asList(getName(), getChannelName()), LockPubSub.unlockMessage, internalLockLeaseTime, getLockName(Thread.currentThread().getId())); - if (opStatus == null) { - throw new IllegalMonitorStateException("attempt to unlock read lock, not locked by current thread by node id: " - + id + " thread-id: " + Thread.currentThread().getId()); - } - if (opStatus) { - cancelExpirationRenewal(); - } + protected RFuture unlockInnerAsync(long threadId) { + return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, + "local mode = redis.call('hget', KEYS[1], 'mode'); " + + "if (mode == false) then " + + "redis.call('publish', KEYS[2], ARGV[1]); " + + "return 1; " + + "end;" + + "if (mode == 'write') then " + + "local lockExists = redis.call('hexists', KEYS[1], ARGV[3]); " + + "if (lockExists == 0) then " + + "return nil;" + + "else " + + "local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " + + "if (counter > 0) then " + + "redis.call('pexpire', KEYS[1], ARGV[2]); " + + "return 0; " + + "else " + + "redis.call('hdel', KEYS[1], ARGV[3]); " + + "if (redis.call('hlen', KEYS[1]) == 1) then " + + "redis.call('del', KEYS[1]); " + + "redis.call('publish', KEYS[2], ARGV[1]); " + + "else " + + // has unlocked read-locks + "redis.call('hset', KEYS[1], 'mode', 'read'); " + + "end; " + + "return 1; "+ + "end; " + + "end; " + + "end; " + + "return nil;", + Arrays.asList(getName(), getChannelName()), LockPubSub.unlockMessage, internalLockLeaseTime, getLockName(threadId)); } - + @Override public Condition newCondition() { throw new UnsupportedOperationException(); @@ -133,9 +126,8 @@ public class RedissonWriteLock extends RedissonLock implements RLock { "redis.call('del', KEYS[1]); " + "redis.call('publish', KEYS[2], ARGV[1]); " + "return 1; " + - "else " + - "return 0; " + - "end;", + "end; " + + "return 0; ", Arrays.asList(getName(), getChannelName()), LockPubSub.unlockMessage); result.addListener(new FutureListener() { diff --git a/redisson/src/main/java/org/redisson/api/LocalCachedMapOptions.java b/redisson/src/main/java/org/redisson/api/LocalCachedMapOptions.java index 89a600aa5..523cc5ff5 100644 --- a/redisson/src/main/java/org/redisson/api/LocalCachedMapOptions.java +++ b/redisson/src/main/java/org/redisson/api/LocalCachedMapOptions.java @@ -25,7 +25,7 @@ import java.util.concurrent.TimeUnit; */ public class LocalCachedMapOptions { - public enum EvictionPolicy {NONE, LRU, LFU}; + public enum EvictionPolicy {NONE, LRU, LFU, SOFT}; private boolean invalidateEntryOnChange; private EvictionPolicy evictionPolicy; @@ -115,6 +115,7 @@ public class LocalCachedMapOptions { * @param evictionPolicy *

LRU - uses cache with LRU (least recently used) eviction policy. *

LFU - uses cache with LFU (least frequently used) eviction policy. + *

SOFT - uses cache with soft references. The garbage collector will evict items from the cache when the JVM is running out of memory. *

NONE - doesn't use eviction policy, but timeToLive and maxIdleTime params are still working. * @return LocalCachedMapOptions instance */ diff --git a/redisson/src/main/java/org/redisson/api/RedissonClient.java b/redisson/src/main/java/org/redisson/api/RedissonClient.java index 2845c5035..b38746061 100755 --- a/redisson/src/main/java/org/redisson/api/RedissonClient.java +++ b/redisson/src/main/java/org/redisson/api/RedissonClient.java @@ -742,12 +742,28 @@ public interface RedissonClient { * Returns ScheduledExecutorService by name * using provided codec for task, response and request serialization * + * Please use getExecutorService(String name, Codec codec) method instead. + * + * @deprecated - use {@link #getExecutorService(String, Codec)} instead. + * * @param name - name of object * @param codec - codec for task, response and request * @return ScheduledExecutorService object */ + @Deprecated RScheduledExecutorService getExecutorService(Codec codec, String name); + /** + * Returns ScheduledExecutorService by name + * using provided codec for task, response and request serialization + * + * @param name - name of object + * @param codec - codec for task, response and request + * @return ScheduledExecutorService object + * @since 2.8.2 + */ + RScheduledExecutorService getExecutorService(String name, Codec codec); + /** * Returns object for remote operations prefixed with the default name (redisson_remote_service) * diff --git a/redisson/src/main/java/org/redisson/misc/AbstractCacheMap.java b/redisson/src/main/java/org/redisson/cache/AbstractCacheMap.java similarity index 81% rename from redisson/src/main/java/org/redisson/misc/AbstractCacheMap.java rename to redisson/src/main/java/org/redisson/cache/AbstractCacheMap.java index 75fecc81c..1741c6d25 100644 --- a/redisson/src/main/java/org/redisson/misc/AbstractCacheMap.java +++ b/redisson/src/main/java/org/redisson/cache/AbstractCacheMap.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.redisson.misc; +package org.redisson.cache; import java.util.AbstractCollection; import java.util.AbstractMap.SimpleEntry; @@ -37,56 +37,8 @@ import io.netty.util.internal.PlatformDependent; */ public abstract class AbstractCacheMap implements Cache { - public static class CachedValue { - - private final Object key; - private final Object value; - - long ttl; - long maxIdleTime; - - long creationTime; - long lastAccess; - - public CachedValue(Object key, Object value, long ttl, long maxIdleTime) { - this.value = value; - this.ttl = ttl; - this.key = key; - this.maxIdleTime = maxIdleTime; - creationTime = System.currentTimeMillis(); - lastAccess = creationTime; - } - - public boolean isExpired() { - boolean result = false; - long currentTime = System.currentTimeMillis(); - if (ttl != 0 && creationTime + ttl < currentTime) { - result = true; - } - if (maxIdleTime != 0 && lastAccess + maxIdleTime < currentTime) { - result = true; - } - return result; - } - - public Object getKey() { - return key; - } - - public Object getValue() { - lastAccess = System.currentTimeMillis(); - return value; - } - - @Override - public String toString() { - return "CachedValue [key=" + key + ", value=" + value + "]"; - } - - } - final int size; - final ConcurrentMap map = PlatformDependent.newConcurrentHashMap(); + final ConcurrentMap> map = PlatformDependent.newConcurrentHashMap(); private final long timeToLiveInMillis; private final long maxIdleInMillis; @@ -100,11 +52,11 @@ public abstract class AbstractCacheMap implements Cache { this.timeToLiveInMillis = timeToLiveInMillis; } - protected void onValueRead(CachedValue value) { + protected void onValueRead(CachedValue value) { } - protected void onValueRemove(CachedValue value) { + protected void onValueRemove(CachedValue value) { } @@ -137,7 +89,7 @@ public abstract class AbstractCacheMap implements Cache { throw new NullPointerException(); } - CachedValue entry = map.get(key); + CachedValue entry = map.get(key); if (entry == null) { return false; } @@ -161,8 +113,8 @@ public abstract class AbstractCacheMap implements Cache { throw new NullPointerException(); } - for (Map.Entry entry : map.entrySet()) { - CachedValue cachedValue = entry.getValue(); + for (Map.Entry> entry : map.entrySet()) { + CachedValue cachedValue = entry.getValue(); if (cachedValue.getValue().equals(value)) { if (cachedValue.isExpired()) { if (map.remove(cachedValue.getKey(), cachedValue)) { @@ -187,7 +139,7 @@ public abstract class AbstractCacheMap implements Cache { throw new NullPointerException(); } - CachedValue entry = map.get(key); + CachedValue entry = map.get(key); if (entry == null) { return null; } @@ -201,8 +153,7 @@ public abstract class AbstractCacheMap implements Cache { return readValue(entry); } - @SuppressWarnings("unchecked") - protected V readValue(CachedValue entry) { + protected V readValue(CachedValue entry) { onValueRead(entry); return (V) entry.getValue(); } @@ -216,17 +167,16 @@ public abstract class AbstractCacheMap implements Cache { return put(key, value, timeToLiveInMillis, TimeUnit.MILLISECONDS, maxIdleInMillis, TimeUnit.MILLISECONDS); } - @SuppressWarnings("unchecked") @Override public V put(K key, V value, long ttl, TimeUnit ttlUnit, long maxIdleTime, TimeUnit maxIdleUnit) { - CachedValue entry = create(key, value, ttlUnit.toMillis(ttl), maxIdleUnit.toMillis(maxIdleTime)); + CachedValue entry = create(key, value, ttlUnit.toMillis(ttl), maxIdleUnit.toMillis(maxIdleTime)); if (isFull(key)) { if (!removeExpiredEntries()) { onMapFull(); } } onValueCreate(entry); - CachedValue prevCachedValue = map.put(key, entry); + CachedValue prevCachedValue = map.put(key, entry); if (prevCachedValue != null) { onValueRemove(prevCachedValue); if (!prevCachedValue.isExpired()) { @@ -236,17 +186,17 @@ public abstract class AbstractCacheMap implements Cache { return null; } - protected CachedValue create(K key, V value, long ttl, long maxIdleTime) { - return new CachedValue(key, value, ttl, maxIdleTime); + protected CachedValue create(K key, V value, long ttl, long maxIdleTime) { + return new StdCachedValue(key, value, ttl, maxIdleTime); } - protected void onValueCreate(CachedValue entry) { + protected void onValueCreate(CachedValue entry) { } - private boolean removeExpiredEntries() { + protected boolean removeExpiredEntries() { boolean removed = false; // TODO optimize - for (CachedValue value : map.values()) { + for (CachedValue value : map.values()) { if (value.isExpired()) { if (map.remove(value.getKey(), value)) { onValueRemove(value); @@ -280,10 +230,9 @@ public abstract class AbstractCacheMap implements Cache { * (non-Javadoc) * @see java.util.Map#remove(java.lang.Object) */ - @SuppressWarnings("unchecked") @Override public V remove(Object key) { - CachedValue entry = map.remove(key); + CachedValue entry = map.remove(key); if (entry != null) { onValueRemove(entry); if (!entry.isExpired()) { @@ -346,9 +295,9 @@ public abstract class AbstractCacheMap implements Cache { abstract class MapIterator implements Iterator { - final Iterator> keyIterator = map.entrySet().iterator(); + final Iterator>> keyIterator = map.entrySet().iterator(); - Map.Entry mapEntry; + Map.Entry> mapEntry; @Override public boolean hasNext() { @@ -357,7 +306,7 @@ public abstract class AbstractCacheMap implements Cache { } mapEntry = null; while (keyIterator.hasNext()) { - Map.Entry entry = keyIterator.next(); + Map.Entry> entry = keyIterator.next(); if (entry.getValue().isExpired()) { continue; } diff --git a/redisson/src/main/java/org/redisson/misc/Cache.java b/redisson/src/main/java/org/redisson/cache/Cache.java similarity index 96% rename from redisson/src/main/java/org/redisson/misc/Cache.java rename to redisson/src/main/java/org/redisson/cache/Cache.java index 2856da98b..87dcdd863 100644 --- a/redisson/src/main/java/org/redisson/misc/Cache.java +++ b/redisson/src/main/java/org/redisson/cache/Cache.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.redisson.misc; +package org.redisson.cache; import java.util.Map; import java.util.concurrent.TimeUnit; diff --git a/redisson/src/main/java/org/redisson/cache/CachedValue.java b/redisson/src/main/java/org/redisson/cache/CachedValue.java new file mode 100644 index 000000000..069dbc331 --- /dev/null +++ b/redisson/src/main/java/org/redisson/cache/CachedValue.java @@ -0,0 +1,27 @@ +/** + * 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.cache; + +/** + * Created by jribble on 2/20/17. + */ +public interface CachedValue { + boolean isExpired(); + + K getKey(); + + V getValue(); +} diff --git a/redisson/src/main/java/org/redisson/cache/CachedValueReference.java b/redisson/src/main/java/org/redisson/cache/CachedValueReference.java new file mode 100644 index 000000000..c6e0c4bfb --- /dev/null +++ b/redisson/src/main/java/org/redisson/cache/CachedValueReference.java @@ -0,0 +1,34 @@ +/** + * 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.cache; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; + +public class CachedValueReference extends SoftReference { + + private final CachedValue owner; + + public CachedValueReference(CachedValue owner, V referent, ReferenceQueue q) { + super(referent, q); + this.owner = owner; + } + + public CachedValue getOwner() { + return owner; + } + +} diff --git a/redisson/src/main/java/org/redisson/misc/LFUCacheMap.java b/redisson/src/main/java/org/redisson/cache/LFUCacheMap.java similarity index 97% rename from redisson/src/main/java/org/redisson/misc/LFUCacheMap.java rename to redisson/src/main/java/org/redisson/cache/LFUCacheMap.java index 720b46088..c06b21e94 100644 --- a/redisson/src/main/java/org/redisson/misc/LFUCacheMap.java +++ b/redisson/src/main/java/org/redisson/cache/LFUCacheMap.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.redisson.misc; +package org.redisson.cache; import java.util.Map; import java.util.concurrent.ConcurrentNavigableMap; @@ -57,7 +57,7 @@ public class LFUCacheMap extends AbstractCacheMap { } - public static class LFUCachedValue extends CachedValue { + public static class LFUCachedValue extends StdCachedValue { Long id; long accessCount; diff --git a/redisson/src/main/java/org/redisson/misc/LRUCacheMap.java b/redisson/src/main/java/org/redisson/cache/LRUCacheMap.java similarity index 98% rename from redisson/src/main/java/org/redisson/misc/LRUCacheMap.java rename to redisson/src/main/java/org/redisson/cache/LRUCacheMap.java index 8ba4368c1..d90ebf471 100644 --- a/redisson/src/main/java/org/redisson/misc/LRUCacheMap.java +++ b/redisson/src/main/java/org/redisson/cache/LRUCacheMap.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.redisson.misc; +package org.redisson.cache; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; diff --git a/redisson/src/main/java/org/redisson/misc/NoneCacheMap.java b/redisson/src/main/java/org/redisson/cache/NoneCacheMap.java similarity index 97% rename from redisson/src/main/java/org/redisson/misc/NoneCacheMap.java rename to redisson/src/main/java/org/redisson/cache/NoneCacheMap.java index 9b71681f0..db6354222 100644 --- a/redisson/src/main/java/org/redisson/misc/NoneCacheMap.java +++ b/redisson/src/main/java/org/redisson/cache/NoneCacheMap.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.redisson.misc; +package org.redisson.cache; /** * diff --git a/redisson/src/main/java/org/redisson/cache/SoftCacheMap.java b/redisson/src/main/java/org/redisson/cache/SoftCacheMap.java new file mode 100644 index 000000000..d861fb37c --- /dev/null +++ b/redisson/src/main/java/org/redisson/cache/SoftCacheMap.java @@ -0,0 +1,55 @@ +/** + * 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.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * + * @author Nikita Koksharov + * + * @param key + * @param value + */ +public class SoftCacheMap extends AbstractCacheMap { + + private final ReferenceQueue queue = new ReferenceQueue(); + + public SoftCacheMap(long timeToLiveInMillis, long maxIdleInMillis) { + super(0, timeToLiveInMillis, maxIdleInMillis); + } + + protected CachedValue create(K key, V value, long ttl, long maxIdleTime) { + return new SoftCachedValue(key, value, ttl, maxIdleTime, queue); + } + + @Override + protected boolean removeExpiredEntries() { + while (true) { + CachedValueReference value = (CachedValueReference) queue.poll(); + if (value == null) { + break; + } + map.remove(value.getOwner().getKey(), value.getOwner()); + } + return super.removeExpiredEntries(); + } + + @Override + protected void onMapFull() { + } + +} diff --git a/redisson/src/main/java/org/redisson/cache/SoftCachedValue.java b/redisson/src/main/java/org/redisson/cache/SoftCachedValue.java new file mode 100644 index 000000000..efdedc100 --- /dev/null +++ b/redisson/src/main/java/org/redisson/cache/SoftCachedValue.java @@ -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.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * Created by jribble on 2/20/17. + */ + +public class SoftCachedValue extends StdCachedValue implements CachedValue { + + private final CachedValueReference ref; + + public SoftCachedValue(K key, V value, long ttl, long maxIdleTime, ReferenceQueue queue) { + super(key, null, ttl, maxIdleTime); + this.ref = new CachedValueReference(this, value, queue); + } + + @Override + public V getValue() { + super.getValue(); + return ref.get(); + } + +} diff --git a/redisson/src/main/java/org/redisson/cache/StdCachedValue.java b/redisson/src/main/java/org/redisson/cache/StdCachedValue.java new file mode 100644 index 000000000..8d41abdf6 --- /dev/null +++ b/redisson/src/main/java/org/redisson/cache/StdCachedValue.java @@ -0,0 +1,71 @@ +/** + * 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.cache; + +/** + * Created by jribble on 2/20/17. + */ + +public class StdCachedValue implements CachedValue { + + protected final K key; + protected final V value; + + long ttl; + long maxIdleTime; + + long creationTime; + long lastAccess; + + public StdCachedValue(K key, V value, long ttl, long maxIdleTime) { + this.value = value; + this.ttl = ttl; + this.key = key; + this.maxIdleTime = maxIdleTime; + creationTime = System.currentTimeMillis(); + lastAccess = creationTime; + } + + @Override + public boolean isExpired() { + boolean result = false; + long currentTime = System.currentTimeMillis(); + if (ttl != 0 && creationTime + ttl < currentTime) { + result = true; + } + if (maxIdleTime != 0 && lastAccess + maxIdleTime < currentTime) { + result = true; + } + return result; + } + + @Override + public K getKey() { + return key; + } + + @Override + public V getValue() { + lastAccess = System.currentTimeMillis(); + return value; + } + + @Override + public String toString() { + return "CachedValue [key=" + key + ", value=" + value + "]"; + } + +} diff --git a/redisson/src/main/java/org/redisson/client/RedisConnection.java b/redisson/src/main/java/org/redisson/client/RedisConnection.java index 23c64625c..003324b2f 100644 --- a/redisson/src/main/java/org/redisson/client/RedisConnection.java +++ b/redisson/src/main/java/org/redisson/client/RedisConnection.java @@ -38,6 +38,11 @@ import io.netty.util.concurrent.Future; import io.netty.util.concurrent.FutureListener; import io.netty.util.concurrent.ScheduledFuture; +/** + * + * @author Nikita Koksharov + * + */ public class RedisConnection implements RedisCommands { private static final AttributeKey CONNECTION = AttributeKey.valueOf("connection"); diff --git a/redisson/src/main/java/org/redisson/client/RedisPubSubConnection.java b/redisson/src/main/java/org/redisson/client/RedisPubSubConnection.java index 7103d1af0..ea8566ed2 100644 --- a/redisson/src/main/java/org/redisson/client/RedisPubSubConnection.java +++ b/redisson/src/main/java/org/redisson/client/RedisPubSubConnection.java @@ -40,6 +40,11 @@ import io.netty.util.concurrent.Future; import io.netty.util.concurrent.FutureListener; import io.netty.util.internal.PlatformDependent; +/** + * + * @author Nikita Koksharov + * + */ public class RedisPubSubConnection extends RedisConnection { final Queue> listeners = new ConcurrentLinkedQueue>(); diff --git a/redisson/src/main/java/org/redisson/client/handler/CommandDecoder.java b/redisson/src/main/java/org/redisson/client/handler/CommandDecoder.java index b5024789f..b2411e589 100644 --- a/redisson/src/main/java/org/redisson/client/handler/CommandDecoder.java +++ b/redisson/src/main/java/org/redisson/client/handler/CommandDecoder.java @@ -184,7 +184,7 @@ public class CommandDecoder extends ReplayingDecoder { CommandsData commandBatch) { int i = state().getBatchIndex(); - RedisException error = null; + Throwable error = null; while (in.writerIndex() > in.readerIndex()) { CommandData cmd = null; try { @@ -192,12 +192,12 @@ public class CommandDecoder extends ReplayingDecoder { state().setBatchIndex(i); cmd = (CommandData) commandBatch.getCommands().get(i); decode(in, cmd, null, ctx.channel()); - i++; - } catch (IOException e) { + } catch (Exception e) { cmd.tryFailure(e); } + i++; if (!cmd.isSuccess()) { - error = (RedisException) cmd.cause(); + error = cmd.cause(); } } diff --git a/redisson/src/main/java/org/redisson/client/protocol/RedisCommands.java b/redisson/src/main/java/org/redisson/client/protocol/RedisCommands.java index 8ba7e5996..8eaf72bef 100644 --- a/redisson/src/main/java/org/redisson/client/protocol/RedisCommands.java +++ b/redisson/src/main/java/org/redisson/client/protocol/RedisCommands.java @@ -278,7 +278,7 @@ public interface RedisCommands { RedisCommand SET = new RedisCommand("SET", new VoidReplayConvertor(), 2); RedisCommand SETPXNX = new RedisCommand("SET", new BooleanNotNullReplayConvertor(), 2); RedisCommand SETNX = new RedisCommand("SETNX", new BooleanReplayConvertor(), 2); - RedisCommand SETEX = new RedisCommand("SETEX", new VoidReplayConvertor(), 3); + RedisCommand PSETEX = new RedisCommand("PSETEX", new VoidReplayConvertor(), 3); RedisStrictCommand EXISTS_LONG = new RedisStrictCommand("EXISTS"); RedisStrictCommand EXISTS = new RedisStrictCommand("EXISTS", new BooleanReplayConvertor()); diff --git a/redisson/src/main/java/org/redisson/cluster/ClusterConnectionManager.java b/redisson/src/main/java/org/redisson/cluster/ClusterConnectionManager.java index e6c5f7b8b..4c4ee586d 100644 --- a/redisson/src/main/java/org/redisson/cluster/ClusterConnectionManager.java +++ b/redisson/src/main/java/org/redisson/cluster/ClusterConnectionManager.java @@ -671,7 +671,7 @@ public class ClusterConnectionManager extends MasterSlaveConnectionManager { private Collection parsePartitions(List nodes) { Map partitions = new HashMap(); for (ClusterNodeInfo clusterNodeInfo : nodes) { - if (clusterNodeInfo.containsFlag(Flag.NOADDR)) { + if (clusterNodeInfo.containsFlag(Flag.NOADDR) || clusterNodeInfo.containsFlag(Flag.HANDSHAKE)) { // skip it continue; } diff --git a/redisson/src/main/java/org/redisson/codec/AvroJacksonCodec.java b/redisson/src/main/java/org/redisson/codec/AvroJacksonCodec.java index 4994bbd64..cfe4fd526 100644 --- a/redisson/src/main/java/org/redisson/codec/AvroJacksonCodec.java +++ b/redisson/src/main/java/org/redisson/codec/AvroJacksonCodec.java @@ -15,8 +15,16 @@ */ package org.redisson.codec; +import java.io.IOException; +import java.io.InputStream; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.avro.AvroFactory; +import com.fasterxml.jackson.dataformat.avro.AvroMapper; +import com.fasterxml.jackson.dataformat.avro.AvroSchema; /** * Avro binary codec @@ -26,12 +34,42 @@ import com.fasterxml.jackson.dataformat.avro.AvroFactory; */ public class AvroJacksonCodec extends JsonJacksonCodec { - public AvroJacksonCodec() { - super(new ObjectMapper(new AvroFactory())); + public static class AvroExtendedMapper extends AvroMapper { + + private static final long serialVersionUID = -560070554221164163L; + + private final AvroSchema schema; + private final Class type; + + public AvroExtendedMapper(Class type, AvroSchema schema) { + super(); + this.type = type; + this.schema = schema; + } + + @Override + public byte[] writeValueAsBytes(Object value) throws JsonProcessingException { + return writerFor(type).with(schema).writeValueAsBytes(value); + } + + @Override + public T readValue(InputStream src, Class valueType) + throws IOException, JsonParseException, JsonMappingException { + return readerFor(type).with(schema).readValue(src); + } + + } + + public AvroJacksonCodec(Class type, AvroSchema schema) { + super(new AvroExtendedMapper(type, schema)); } public AvroJacksonCodec(ClassLoader classLoader) { super(createObjectMapper(classLoader, new ObjectMapper(new AvroFactory()))); } + @Override + protected void initTypeInclusion(ObjectMapper mapObjectMapper) { + } + } diff --git a/redisson/src/main/java/org/redisson/codec/JsonJacksonCodec.java b/redisson/src/main/java/org/redisson/codec/JsonJacksonCodec.java index 98976b9f0..f31482fa7 100755 --- a/redisson/src/main/java/org/redisson/codec/JsonJacksonCodec.java +++ b/redisson/src/main/java/org/redisson/codec/JsonJacksonCodec.java @@ -93,7 +93,10 @@ public class JsonJacksonCodec implements Codec { public JsonJacksonCodec(ObjectMapper mapObjectMapper) { this.mapObjectMapper = mapObjectMapper; init(mapObjectMapper); - // type info inclusion + initTypeInclusion(mapObjectMapper); + } + + protected void initTypeInclusion(ObjectMapper mapObjectMapper) { TypeResolverBuilder mapTyper = new DefaultTypeResolverBuilder(DefaultTyping.NON_FINAL) { public boolean useForType(JavaType t) { switch (_appliesFor) { diff --git a/redisson/src/main/java/org/redisson/config/BaseMasterSlaveServersConfig.java b/redisson/src/main/java/org/redisson/config/BaseMasterSlaveServersConfig.java index 9965501c8..bdc5d7312 100644 --- a/redisson/src/main/java/org/redisson/config/BaseMasterSlaveServersConfig.java +++ b/redisson/src/main/java/org/redisson/config/BaseMasterSlaveServersConfig.java @@ -31,16 +31,6 @@ public class BaseMasterSlaveServersConfigeach slave node - */ - private int slaveSubscriptionConnectionMinimumIdleSize = 1; - - /** - * Redis 'slave' node maximum subscription (pub/sub) connection pool size for each slave node - */ - private int slaveSubscriptionConnectionPoolSize = 50; - /** * Redis 'slave' node minimum idle connection amount for each slave node */ @@ -62,6 +52,18 @@ public class BaseMasterSlaveServersConfigeach slave node + */ + private int subscriptionConnectionMinimumIdleSize = 1; + + /** + * Redis 'slave' node maximum subscription (pub/sub) connection pool size for each slave node + */ + private int subscriptionConnectionPoolSize = 50; public BaseMasterSlaveServersConfig() { } @@ -71,11 +73,12 @@ public class BaseMasterSlaveServersConfigeach slave node *

* Default is 50 *

- * @see #setSlaveSubscriptionConnectionMinimumIdleSize(int) + * @see #setSubscriptionConnectionMinimumIdleSize(int) * - * @param slaveSubscriptionConnectionPoolSize - pool size + * @param subscriptionConnectionPoolSize - pool size * @return config */ - public T setSlaveSubscriptionConnectionPoolSize(int slaveSubscriptionConnectionPoolSize) { - this.slaveSubscriptionConnectionPoolSize = slaveSubscriptionConnectionPoolSize; + public T setSubscriptionConnectionPoolSize(int subscriptionConnectionPoolSize) { + this.subscriptionConnectionPoolSize = subscriptionConnectionPoolSize; return (T)this; } - public int getSlaveSubscriptionConnectionPoolSize() { - return slaveSubscriptionConnectionPoolSize; + public int getSubscriptionConnectionPoolSize() { + return subscriptionConnectionPoolSize; } + /** * Redis 'slave' node minimum idle connection amount for each slave node *

@@ -188,24 +207,40 @@ public class BaseMasterSlaveServersConfigeach slave node. *

* Default is 1 *

- * @see #setSlaveSubscriptionConnectionPoolSize(int) + * @see #setSubscriptionConnectionPoolSize(int) * - * @param slaveSubscriptionConnectionMinimumIdleSize - pool size + * @param subscriptionConnectionMinimumIdleSize - pool size * @return config */ - public T setSlaveSubscriptionConnectionMinimumIdleSize(int slaveSubscriptionConnectionMinimumIdleSize) { - this.slaveSubscriptionConnectionMinimumIdleSize = slaveSubscriptionConnectionMinimumIdleSize; + public T setSubscriptionConnectionMinimumIdleSize(int subscriptionConnectionMinimumIdleSize) { + this.subscriptionConnectionMinimumIdleSize = subscriptionConnectionMinimumIdleSize; return (T) this; } - public int getSlaveSubscriptionConnectionMinimumIdleSize() { - return slaveSubscriptionConnectionMinimumIdleSize; + public int getSubscriptionConnectionMinimumIdleSize() { + return subscriptionConnectionMinimumIdleSize; } + /** * Set node type used for read operation. *

@@ -222,4 +257,21 @@ public class BaseMasterSlaveServersConfig + * Default is SLAVE + * + * @param subscriptionMode param + * @return config + */ + public T setSubscriptionMode(SubscriptionMode subscriptionMode) { + this.subscriptionMode = subscriptionMode; + return (T) this; + } + public SubscriptionMode getSubscriptionMode() { + return subscriptionMode; + } + + } diff --git a/redisson/src/main/java/org/redisson/config/Config.java b/redisson/src/main/java/org/redisson/config/Config.java index 233778dc0..e5c74e387 100644 --- a/redisson/src/main/java/org/redisson/config/Config.java +++ b/redisson/src/main/java/org/redisson/config/Config.java @@ -556,12 +556,24 @@ public class Config { * Read config object stored in JSON format from File * * @param file object + * @param classLoader class loader * @return config * @throws IOException error */ - public static Config fromJSON(File file) throws IOException { + public static Config fromJSON(File file, ClassLoader classLoader) throws IOException { ConfigSupport support = new ConfigSupport(); - return support.fromJSON(file, Config.class); + return support.fromJSON(file, Config.class, classLoader); + } + + /** + * Read config object stored in JSON format from File + * + * @param file object + * @return config + * @throws IOException error + */ + public static Config fromJSON(File file) throws IOException { + return fromJSON(file, null); } /** @@ -631,8 +643,12 @@ public class Config { * @throws IOException error */ public static Config fromYAML(File file) throws IOException { + return fromYAML(file, null); + } + + public static Config fromYAML(File file, ClassLoader classLoader) throws IOException { ConfigSupport support = new ConfigSupport(); - return support.fromYAML(file, Config.class); + return support.fromYAML(file, Config.class, classLoader); } /** diff --git a/redisson/src/main/java/org/redisson/config/ConfigSupport.java b/redisson/src/main/java/org/redisson/config/ConfigSupport.java index 8d08feb1d..7c55bb5e4 100644 --- a/redisson/src/main/java/org/redisson/config/ConfigSupport.java +++ b/redisson/src/main/java/org/redisson/config/ConfigSupport.java @@ -26,13 +26,16 @@ import java.util.List; import org.redisson.api.RedissonNodeInitializer; import org.redisson.client.codec.Codec; import org.redisson.cluster.ClusterConnectionManager; +import org.redisson.codec.CodecProvider; import org.redisson.connection.ConnectionManager; import org.redisson.connection.ElasticacheConnectionManager; -import org.redisson.connection.ReplicatedConnectionManager; import org.redisson.connection.MasterSlaveConnectionManager; +import org.redisson.connection.ReplicatedConnectionManager; import org.redisson.connection.SentinelConnectionManager; import org.redisson.connection.SingleConnectionManager; import org.redisson.connection.balancer.LoadBalancer; +import org.redisson.liveobject.provider.ResolverProvider; +import org.redisson.misc.URLBuilder; import com.fasterxml.jackson.annotation.JsonFilter; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -45,10 +48,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ser.FilterProvider; import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; +import com.fasterxml.jackson.databind.type.TypeFactory; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; -import org.redisson.codec.CodecProvider; -import org.redisson.liveobject.provider.ResolverProvider; -import org.redisson.misc.URLBuilder; /** * @@ -118,8 +119,8 @@ public class ConfigSupport { } - private final ObjectMapper jsonMapper = createMapper(null); - private final ObjectMapper yamlMapper = createMapper(new YAMLFactory()); + private ObjectMapper jsonMapper = createMapper(null, null); + private ObjectMapper yamlMapper = createMapper(new YAMLFactory(), null); public T fromJSON(String content, Class configType) throws IOException { URLBuilder.replaceURLFactory(); @@ -131,8 +132,13 @@ public class ConfigSupport { } public T fromJSON(File file, Class configType) throws IOException { + return fromJSON(file, configType, null); + } + + public T fromJSON(File file, Class configType, ClassLoader classLoader) throws IOException { URLBuilder.replaceURLFactory(); try { + jsonMapper = createMapper(null, classLoader); return jsonMapper.readValue(file, configType); } finally { URLBuilder.restoreURLFactory(); @@ -192,6 +198,17 @@ public class ConfigSupport { URLBuilder.restoreURLFactory(); } } + + public T fromYAML(File file, Class configType, ClassLoader classLoader) throws IOException { + URLBuilder.replaceURLFactory(); + try { + yamlMapper = createMapper(new YAMLFactory(), classLoader); + return yamlMapper.readValue(file, configType); + } finally { + URLBuilder.restoreURLFactory(); + } + } + public T fromYAML(URL url, Class configType) throws IOException { URLBuilder.replaceURLFactory(); @@ -266,12 +283,12 @@ public class ConfigSupport { if (config.getMasterConnectionPoolSize() < config.getMasterConnectionMinimumIdleSize()) { throw new IllegalArgumentException("masterConnectionPoolSize can't be lower than masterConnectionMinimumIdleSize"); } - if (config.getSlaveSubscriptionConnectionPoolSize() < config.getSlaveSubscriptionConnectionMinimumIdleSize()) { + if (config.getSubscriptionConnectionPoolSize() < config.getSubscriptionConnectionMinimumIdleSize()) { throw new IllegalArgumentException("slaveSubscriptionConnectionMinimumIdleSize can't be lower than slaveSubscriptionConnectionPoolSize"); } } - private ObjectMapper createMapper(JsonFactory mapping) { + private ObjectMapper createMapper(JsonFactory mapping, ClassLoader classLoader) { ObjectMapper mapper = new ObjectMapper(mapping); mapper.addMixIn(MasterSlaveServersConfig.class, MasterSlaveServersConfigMixIn.class); mapper.addMixIn(SingleServerConfig.class, SingleSeverConfigMixIn.class); @@ -285,6 +302,13 @@ public class ConfigSupport { .addFilter("classFilter", SimpleBeanPropertyFilter.filterOutAllExcept()); mapper.setFilterProvider(filterProvider); mapper.setSerializationInclusion(Include.NON_NULL); + + if (classLoader != null) { + TypeFactory tf = TypeFactory.defaultInstance() + .withClassLoader(classLoader); + mapper.setTypeFactory(tf); + } + return mapper; } diff --git a/redisson/src/main/java/org/redisson/config/ReadMode.java b/redisson/src/main/java/org/redisson/config/ReadMode.java index 51cfe3421..320c7dc82 100644 --- a/redisson/src/main/java/org/redisson/config/ReadMode.java +++ b/redisson/src/main/java/org/redisson/config/ReadMode.java @@ -15,6 +15,11 @@ */ package org.redisson.config; +/** + * + * @author Nikita Koksharov + * + */ public enum ReadMode { /** diff --git a/redisson/src/main/java/org/redisson/config/SubscriptionMode.java b/redisson/src/main/java/org/redisson/config/SubscriptionMode.java new file mode 100644 index 000000000..ec1f0495c --- /dev/null +++ b/redisson/src/main/java/org/redisson/config/SubscriptionMode.java @@ -0,0 +1,35 @@ +/** + * 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.config; + +/** + * + * @author Nikita Koksharov + * + */ +public enum SubscriptionMode { + + /** + * Subscribe to slave nodes + */ + SLAVE, + + /** + * Subscribe to master node + */ + MASTER + +} diff --git a/redisson/src/main/java/org/redisson/connection/CountListener.java b/redisson/src/main/java/org/redisson/connection/CountListener.java new file mode 100644 index 000000000..ec449beb3 --- /dev/null +++ b/redisson/src/main/java/org/redisson/connection/CountListener.java @@ -0,0 +1,64 @@ +/** + * 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.connection; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.redisson.api.RFuture; +import org.redisson.misc.RPromise; +import org.redisson.misc.RedissonPromise; + +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.FutureListener; + +/** + * + * @author Nikita Koksharov + * + */ +public class CountListener implements FutureListener { + + private final RPromise res; + + private final AtomicInteger counter; + + public static RPromise create(RFuture... futures) { + RPromise result = new RedissonPromise(); + FutureListener listener = new CountListener(result, futures.length); + for (RFuture future : futures) { + future.addListener(listener); + } + return result; + } + + public CountListener(RPromise res, int amount) { + super(); + this.res = res; + this.counter = new AtomicInteger(amount); + } + + @Override + public void operationComplete(Future future) throws Exception { + if (!future.isSuccess()) { + res.tryFailure(future.cause()); + return; + } + if (counter.decrementAndGet() == 0) { + res.trySuccess(null); + } + } + +} diff --git a/redisson/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java b/redisson/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java index 1069f2885..de0392b13 100644 --- a/redisson/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java +++ b/redisson/src/main/java/org/redisson/connection/MasterSlaveConnectionManager.java @@ -320,7 +320,7 @@ public class MasterSlaveConnectionManager implements ConnectionManager { c.setClientName(cfg.getClientName()); c.setMasterConnectionPoolSize(cfg.getMasterConnectionPoolSize()); c.setSlaveConnectionPoolSize(cfg.getSlaveConnectionPoolSize()); - c.setSlaveSubscriptionConnectionPoolSize(cfg.getSlaveSubscriptionConnectionPoolSize()); + c.setSubscriptionConnectionPoolSize(cfg.getSubscriptionConnectionPoolSize()); c.setSubscriptionsPerConnection(cfg.getSubscriptionsPerConnection()); c.setConnectTimeout(cfg.getConnectTimeout()); c.setIdleConnectionTimeout(cfg.getIdleConnectionTimeout()); @@ -329,8 +329,9 @@ public class MasterSlaveConnectionManager implements ConnectionManager { c.setReconnectionTimeout(cfg.getReconnectionTimeout()); c.setMasterConnectionMinimumIdleSize(cfg.getMasterConnectionMinimumIdleSize()); c.setSlaveConnectionMinimumIdleSize(cfg.getSlaveConnectionMinimumIdleSize()); - c.setSlaveSubscriptionConnectionMinimumIdleSize(cfg.getSlaveSubscriptionConnectionMinimumIdleSize()); + c.setSubscriptionConnectionMinimumIdleSize(cfg.getSubscriptionConnectionMinimumIdleSize()); c.setReadMode(cfg.getReadMode()); + c.setSubscriptionMode(cfg.getSubscriptionMode()); return c; } diff --git a/redisson/src/main/java/org/redisson/connection/MasterSlaveEntry.java b/redisson/src/main/java/org/redisson/connection/MasterSlaveEntry.java index e8d940300..2fab36ea6 100644 --- a/redisson/src/main/java/org/redisson/connection/MasterSlaveEntry.java +++ b/redisson/src/main/java/org/redisson/connection/MasterSlaveEntry.java @@ -37,9 +37,11 @@ import org.redisson.client.protocol.RedisCommands; import org.redisson.cluster.ClusterSlotRange; import org.redisson.config.MasterSlaveServersConfig; import org.redisson.config.ReadMode; +import org.redisson.config.SubscriptionMode; import org.redisson.connection.ClientConnectionsEntry.FreezeReason; import org.redisson.connection.balancer.LoadBalancerManager; import org.redisson.connection.pool.MasterConnectionPool; +import org.redisson.connection.pool.MasterPubSubConnectionPool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -65,6 +67,8 @@ public class MasterSlaveEntry { final MasterConnectionPool writeConnectionHolder; final Set slots = new HashSet(); + + final MasterPubSubConnectionPool pubSubConnectionHolder; final AtomicBoolean active = new AtomicBoolean(true); @@ -79,6 +83,7 @@ public class MasterSlaveEntry { slaveBalancer = new LoadBalancerManager(config, connectionManager, this); writeConnectionHolder = new MasterConnectionPool(config, connectionManager, this); + pubSubConnectionHolder = new MasterPubSubConnectionPool(config, connectionManager, this); } public List> initSlaveBalancer(Collection disconnectedNodes) { @@ -98,8 +103,20 @@ public class MasterSlaveEntry { public RFuture setupMasterEntry(String host, int port) { RedisClient client = connectionManager.createClient(NodeType.MASTER, host, port); - masterEntry = new ClientConnectionsEntry(client, config.getMasterConnectionMinimumIdleSize(), config.getMasterConnectionPoolSize(), - 0, 0, connectionManager, NodeType.MASTER); + masterEntry = new ClientConnectionsEntry( + client, + config.getMasterConnectionMinimumIdleSize(), + config.getMasterConnectionPoolSize(), + config.getSubscriptionConnectionMinimumIdleSize(), + config.getSubscriptionConnectionPoolSize(), + connectionManager, + NodeType.MASTER); + + if (config.getSubscriptionMode() == SubscriptionMode.MASTER) { + RFuture f = writeConnectionHolder.add(masterEntry); + RFuture s = pubSubConnectionHolder.add(masterEntry); + return CountListener.create(s, f); + } return writeConnectionHolder.add(masterEntry); } @@ -307,8 +324,8 @@ public class MasterSlaveEntry { ClientConnectionsEntry entry = new ClientConnectionsEntry(client, this.config.getSlaveConnectionMinimumIdleSize(), this.config.getSlaveConnectionPoolSize(), - this.config.getSlaveSubscriptionConnectionMinimumIdleSize(), - this.config.getSlaveSubscriptionConnectionPoolSize(), connectionManager, mode); + this.config.getSubscriptionConnectionMinimumIdleSize(), + this.config.getSubscriptionConnectionPoolSize(), connectionManager, mode); if (freezed) { synchronized (entry) { entry.setFreezed(freezed); @@ -352,6 +369,7 @@ public class MasterSlaveEntry { @Override public void operationComplete(Future future) throws Exception { writeConnectionHolder.remove(oldMaster); + pubSubConnectionHolder.remove(oldMaster); slaveDown(oldMaster, FreezeReason.MANAGER); // more than one slave available, so master can be removed from slaves @@ -406,10 +424,18 @@ public class MasterSlaveEntry { } RFuture nextPubSubConnection() { + if (config.getSubscriptionMode() == SubscriptionMode.MASTER) { + return pubSubConnectionHolder.get(); + } + return slaveBalancer.nextPubSubConnection(); } public void returnPubSubConnection(PubSubConnectionEntry entry) { + if (config.getSubscriptionMode() == SubscriptionMode.MASTER) { + pubSubConnectionHolder.returnConnection(masterEntry, entry.getConnection()); + return; + } slaveBalancer.returnPubSubConnection(entry.getConnection()); } diff --git a/redisson/src/main/java/org/redisson/connection/SingleConnectionManager.java b/redisson/src/main/java/org/redisson/connection/SingleConnectionManager.java index 8cb088bf3..b965b54b4 100644 --- a/redisson/src/main/java/org/redisson/connection/SingleConnectionManager.java +++ b/redisson/src/main/java/org/redisson/connection/SingleConnectionManager.java @@ -25,6 +25,7 @@ import org.redisson.config.Config; import org.redisson.config.MasterSlaveServersConfig; import org.redisson.config.ReadMode; import org.redisson.config.SingleServerConfig; +import org.redisson.config.SubscriptionMode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -71,15 +72,16 @@ public class SingleConnectionManager extends MasterSlaveConnectionManager { newconfig.setMasterAddress(addr); newconfig.setMasterConnectionPoolSize(cfg.getConnectionPoolSize()); newconfig.setSubscriptionsPerConnection(cfg.getSubscriptionsPerConnection()); - newconfig.setSlaveSubscriptionConnectionPoolSize(cfg.getSubscriptionConnectionPoolSize()); + newconfig.setSubscriptionConnectionPoolSize(cfg.getSubscriptionConnectionPoolSize()); newconfig.setConnectTimeout(cfg.getConnectTimeout()); newconfig.setIdleConnectionTimeout(cfg.getIdleConnectionTimeout()); newconfig.setFailedAttempts(cfg.getFailedAttempts()); newconfig.setReconnectionTimeout(cfg.getReconnectionTimeout()); newconfig.setMasterConnectionMinimumIdleSize(cfg.getConnectionMinimumIdleSize()); - newconfig.setSlaveSubscriptionConnectionMinimumIdleSize(cfg.getSubscriptionConnectionMinimumIdleSize()); + newconfig.setSubscriptionConnectionMinimumIdleSize(cfg.getSubscriptionConnectionMinimumIdleSize()); newconfig.setReadMode(ReadMode.MASTER); + newconfig.setSubscriptionMode(SubscriptionMode.MASTER); return newconfig; } diff --git a/redisson/src/main/java/org/redisson/connection/SingleEntry.java b/redisson/src/main/java/org/redisson/connection/SingleEntry.java index c5b0c7a5a..5fffe82f6 100644 --- a/redisson/src/main/java/org/redisson/connection/SingleEntry.java +++ b/redisson/src/main/java/org/redisson/connection/SingleEntry.java @@ -17,69 +17,22 @@ package org.redisson.connection; import java.net.InetSocketAddress; import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; -import org.redisson.api.NodeType; import org.redisson.api.RFuture; -import org.redisson.client.RedisClient; import org.redisson.client.RedisConnection; -import org.redisson.client.RedisPubSubConnection; import org.redisson.client.protocol.RedisCommand; import org.redisson.cluster.ClusterSlotRange; import org.redisson.config.MasterSlaveServersConfig; -import org.redisson.connection.pool.PubSubConnectionPool; -import org.redisson.connection.pool.SinglePubSubConnectionPool; -import org.redisson.misc.RPromise; - -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureListener; +/** + * + * @author Nikita Koksharov + * + */ public class SingleEntry extends MasterSlaveEntry { - final PubSubConnectionPool pubSubConnectionHolder; - public SingleEntry(Set slotRanges, ConnectionManager connectionManager, MasterSlaveServersConfig config) { super(slotRanges, connectionManager, config); - pubSubConnectionHolder = new SinglePubSubConnectionPool(config, connectionManager, this); - } - - @Override - public RFuture setupMasterEntry(String host, int port) { - RedisClient masterClient = connectionManager.createClient(NodeType.MASTER, host, port); - masterEntry = new ClientConnectionsEntry(masterClient, - config.getMasterConnectionMinimumIdleSize(), - config.getMasterConnectionPoolSize(), - config.getSlaveConnectionMinimumIdleSize(), - config.getSlaveSubscriptionConnectionPoolSize(), connectionManager, NodeType.MASTER); - final RPromise res = connectionManager.newPromise(); - RFuture f = writeConnectionHolder.add(masterEntry); - RFuture s = pubSubConnectionHolder.add(masterEntry); - FutureListener listener = new FutureListener() { - AtomicInteger counter = new AtomicInteger(2); - @Override - public void operationComplete(Future future) throws Exception { - if (!future.isSuccess()) { - res.tryFailure(future.cause()); - return; - } - if (counter.decrementAndGet() == 0) { - res.trySuccess(null); - } - } - }; - f.addListener(listener); - s.addListener(listener); - return res; - } - - @Override - RFuture nextPubSubConnection() { - return pubSubConnectionHolder.get(); - } - - @Override - public void returnPubSubConnection(PubSubConnectionEntry entry) { - pubSubConnectionHolder.returnConnection(masterEntry, entry.getConnection()); } @Override diff --git a/redisson/src/main/java/org/redisson/connection/pool/SinglePubSubConnectionPool.java b/redisson/src/main/java/org/redisson/connection/pool/MasterPubSubConnectionPool.java similarity index 83% rename from redisson/src/main/java/org/redisson/connection/pool/SinglePubSubConnectionPool.java rename to redisson/src/main/java/org/redisson/connection/pool/MasterPubSubConnectionPool.java index 08741a488..00cbac51d 100644 --- a/redisson/src/main/java/org/redisson/connection/pool/SinglePubSubConnectionPool.java +++ b/redisson/src/main/java/org/redisson/connection/pool/MasterPubSubConnectionPool.java @@ -26,15 +26,20 @@ import org.redisson.connection.MasterSlaveEntry; * @author Nikita Koksharov * */ -public class SinglePubSubConnectionPool extends PubSubConnectionPool { +public class MasterPubSubConnectionPool extends PubSubConnectionPool { - public SinglePubSubConnectionPool(MasterSlaveServersConfig config, ConnectionManager connectionManager, + public MasterPubSubConnectionPool(MasterSlaveServersConfig config, ConnectionManager connectionManager, MasterSlaveEntry masterSlaveEntry) { super(config, connectionManager, masterSlaveEntry); } + @Override protected ClientConnectionsEntry getEntry() { return entries.get(0); } + public void remove(ClientConnectionsEntry entry) { + entries.remove(entry); + } + } diff --git a/redisson/src/main/java/org/redisson/connection/pool/PubSubConnectionPool.java b/redisson/src/main/java/org/redisson/connection/pool/PubSubConnectionPool.java index 859623a9b..7051d55a6 100644 --- a/redisson/src/main/java/org/redisson/connection/pool/PubSubConnectionPool.java +++ b/redisson/src/main/java/org/redisson/connection/pool/PubSubConnectionPool.java @@ -47,7 +47,7 @@ public class PubSubConnectionPool extends ConnectionPool @Override protected int getMinimumIdleSize(ClientConnectionsEntry entry) { - return config.getSlaveSubscriptionConnectionMinimumIdleSize(); + return config.getSubscriptionConnectionMinimumIdleSize(); } @Override diff --git a/redisson/src/main/java/org/redisson/misc/URLBuilder.java b/redisson/src/main/java/org/redisson/misc/URLBuilder.java index f27c189db..4a7e0291b 100644 --- a/redisson/src/main/java/org/redisson/misc/URLBuilder.java +++ b/redisson/src/main/java/org/redisson/misc/URLBuilder.java @@ -33,7 +33,36 @@ public class URLBuilder { private static URLStreamHandlerFactory currentFactory; - public static void restoreURLFactory() { + private final static URLStreamHandlerFactory newFactory = new URLStreamHandlerFactory() { + @Override + public URLStreamHandler createURLStreamHandler(String protocol) { + if ("redis".equals(protocol)) { + return new URLStreamHandler() { + @Override + protected URLConnection openConnection(URL u) throws IOException { + throw new UnsupportedOperationException(); + }; + + @Override + protected boolean equals(URL u1, URL u2) { + return u1.toString().equals(u2.toString()); + } + + @Override + protected int hashCode(URL u) { + return u.toString().hashCode(); + } + }; + } + + if (currentFactory != null) { + return currentFactory.createURLStreamHandler(protocol); + } + return null; + } + }; + + public static synchronized void restoreURLFactory() { try { Field field = URL.class.getDeclaredField("factory"); field.setAccessible(true); @@ -43,43 +72,20 @@ public class URLBuilder { } } - public static void replaceURLFactory() { + public static synchronized void replaceURLFactory() { try { Field field = URL.class.getDeclaredField("factory"); field.setAccessible(true); currentFactory = (URLStreamHandlerFactory) field.get(null); - if (currentFactory != null) { + if (currentFactory != null && currentFactory != newFactory) { field.set(null, null); } - URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory() { - @Override - public URLStreamHandler createURLStreamHandler(String protocol) { - if ("redis".equals(protocol)) { - return new URLStreamHandler() { - @Override - protected URLConnection openConnection(URL u) throws IOException { - throw new UnsupportedOperationException(); - }; - - @Override - protected boolean equals(URL u1, URL u2) { - return u1.toString().equals(u2.toString()); - } - - @Override - protected int hashCode(URL u) { - return u.toString().hashCode(); - } - }; - } - - if (currentFactory != null) { - return currentFactory.createURLStreamHandler(protocol); - } - return null; - } - }); + if (currentFactory != newFactory) { + URL.setURLStreamHandlerFactory(newFactory); + } else { + currentFactory = null; + } } catch (Exception e) { throw new IllegalStateException(e); } diff --git a/redisson/src/main/java/org/redisson/pubsub/PublishSubscribe.java b/redisson/src/main/java/org/redisson/pubsub/PublishSubscribe.java index d15bec4c0..eb9a0fce1 100644 --- a/redisson/src/main/java/org/redisson/pubsub/PublishSubscribe.java +++ b/redisson/src/main/java/org/redisson/pubsub/PublishSubscribe.java @@ -34,7 +34,6 @@ import io.netty.util.internal.PlatformDependent; * * @author Nikita Koksharov * - * @param */ abstract class PublishSubscribe> { diff --git a/redisson/src/main/java/org/redisson/reactive/RedissonBucketReactive.java b/redisson/src/main/java/org/redisson/reactive/RedissonBucketReactive.java index 50536c832..0df97e17e 100644 --- a/redisson/src/main/java/org/redisson/reactive/RedissonBucketReactive.java +++ b/redisson/src/main/java/org/redisson/reactive/RedissonBucketReactive.java @@ -45,7 +45,7 @@ public class RedissonBucketReactive extends RedissonExpirableReactive impleme @Override public Publisher set(V value, long timeToLive, TimeUnit timeUnit) { - return commandExecutor.writeReactive(getName(), codec, RedisCommands.SETEX, getName(), timeUnit.toSeconds(timeToLive), value); + return commandExecutor.writeReactive(getName(), codec, RedisCommands.PSETEX, getName(), timeUnit.toMillis(timeToLive), value); } } diff --git a/redisson/src/main/java/org/redisson/spring/misc/BeanMethodInvoker.java b/redisson/src/main/java/org/redisson/spring/misc/BeanMethodInvoker.java new file mode 100644 index 000000000..fe6d8c7e0 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/misc/BeanMethodInvoker.java @@ -0,0 +1,44 @@ +/** + * 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.spring.misc; + +import java.lang.reflect.InvocationTargetException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.support.ArgumentConvertingMethodInvoker; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class BeanMethodInvoker extends ArgumentConvertingMethodInvoker + implements InitializingBean { + + @Override + public void afterPropertiesSet() throws Exception { + prepare(); + try { + invoke(); + } catch (InvocationTargetException ex) { + if (ex.getTargetException() instanceof Exception) { + throw (Exception) ex.getTargetException(); + } + if (ex.getTargetException() instanceof Error) { + throw (Error) ex.getTargetException(); + } + throw ex; + } + } +} diff --git a/redisson/src/main/java/org/redisson/spring/support/AbstractRedissonNamespaceDefinitionParser.java b/redisson/src/main/java/org/redisson/spring/support/AbstractRedissonNamespaceDefinitionParser.java new file mode 100644 index 000000000..313333612 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/AbstractRedissonNamespaceDefinitionParser.java @@ -0,0 +1,84 @@ +/** + * 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.spring.support; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.util.Assert; +import org.w3c.dom.Element; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public abstract class AbstractRedissonNamespaceDefinitionParser + extends AbstractSingleBeanDefinitionParser { + + protected final RedissonNamespaceParserSupport helper; + private final RedissonNamespaceDecorator decorator; + private final String parentRefAttribute; + + protected AbstractRedissonNamespaceDefinitionParser(RedissonNamespaceParserSupport helper, String parentRefAttribute) { + this.helper = helper; + this.parentRefAttribute = parentRefAttribute; + this.decorator = new RedissonNamespaceDefaultDecorator(); + } + + public AbstractRedissonNamespaceDefinitionParser(RedissonNamespaceParserSupport helper, String parentRefAttribute, RedissonNamespaceDecorator decorator) { + this.helper = helper; + this.parentRefAttribute = parentRefAttribute; + this.decorator = decorator; + } + + @Override + protected final void doParse(Element element, BeanDefinitionBuilder builder) { + } + + @Override + protected final void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + Assert.state(helper.isRedissonNS(element), + "Illegal state. " + + this.getClass().getName() + + " can only parse " + + RedissonNamespaceParserSupport.REDISSON_NAMESPACE + + " namespace elements"); + Assert.state(element.hasAttribute(parentRefAttribute), + "Illegal state. property \"" + parentRefAttribute + + "\" is required in the \"" + + helper.getName(element) + + "\" element."); + + helper.populateIdAttribute(element, builder, parserContext); + AbstractBeanDefinition bd = builder.getRawBeanDefinition(); + parseNested(element, parserContext, builder, bd); + decorator.decorate(element, parserContext, builder, helper); + parserContext.getDelegate().parseQualifierElements(element, bd); + if (parserContext.isNested()) { + helper.registerBeanDefinition(builder, element, parserContext); + } + } + + protected abstract void parseNested(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, BeanDefinition bd); + + @Override + protected final boolean shouldGenerateIdAsFallback() { + return true; + } + +} diff --git a/redisson/src/main/java/org/redisson/spring/support/DelayedQueueDecorator.java b/redisson/src/main/java/org/redisson/spring/support/DelayedQueueDecorator.java new file mode 100644 index 000000000..452d4ab5e --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/DelayedQueueDecorator.java @@ -0,0 +1,44 @@ +/** + * 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.spring.support; + +import org.redisson.api.RQueue; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.util.Assert; +import org.w3c.dom.Element; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class DelayedQueueDecorator implements RedissonNamespaceDecorator { + + private static final String DESTINATION_QUEUE_REF = "destination-queue-ref"; + + @Override + public void decorate(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, RedissonNamespaceParserSupport helper) { + Assert.state(element.hasAttribute(DESTINATION_QUEUE_REF), + "Illegal state. property \"" + DESTINATION_QUEUE_REF + + "\" is required in the \"" + + helper.getName(element) + + "\" element."); + helper.addConstructorArgs(new RuntimeBeanReference( + helper.getAttribute(element, DESTINATION_QUEUE_REF)), + RQueue.class, builder); + } +} diff --git a/redisson/src/main/java/org/redisson/spring/support/LocalCachedMapOptionsDecorator.java b/redisson/src/main/java/org/redisson/spring/support/LocalCachedMapOptionsDecorator.java new file mode 100644 index 000000000..16d5c4635 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/LocalCachedMapOptionsDecorator.java @@ -0,0 +1,95 @@ +/** + * 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.spring.support; + +import org.redisson.api.LocalCachedMapOptions; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.parsing.BeanComponentDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.core.Conventions; +import org.springframework.util.Assert; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class LocalCachedMapOptionsDecorator implements RedissonNamespaceDecorator { + + @Override + public void decorate(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, RedissonNamespaceParserSupport helper) { + NodeList list = element.getElementsByTagNameNS( + RedissonNamespaceParserSupport.REDISSON_NAMESPACE, + RedissonNamespaceParserSupport.LOCAL_CACHED_MAP_OPTIONS_ELEMENT); + Element options = null; + String id; + if (list.getLength() == 1) { + options = (Element) list.item(0); + id = invokeOptions(options, parserContext, helper); + for (int i = 0; i < options.getAttributes().getLength(); i++) { + Attr item = (Attr) options.getAttributes().item(i); + if (helper.isEligibleAttribute(item) + && !RedissonNamespaceParserSupport.TIME_TO_LIVE_UNIT_ATTRIBUTE + .equals(item.getLocalName()) + && !RedissonNamespaceParserSupport.MAX_IDLE_UNIT_ATTRIBUTE + .equals(item.getLocalName())) { + helper.invoker(id, + helper.getName(item), + new Object[]{item.getValue()}, + parserContext); + } + } + invokeTimeUnitOptions(options, id, parserContext, helper, + RedissonNamespaceParserSupport.TIME_TO_LIVE_ATTRIBUTE, + RedissonNamespaceParserSupport.TIME_TO_LIVE_UNIT_ATTRIBUTE); + + invokeTimeUnitOptions(options, id, parserContext, helper, + RedissonNamespaceParserSupport.MAX_IDLE_ATTRIBUTE, + RedissonNamespaceParserSupport.MAX_IDLE_UNIT_ATTRIBUTE); + } else { + id = invokeOptions(options, parserContext, helper); + } + helper.addConstructorArgs(new RuntimeBeanReference(id), + LocalCachedMapOptions.class, builder); + } + + private String invokeOptions(Element element, ParserContext parserContext, RedissonNamespaceParserSupport helper) { + BeanComponentDefinition defaultOption + = helper.factoryInvoker(element, LocalCachedMapOptions.class, + "defaults", null, parserContext); + return defaultOption.getName(); + } + + private void invokeTimeUnitOptions(Element element, String id, ParserContext parserContext, RedissonNamespaceParserSupport helper, String timeAttrubute, String timeUnitAttribute) { + if (helper.hasAttribute(element, timeUnitAttribute)) { + Assert.state( + helper.hasAttribute(element, timeAttrubute), + "Missing \"" + timeAttrubute + "\" attribute in \"" + + RedissonNamespaceParserSupport.LOCAL_CACHED_MAP_OPTIONS_ELEMENT + + "\" element."); + helper.invoker(id, + Conventions.attributeNameToPropertyName(timeAttrubute), + new Object[]{ + Integer.parseInt( + helper.getAttribute(element, timeAttrubute)), + helper.getAttribute(element, timeUnitAttribute)}, + parserContext); + } + } +} diff --git a/redisson/src/main/java/org/redisson/spring/support/ReadWriteLockDecorator.java b/redisson/src/main/java/org/redisson/spring/support/ReadWriteLockDecorator.java new file mode 100644 index 000000000..6f36c8c59 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/ReadWriteLockDecorator.java @@ -0,0 +1,48 @@ +/** + * 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.spring.support; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class ReadWriteLockDecorator implements RedissonNamespaceDecorator { + + + @Override + public void decorate(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, RedissonNamespaceParserSupport helper) { + parseNested(element, RedissonNamespaceParserSupport.READ_LOCK_ELEMENT, parserContext, builder, helper); + parseNested(element, RedissonNamespaceParserSupport.WRITE_LOCK_ELEMENT, parserContext, builder, helper); + } + + private void parseNested(Element element, String eltType, ParserContext parserContext, BeanDefinitionBuilder builder, RedissonNamespaceParserSupport helper) { + NodeList list = element.getElementsByTagNameNS( + RedissonNamespaceParserSupport.REDISSON_NAMESPACE, eltType); + if (list.getLength() == 1) { + Element elt = (Element) list.item(0); + helper.setAttribute(elt, RedissonNamespaceParserSupport.READ_WRITE_LOCK_REF_ATTRIBUTE, + helper.getAttribute(element, + RedissonNamespaceParserSupport.ID_ATTRIBUTE)); + parserContext.getDelegate() + .parseCustomElement(elt, builder.getRawBeanDefinition()); + } + } +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedisDefinitionParser.java b/redisson/src/main/java/org/redisson/spring/support/RedisDefinitionParser.java new file mode 100644 index 000000000..6c02eee29 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedisDefinitionParser.java @@ -0,0 +1,78 @@ +/** + * 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.spring.support; + +import org.redisson.client.RedisClient; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public final class RedisDefinitionParser + extends AbstractSimpleBeanDefinitionParser { + + private static final String HOST_ATTRIBUTE = "host"; + private static final String PORT_ATTRIBUTE = "port"; + private static final String CONNECTION_TIMEOUT_ATTRIBUTE = "connectionTimeout"; + private static final String COMMAND_TIMEOUT_ATTRIBUTE = "commandTimeout"; + + private final RedissonNamespaceParserSupport helper; + + public RedisDefinitionParser(RedissonNamespaceParserSupport helper) { + this.helper = helper; + } + + @Override + protected Class getBeanClass(Element element) { + return RedisClient.class; + } + + @Override + protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + builder.getRawBeanDefinition().setBeanClass(RedisClient.class); + helper.addConstructorArgs(element, + HOST_ATTRIBUTE, String.class, builder); + helper.addConstructorArgs(element, + PORT_ATTRIBUTE, int.class, builder); + helper.addConstructorArgs(element, + CONNECTION_TIMEOUT_ATTRIBUTE, int.class, builder); + helper.addConstructorArgs(element, + COMMAND_TIMEOUT_ATTRIBUTE, int.class, builder); + builder.setDestroyMethodName("shutdown"); + parserContext.getDelegate().parseQualifierElements(element, + builder.getRawBeanDefinition()); + } + + @Override + protected boolean shouldGenerateIdAsFallback() { + return true; + } + + @Override + protected boolean isEligibleAttribute(String attributeName) { + return helper.isEligibleAttribute(attributeName); + } + + @Override + protected boolean isEligibleAttribute(Attr attribute, ParserContext parserContext) { + return helper.isEligibleAttribute(attribute); + } +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonDefinitionParser.java b/redisson/src/main/java/org/redisson/spring/support/RedissonDefinitionParser.java new file mode 100644 index 000000000..7beef7354 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonDefinitionParser.java @@ -0,0 +1,210 @@ +/** + * 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.spring.support; + +import java.util.List; +import org.redisson.Redisson; +import org.redisson.config.Config; +import org.redisson.misc.URLBuilder; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.parsing.BeanComponentDefinition; +import org.springframework.beans.factory.parsing.CompositeComponentDefinition; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.core.Conventions; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; +import org.springframework.util.xml.DomUtils; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public final class RedissonDefinitionParser + implements BeanDefinitionParser { + + public static final String ID_ATTRIBUTE = "id"; + public static final String NAME_ATTRIBUTE = "name"; + private static final String REF_SUFFIX = "-ref"; + private static final String REDISSON_REF = "redisson-ref"; + + static enum ConfigType { + singleServer, + sentinelServers, + replicatedServers, + masterSlaveServers, + clusterServers; + + public static boolean contains(String type) { + try { + valueOf(type); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } + } + + static enum AddressType { + slaveAddress, + sentinelAddress, + nodeAddress; + + public static boolean contains(String type) { + try { + valueOf(type); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } + } + + private final RedissonNamespaceParserSupport helper; + + RedissonDefinitionParser(RedissonNamespaceParserSupport helper) { + this.helper = helper; + } + + private void parseChildElements(Element element, String parentId, String redissonRef, BeanDefinitionBuilder redissonDef, ParserContext parserContext) { + if (element.hasChildNodes()) { + CompositeComponentDefinition compositeDef + = new CompositeComponentDefinition(parentId, + parserContext.extractSource(element)); + parserContext.pushContainingComponent(compositeDef); + List childElts = DomUtils.getChildElements(element); + for (Element elt : childElts) { + if(BeanDefinitionParserDelegate + .QUALIFIER_ELEMENT.equals(elt.getLocalName())) { + continue;//parsed elsewhere + } + String localName = parserContext.getDelegate().getLocalName(elt); + localName = Conventions.attributeNameToPropertyName(localName); + if (ConfigType.contains(localName)) { + parseConfigTypes(elt, localName, redissonDef, parserContext); + } else if (AddressType.contains(localName)) { + parseAddressTypes(elt, localName, redissonDef, parserContext); + } else if (helper.isRedissonNS(elt)) { + elt.setAttribute(REDISSON_REF, redissonRef); + parserContext.getDelegate().parseCustomElement(elt); + } + } + parserContext.popContainingComponent(); + } + } + + private void parseConfigTypes(Element element, String configType, BeanDefinitionBuilder redissonDef, ParserContext parserContext) { + BeanDefinitionBuilder builder + = helper.createBeanDefinitionBuilder(element, + parserContext, null); + //Use factory method on the Config bean + AbstractBeanDefinition bd = builder.getRawBeanDefinition(); + bd.setFactoryMethodName("use" + StringUtils.capitalize(configType)); + bd.setFactoryBeanName(parserContext.getContainingComponent().getName()); + String id = parserContext.getReaderContext().generateBeanName(bd); + helper.registerBeanDefinition(builder, id, + helper.parseAliase(element), parserContext); + parseAttributes(element, parserContext, builder); + redissonDef.addDependsOn(id); + parseChildElements(element, id, null, redissonDef, parserContext); + parserContext.getDelegate().parseQualifierElements(element, bd); + } + + private void parseAddressTypes(Element element, String addressType, BeanDefinitionBuilder redissonDef, ParserContext parserContext) { + BeanComponentDefinition invoker = helper.invoker(element, + parserContext.getContainingComponent().getName(), + "add" + StringUtils.capitalize(addressType), + new String[]{element.getAttribute("value")}, + parserContext); + String id = invoker.getName(); + redissonDef.addDependsOn(id); + } + + private void parseAttributes(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + NamedNodeMap attributes = element.getAttributes(); + for (int x = 0; x < attributes.getLength(); x++) { + Attr attribute = (Attr) attributes.item(x); + if (helper.isEligibleAttribute(attribute)) { + String propertyName + = attribute.getLocalName().endsWith(REF_SUFFIX) + ? attribute.getLocalName() + .substring(0, attribute.getLocalName() + .length() - REF_SUFFIX.length()) + : attribute.getLocalName(); + propertyName = Conventions + .attributeNameToPropertyName(propertyName); + Assert.state(StringUtils.hasText(propertyName), + "Illegal property name returned from" + + " 'extractPropertyName(String)': cannot be" + + " null or empty."); + if (attribute.getLocalName().endsWith(REF_SUFFIX)) { + builder.addPropertyReference(propertyName, + attribute.getValue()); + } else { + Object value = attribute.getValue(); + String localName = helper.getName(element); + if ("masterAddress".equals(propertyName) + && ConfigType.masterSlaveServers.name() + .equals(localName)) { + try { + value = URLBuilder.create((String) value); + } catch (Exception e) { + //value may be a placeholder + value = "redis://" + value; + } + } + builder.addPropertyValue(propertyName, value); + } + } + } + } + + @Override + public BeanDefinition parse(Element element, ParserContext parserContext) { + //Sort out the Config Class + BeanDefinitionBuilder configBuilder + = helper.createBeanDefinitionBuilder(element, parserContext, + Config.class); + String configId = helper.getId(null, configBuilder, parserContext); + parseAttributes(element, parserContext, configBuilder); + helper.registerBeanDefinition(configBuilder, configId, + null, parserContext); + + //Do the main Redisson bean + BeanDefinitionBuilder builder + = helper.createBeanDefinitionBuilder(element, parserContext, + Redisson.class); + builder.setFactoryMethod("create"); + builder.setDestroyMethodName("shutdown"); + builder.addConstructorArgReference(configId); + parserContext.getDelegate().parseQualifierElements(element, + builder.getRawBeanDefinition()); + String id = helper.getId(element, builder, parserContext); + parseAttributes(element, parserContext, configBuilder); + //Sort out all the nested elements + parseChildElements(element, configId, id, builder, parserContext); + + helper.registerBeanDefinition(builder, id, + helper.parseAliase(element), parserContext); + return builder.getBeanDefinition(); + } +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonGenericObjectDefinitionParser.java b/redisson/src/main/java/org/redisson/spring/support/RedissonGenericObjectDefinitionParser.java new file mode 100644 index 000000000..681e26a3d --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonGenericObjectDefinitionParser.java @@ -0,0 +1,90 @@ +/** + * 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.spring.support; + +import org.redisson.api.RDestroyable; +import org.redisson.client.codec.Codec; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.core.Conventions; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class RedissonGenericObjectDefinitionParser + extends AbstractRedissonNamespaceDefinitionParser { + + private final static String KEY_ATTRIBUTE = "key"; + private final static String TOPIC_ATTRIBUTE = "topic"; + private final static String PATTERN_ATTRIBUTE = "pattern"; + private final static String SERVICE_ATTRIBUTE = "service"; + private final static String CODEC_REF_ATTRIBUTE = "codec-ref"; + private final static String FAIL_LOCK = "fairLock"; + + RedissonGenericObjectDefinitionParser(RedissonNamespaceParserSupport helper) { + super(helper, RedissonNamespaceParserSupport.REDISSON_REF_ATTRIBUTE); + } + + RedissonGenericObjectDefinitionParser(RedissonNamespaceParserSupport helper, RedissonNamespaceDecorator decorator) { + super(helper, + RedissonNamespaceParserSupport.REDISSON_REF_ATTRIBUTE, + decorator); + } + + @Override + protected void parseNested(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, BeanDefinition bd) { + bd.setFactoryBeanName(element.getAttribute( + RedissonNamespaceParserSupport.REDISSON_REF_ATTRIBUTE)); + String typeName + = Conventions.attributeNameToPropertyName(element.getLocalName()); + bd.setFactoryMethodName("get" + StringUtils.capitalize(typeName)); + + helper.addConstructorArgs(element, KEY_ATTRIBUTE, + String.class, builder); + helper.addConstructorArgs(element, TOPIC_ATTRIBUTE, + String.class, builder); + helper.addConstructorArgs(element, PATTERN_ATTRIBUTE, + String.class, builder); + helper.addConstructorArgs(element, SERVICE_ATTRIBUTE, + String.class, builder); + helper.addConstructorArgs(element, CODEC_REF_ATTRIBUTE, + Codec.class, builder); + if (RDestroyable.class.isAssignableFrom(getBeanClass(element))) { + ((AbstractBeanDefinition) bd).setDestroyMethodName("destroy"); + } + } + + @Override + protected Class getBeanClass(Element element) { + String elementName + = Conventions.attributeNameToPropertyName( + element.getLocalName()); + try { + return Class.forName(RedissonNamespaceParserSupport.API_CLASS_PATH_PREFIX + + (StringUtils.capitalize(FAIL_LOCK.equals(elementName) + ? "lock" + : elementName))); + } catch (ClassNotFoundException ex) { + throw new IllegalArgumentException(ex); + } + } + +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonLiveObjectDefinitionParser.java b/redisson/src/main/java/org/redisson/spring/support/RedissonLiveObjectDefinitionParser.java new file mode 100644 index 000000000..38ec7f644 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonLiveObjectDefinitionParser.java @@ -0,0 +1,87 @@ +/** + * 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.spring.support; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.MethodInvokingFactoryBean; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.ManagedList; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Element; +import reactor.core.support.Assert; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class RedissonLiveObjectDefinitionParser + extends AbstractRedissonNamespaceDefinitionParser { + + public RedissonLiveObjectDefinitionParser(RedissonNamespaceParserSupport helper) { + super(helper, RedissonNamespaceParserSupport.LIVE_OBJECT_SERVICE_REF_ATTRIBUTE); + } + + @Override + protected void parseNested(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, BeanDefinition bd) { + Class apiClass; + try { + apiClass = Class.forName(helper.getAttribute(element, + RedissonNamespaceParserSupport.CLASS_ATTRIBUTE)); + } catch (ClassNotFoundException ex) { + throw new IllegalArgumentException( + "The class [" + helper.getAttribute(element, + RedissonNamespaceParserSupport.CLASS_ATTRIBUTE) + + "] specified in \"api-class\" attribute has not " + + "found. Please check the class path.", ex); + } + Assert.state(helper.hasAttribute(element, + RedissonNamespaceParserSupport.OBJECT_ID_ATTRIBUTE) + || helper.hasAttribute(element, + RedissonNamespaceParserSupport.OBJECT_ID_REF_ATTRIBUTE), + "One of \"" + + RedissonNamespaceParserSupport.OBJECT_ID_ATTRIBUTE + + "\" or \"" + + RedissonNamespaceParserSupport.OBJECT_ID_REF_ATTRIBUTE + + "\" attribute is required in the \"" + + RedissonNamespaceParserSupport.LIVE_OBJECT_ELEMENT + + "\" element."); + builder.addPropertyValue("targetObject", new RuntimeBeanReference( + helper.getAttribute(element, + RedissonNamespaceParserSupport.LIVE_OBJECT_SERVICE_REF_ATTRIBUTE))); + builder.addPropertyValue("targetMethod", "get"); + ManagedList args = new ManagedList(); + args.add(apiClass); + if (helper.hasAttribute(element, + RedissonNamespaceParserSupport.OBJECT_ID_ATTRIBUTE)) { + args.add(helper.getAttribute(element, + RedissonNamespaceParserSupport.OBJECT_ID_ATTRIBUTE)); + } + if (helper.hasAttribute(element, + RedissonNamespaceParserSupport.OBJECT_ID_REF_ATTRIBUTE)) { + args.add(new RuntimeBeanReference( + helper.getAttribute(element, + RedissonNamespaceParserSupport.OBJECT_ID_REF_ATTRIBUTE))); + } + builder.addPropertyValue("arguments", args); + } + + @Override + protected Class getBeanClass(Element element) { + return MethodInvokingFactoryBean.class; + } + +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonLiveObjectRegistrationDefinitionParser.java b/redisson/src/main/java/org/redisson/spring/support/RedissonLiveObjectRegistrationDefinitionParser.java new file mode 100644 index 000000000..4090cc0ec --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonLiveObjectRegistrationDefinitionParser.java @@ -0,0 +1,62 @@ +/** + * 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.spring.support; + +import org.redisson.spring.misc.BeanMethodInvoker; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Element; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class RedissonLiveObjectRegistrationDefinitionParser + extends AbstractRedissonNamespaceDefinitionParser { + + public RedissonLiveObjectRegistrationDefinitionParser(RedissonNamespaceParserSupport helper) { + super(helper, + RedissonNamespaceParserSupport.LIVE_OBJECT_SERVICE_REF_ATTRIBUTE); + } + + @Override + protected void parseNested(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, BeanDefinition bd) { + Class apiClass; + try { + apiClass = Class.forName(helper.getAttribute(element, + RedissonNamespaceParserSupport.CLASS_ATTRIBUTE)); + } catch (ClassNotFoundException ex) { + throw new IllegalArgumentException( + "The class [" + helper.getAttribute(element, + RedissonNamespaceParserSupport.CLASS_ATTRIBUTE) + + "] specified in \"api-class\" attribute has not " + + "found. Please check the class path.", ex); + } + builder.addPropertyValue("targetObject", new RuntimeBeanReference( + helper.getAttribute(element, + RedissonNamespaceParserSupport.LIVE_OBJECT_SERVICE_REF_ATTRIBUTE))); + builder.addPropertyValue("targetMethod", "registerClass"); + builder.addPropertyValue("arguments", new Object[] {apiClass}); + } + + @Override + protected Class getBeanClass(Element element) { + return BeanMethodInvoker.class; + } + +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonMultiLockDefinitionParser.java b/redisson/src/main/java/org/redisson/spring/support/RedissonMultiLockDefinitionParser.java new file mode 100644 index 000000000..0cad4c335 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonMultiLockDefinitionParser.java @@ -0,0 +1,102 @@ +/** + * 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.spring.support; + +import java.util.List; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConstructorArgumentValues; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.ManagedList; +import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.core.Conventions; +import org.springframework.util.StringUtils; +import org.springframework.util.xml.DomUtils; +import org.w3c.dom.Element; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class RedissonMultiLockDefinitionParser + extends AbstractRedissonNamespaceDefinitionParser { + + public RedissonMultiLockDefinitionParser(RedissonNamespaceParserSupport helper) { + super(helper, + RedissonNamespaceParserSupport.REDISSON_REF_ATTRIBUTE); + } + + @Override + protected void parseNested(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, BeanDefinition bd) { + bd.setDependsOn(element.getAttribute( + RedissonNamespaceParserSupport.REDISSON_REF_ATTRIBUTE)); + List childElements = DomUtils.getChildElements(element); + for (Element elt : childElements) { + String localName = elt.getLocalName(); + if (BeanDefinitionParserDelegate + .QUALIFIER_ELEMENT.equals(localName)) { + continue;//parsed elsewhere + } + String id; + if (BeanDefinitionParserDelegate.REF_ELEMENT.equals(localName)){ + id = elt.getAttribute( + BeanDefinitionParserDelegate.BEAN_REF_ATTRIBUTE); + } else { + if (!elt.hasAttribute( + RedissonNamespaceParserSupport.REDISSON_REF_ATTRIBUTE)) { + helper.setAttribute(elt, + RedissonNamespaceParserSupport.REDISSON_REF_ATTRIBUTE, + element.getAttribute( + RedissonNamespaceParserSupport.REDISSON_REF_ATTRIBUTE)); + } + helper.populateIdAttribute(elt, builder, parserContext); + parserContext.getDelegate().parseCustomElement(elt, bd); + id = elt.getAttribute( + RedissonNamespaceParserSupport.ID_ATTRIBUTE); + } + ConstructorArgumentValues args + = builder.getRawBeanDefinition() + .getConstructorArgumentValues(); + if (args.getArgumentCount() > 0) { + ConstructorArgumentValues.ValueHolder value + = args.getIndexedArgumentValues().get(0); + ManagedList list; + if (value.getValue() instanceof ManagedList) { + list = (ManagedList) value.getValue(); + } else { + list = new ManagedList(); + list.add(value.getValue()); + value.setValue(list); + value.setType(ManagedList.class.getName()); + } + list.add(new RuntimeBeanReference(id)); + } else { + builder.addConstructorArgReference(id); + } + } + } + + @Override + protected String getBeanClassName(Element element) { + String elementName + = Conventions.attributeNameToPropertyName( + element.getLocalName()); + return RedissonNamespaceParserSupport.IMPL_CLASS_PATH_PREFIX + + StringUtils.capitalize(elementName); + } + +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceDecorator.java b/redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceDecorator.java new file mode 100644 index 000000000..b04c29824 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceDecorator.java @@ -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.spring.support; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Element; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public interface RedissonNamespaceDecorator { + + void decorate(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, RedissonNamespaceParserSupport helper); +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceDefaultDecorator.java b/redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceDefaultDecorator.java new file mode 100644 index 000000000..7aa9bf96c --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceDefaultDecorator.java @@ -0,0 +1,33 @@ +/** + * 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.spring.support; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Element; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class RedissonNamespaceDefaultDecorator implements RedissonNamespaceDecorator { + + @Override + public void decorate(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, RedissonNamespaceParserSupport helper) { + //default is no decoration; + } + +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceHandlerSupport.java b/redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceHandlerSupport.java new file mode 100644 index 000000000..cf14811a0 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceHandlerSupport.java @@ -0,0 +1,144 @@ +/** + * 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.spring.support; + +import org.springframework.beans.factory.xml.NamespaceHandlerSupport; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class RedissonNamespaceHandlerSupport extends NamespaceHandlerSupport { + + @Override + public void init() { + RedissonNamespaceParserSupport helper + = new RedissonNamespaceParserSupport(); + + RedissonGenericObjectDefinitionParser defaultParser + = new RedissonGenericObjectDefinitionParser(helper); + + RedissonReadAndWriteLockDefinitionParser readAndWriteLockParser + = new RedissonReadAndWriteLockDefinitionParser(helper); + + RedissonMultiLockDefinitionParser nestedParser + = new RedissonMultiLockDefinitionParser(helper); + + RedissonNestedElementAwareDecorator readWriteLockDecorator + = new RedissonNestedElementAwareDecorator( + new String[]{ + RedissonNamespaceParserSupport.READ_LOCK_ELEMENT, + RedissonNamespaceParserSupport.WRITE_LOCK_ELEMENT + }, + RedissonNamespaceParserSupport.READ_WRITE_LOCK_REF_ATTRIBUTE); + + RedissonGenericObjectDefinitionParser readWriteLockParser + = new RedissonGenericObjectDefinitionParser(helper, + readWriteLockDecorator); + + RedissonNestedElementAwareDecorator remoteServiceDecorator + = new RedissonNestedElementAwareDecorator( + new String[]{ + RedissonNamespaceParserSupport.RPC_SERVER_ELEMENT, + RedissonNamespaceParserSupport.RPC_CLIENT_ELEMENT + }, + RedissonNamespaceParserSupport.REMOTE_SERVICE_REF_ATTRIBUTE); + + RedissonGenericObjectDefinitionParser remoteServiceParser + = new RedissonGenericObjectDefinitionParser(helper, + remoteServiceDecorator); + + RedissonNestedElementAwareDecorator liveObjectServiceDecorator + = new RedissonNestedElementAwareDecorator( + new String[]{ + RedissonNamespaceParserSupport.LIVE_OBJECT_ELEMENT, + RedissonNamespaceParserSupport.LIVE_OBJECT_REGISTRATION_ELEMENT + }, + RedissonNamespaceParserSupport.LIVE_OBJECT_SERVICE_REF_ATTRIBUTE); + + RedissonGenericObjectDefinitionParser liveObjectServiceParser + = new RedissonGenericObjectDefinitionParser(helper, + liveObjectServiceDecorator); + + //root beans + registerBeanDefinitionParser("client", + new RedissonDefinitionParser(helper)); + registerBeanDefinitionParser("redis", new RedisDefinitionParser(helper)); + //object parsers + registerBeanDefinitionParser("binary-stream", defaultParser); + registerBeanDefinitionParser("geo", defaultParser); + registerBeanDefinitionParser("set-cache", defaultParser); + registerBeanDefinitionParser("map-cache", defaultParser); + registerBeanDefinitionParser("bucket", defaultParser); + registerBeanDefinitionParser("buckets", defaultParser); + registerBeanDefinitionParser("hyper-log-log", defaultParser); + registerBeanDefinitionParser("list", defaultParser); + registerBeanDefinitionParser("list-multimap", defaultParser); + registerBeanDefinitionParser("list-multimap-cache", defaultParser); + registerBeanDefinitionParser("local-cached-map", + new RedissonGenericObjectDefinitionParser(helper, + new LocalCachedMapOptionsDecorator())); + registerBeanDefinitionParser("map", defaultParser); + registerBeanDefinitionParser("set-multimap", defaultParser); + registerBeanDefinitionParser("set-multimap-cache", defaultParser); + registerBeanDefinitionParser("semaphore", defaultParser); + registerBeanDefinitionParser("permit-expirable-semaphore", defaultParser); + registerBeanDefinitionParser("lock", defaultParser); + registerBeanDefinitionParser("fair-lock", defaultParser); + registerBeanDefinitionParser("read-write-lock",readWriteLockParser); + registerBeanDefinitionParser("read-lock", readAndWriteLockParser); + registerBeanDefinitionParser("write-lock", readAndWriteLockParser); + registerBeanDefinitionParser("multi-lock", nestedParser); + registerBeanDefinitionParser("red-lock", nestedParser); + registerBeanDefinitionParser("set", defaultParser); + registerBeanDefinitionParser("sorted-set", defaultParser); + registerBeanDefinitionParser("scored-sorted-set", defaultParser); + registerBeanDefinitionParser("lex-sorted-set", defaultParser); + registerBeanDefinitionParser("topic", defaultParser); + registerBeanDefinitionParser("pattern-topic", defaultParser); + registerBeanDefinitionParser("blocking-fair-queue", defaultParser); + registerBeanDefinitionParser("queue", defaultParser); + registerBeanDefinitionParser("delayed-queue", + new RedissonGenericObjectDefinitionParser(helper, + new DelayedQueueDecorator())); + registerBeanDefinitionParser("priority-queue", defaultParser); + registerBeanDefinitionParser("priority-deque", defaultParser); + registerBeanDefinitionParser("blocking-queue", defaultParser); + registerBeanDefinitionParser("bounded-blocking-queue", defaultParser); + registerBeanDefinitionParser("deque", defaultParser); + registerBeanDefinitionParser("blocking-deque", defaultParser); + registerBeanDefinitionParser("atomic-long", defaultParser); + registerBeanDefinitionParser("atomic-double", defaultParser); + registerBeanDefinitionParser("count-down-latch", defaultParser); + registerBeanDefinitionParser("bit-set", defaultParser); + registerBeanDefinitionParser("bloom-filter", defaultParser); + registerBeanDefinitionParser("script", defaultParser); + registerBeanDefinitionParser("executor-service", defaultParser);//nested unfinished + registerBeanDefinitionParser("remote-service", remoteServiceParser); + registerBeanDefinitionParser("rpc-server", + new RedissonRPCServerDefinitionParser(helper)); + registerBeanDefinitionParser("rpc-client", + new RedissonRPCClientDefinitionParser(helper, + new RemoteInvocationOptionDecorator())); + registerBeanDefinitionParser("keys", defaultParser); + registerBeanDefinitionParser("live-object-service", liveObjectServiceParser); + registerBeanDefinitionParser("live-object", + new RedissonLiveObjectDefinitionParser(helper)); + registerBeanDefinitionParser("live-object-registration", + new RedissonLiveObjectRegistrationDefinitionParser(helper)); + } + +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceParserSupport.java b/redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceParserSupport.java new file mode 100644 index 000000000..d7b2804aa --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceParserSupport.java @@ -0,0 +1,300 @@ +/** + * 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.spring.support; + +import org.redisson.spring.misc.BeanMethodInvoker; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.config.ConstructorArgumentValues; +import org.springframework.beans.factory.config.MethodInvokingFactoryBean; +import org.springframework.beans.factory.parsing.BeanComponentDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.core.Conventions; +import org.springframework.util.StringUtils; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class RedissonNamespaceParserSupport { + + public final static String REDISSON_NAMESPACE + = "http://redisson.org/schema/redisson"; + + static final String API_CLASS_PATH_PREFIX = "org.redisson.api.R"; + static final String IMPL_CLASS_PATH_PREFIX = "org.redisson.Redisson"; + + static final String ID_ATTRIBUTE = "id"; + static final String NAME_ATTRIBUTE = "name"; + static final String REDISSON_REF_ATTRIBUTE = "redisson-ref"; + static final String READ_WRITE_LOCK_REF_ATTRIBUTE = "read-write-lock-ref"; + static final String EXECUTOR_REF_ATTRIBUTE = "executor-ref"; + static final String REMOTE_SERVICE_REF_ATTRIBUTE = "remote-service-ref"; + static final String LIVE_OBJECT_SERVICE_REF_ATTRIBUTE + = "live-object-service-ref"; + static final String OBJECT_ID_REF_ATTRIBUTE = "object-id-ref"; + + static final String MAX_IDLE_ATTRIBUTE = "max-idle"; + static final String TIME_TO_LIVE_ATTRIBUTE = "time-to-live"; + static final String MAX_IDLE_UNIT_ATTRIBUTE = "max-idle-unit"; + static final String TIME_TO_LIVE_UNIT_ATTRIBUTE = "time-to-live-unit"; + static final String CONCURRENT_WORKERS_ATTRIBUTE = "concurrent-workers"; + static final String WITHIN_ATTRIBUTE = "within"; + static final String TIME_UNIT_ATTRIBUTE = "time-unit"; + static final String API_CLASS_ATTRIBUTE = "api-class"; + static final String CLASS_ATTRIBUTE = "class"; + static final String OBJECT_ID_ATTRIBUTE = "object-id"; + + static final String READ_LOCK_ELEMENT = "read-lock"; + static final String WRITE_LOCK_ELEMENT = "write-lock"; + static final String RPC_SERVER_ELEMENT = "rpc-server"; + static final String RPC_CLIENT_ELEMENT = "rpc-client"; + static final String REMOTE_INVOCATION_OPTIONS_ELEMENT + = "remote-invocation-options"; + static final String REMOTE_NO_ACK_ELEMENT = "remote-no-ack"; + static final String REMOTE_ACK_ELEMENT = "remote-ack"; + static final String REMOTE_NO_RESULT_ELEMENT = "remote-no-result"; + static final String REMOTE_RESULT_ELEMENT = "remote-result"; + static final String LOCAL_CACHED_MAP_OPTIONS_ELEMENT + = "local-cached-map-options"; + static final String LIVE_OBJECT_ELEMENT + = "live-object"; + static final String LIVE_OBJECT_REGISTRATION_ELEMENT + = "live-object-registration"; + + public String[] parseAliase(Element element) { + if (element == null) { + return null; + } + String[] aliases = null; + String name = element.getAttribute(NAME_ATTRIBUTE); + if (StringUtils.hasLength(name)) { + aliases = StringUtils.trimArrayElements( + StringUtils.commaDelimitedListToStringArray(name)); + } + return aliases; + } + + public BeanDefinitionBuilder createBeanDefinitionBuilder(Element element, ParserContext parserContext, Class cls) { + BeanDefinitionBuilder builder + = BeanDefinitionBuilder.genericBeanDefinition(); + builder.getRawBeanDefinition().setBeanClass(cls); + builder.getRawBeanDefinition() + .setSource(parserContext.extractSource(element)); + if (parserContext.isNested()) { + builder.setScope(parserContext.getContainingBeanDefinition() + .getScope()); + } + if (parserContext.isDefaultLazyInit()) { + builder.setLazyInit(true); + } + return builder; + } + + public BeanComponentDefinition registerBeanDefinition(BeanDefinitionBuilder builder, String id, String[] aliases, ParserContext parserContext) { + BeanDefinitionHolder holder + = new BeanDefinitionHolder(builder.getBeanDefinition(), id, + aliases); + BeanDefinitionReaderUtils + .registerBeanDefinition(holder, parserContext.getRegistry()); + BeanComponentDefinition componentDefinition + = new BeanComponentDefinition(holder); + parserContext.registerComponent(componentDefinition); + return componentDefinition; + } + + public BeanComponentDefinition registerBeanDefinition(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) { + BeanDefinitionHolder holder + = new BeanDefinitionHolder(builder.getBeanDefinition(), + getId(element, builder, parserContext), + parseAliase(element)); + BeanDefinitionReaderUtils + .registerBeanDefinition(holder, parserContext.getRegistry()); + BeanComponentDefinition componentDefinition + = new BeanComponentDefinition(holder); + parserContext.registerComponent(componentDefinition); + return componentDefinition; + } + + public void addConstructorArgs(Element element, String attribute, Class type, BeanDefinition bd) { + if (element.hasAttribute(attribute)) { + addConstructorArgs(element.getAttribute(attribute), type, bd); + } + } + + public void addConstructorArgs(Object value, Class type, BeanDefinition bd) { + ConstructorArgumentValues.ValueHolder vHolder + = new ConstructorArgumentValues.ValueHolder(value, type.getName()); + ConstructorArgumentValues args + = bd.getConstructorArgumentValues(); + args.addIndexedArgumentValue(args.getArgumentCount(), vHolder); + } + + public void addConstructorArgs(Element element, String attribute, Class type, BeanDefinitionBuilder builder) { + addConstructorArgs(element, attribute, type, builder.getRawBeanDefinition()); + } + + public void addConstructorArgs(Object value, Class type, BeanDefinitionBuilder builder) { + addConstructorArgs(value, type, builder.getRawBeanDefinition()); + } + + public String getName(Node node) { + return Conventions.attributeNameToPropertyName(node.getLocalName()); + } + + public String getId(Element element, BeanDefinitionBuilder builder, ParserContext parserContext) { + String id = element != null + ? element.getAttribute(ID_ATTRIBUTE) + : null; + if (!StringUtils.hasText(id)) { + id = generateId(builder, parserContext); + } + return id; + } + + public String generateId(BeanDefinitionBuilder builder, ParserContext parserContext) { + return parserContext.getReaderContext() + .generateBeanName(builder.getRawBeanDefinition()); + } + + public void populateIdAttribute(Element element, BeanDefinitionBuilder builder, ParserContext parserContext) { + if (element == null) { + return; + } + if (!StringUtils.hasText(element.getAttribute(ID_ATTRIBUTE))) { + element.setAttribute(ID_ATTRIBUTE, + generateId(builder, parserContext)); + } + } + + public BeanComponentDefinition factoryInvoker(Element element, String bean, String method, Object[] args, ParserContext parserContext) { + BeanDefinitionBuilder builder + = preInvoke(element, bean, method, args, parserContext, true); + builder.addPropertyReference("targetObject", bean); + return doInvoke(element, builder, parserContext); + } + + public BeanComponentDefinition factoryInvoker(Element element, Object obj, String method, Object[] args, ParserContext parserContext) { + BeanDefinitionBuilder builder + = preInvoke(element, obj, method, args, parserContext, true); + builder.addPropertyValue("targetObject", obj); + return doInvoke(element, builder, parserContext); + } + + + public BeanComponentDefinition factoryInvoker(String bean, String method, Object[] args, ParserContext parserContext) { + return factoryInvoker(null, bean, method, args, parserContext); + } + + public BeanComponentDefinition factoryInvoker(Object obj, String method, Object[] args, ParserContext parserContext) { + return factoryInvoker(null, obj, method, args, parserContext); + } + + + public BeanComponentDefinition invoker(Element element, String bean, String method, Object[] args, ParserContext parserContext) { + BeanDefinitionBuilder builder + = preInvoke(element, bean, method, args, parserContext, false); + builder.addPropertyReference("targetObject", bean); + return doInvoke(element, builder, parserContext); + } + + public BeanComponentDefinition invoker(Element element, Object obj, String method, Object[] args, ParserContext parserContext) { + BeanDefinitionBuilder builder + = preInvoke(element, obj, method, args, parserContext, false); + builder.addPropertyValue("targetObject", obj); + return doInvoke(element, builder, parserContext); + } + + + public BeanComponentDefinition invoker(String bean, String method, Object[] args, ParserContext parserContext) { + return invoker(null, bean, method, args, parserContext); + } + + public BeanComponentDefinition invoker(Object obj, String method, Object[] args, ParserContext parserContext) { + return invoker(null, obj, method, args, parserContext); + } + + private BeanDefinitionBuilder preInvoke(Element element, Object obj, String method, Object[] args, ParserContext parserContext, boolean factory) { + BeanDefinitionBuilder builder + = createBeanDefinitionBuilder(element, parserContext, + factory + ? MethodInvokingFactoryBean.class + : BeanMethodInvoker.class); + if (obj instanceof Class) { + builder.addPropertyValue("staticMethod", + ((Class) obj).getName() + "." + method); + } else { + builder.addPropertyValue("targetMethod", method); + } + builder.addPropertyValue("arguments", args); + if (element != null) { + parserContext.getDelegate().parseQualifierElements(element, + builder.getRawBeanDefinition()); + } + return builder; + } + + private BeanComponentDefinition doInvoke(Element element, BeanDefinitionBuilder builder, ParserContext parserContext) { + String id = getId(element, builder, parserContext); + return registerBeanDefinition(builder, id, + parseAliase(element), parserContext); + } + + public boolean isEligibleAttribute(String attributeName) { + return !attributeName.equals("xmlns") + && !attributeName.startsWith("xmlns:") + && !ID_ATTRIBUTE.equals(attributeName) + && !NAME_ATTRIBUTE.equals(attributeName); + } + + public boolean isEligibleAttribute(Attr attribute) { + return isEligibleAttribute(attribute.getName()); + } + + public boolean isRedissonNS(Node node) { + return node != null + && REDISSON_NAMESPACE.equals(node.getNamespaceURI()); + } + + public String getAttribute(Element element, String attribute) { + return element.getAttribute(attribute); + } + + public void setAttribute(Element element, String attribute, String value) { + element.setAttribute(attribute, value); + } + + public boolean hasAttribute(Element element, String attribute) { + return element.hasAttribute(attribute); + } + + public boolean hasElement(Element element, String tagName) { + return element.getElementsByTagNameNS( + RedissonNamespaceParserSupport.REDISSON_NAMESPACE, tagName) + .getLength() > 0; + } + + public Element getSingleElement(Element element, String tagName) { + return (Element) element.getElementsByTagNameNS( + RedissonNamespaceParserSupport.REDISSON_NAMESPACE, tagName) + .item(0); + } +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonNestedElementAwareDecorator.java b/redisson/src/main/java/org/redisson/spring/support/RedissonNestedElementAwareDecorator.java new file mode 100644 index 000000000..7ff45b562 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonNestedElementAwareDecorator.java @@ -0,0 +1,62 @@ +/** + * 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.spring.support; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class RedissonNestedElementAwareDecorator implements RedissonNamespaceDecorator { + + private final String[] nestedElements; + private final String referenceAttribute; + + public RedissonNestedElementAwareDecorator(String[] nestedElements, String referenceAttribute) { + this.nestedElements = nestedElements; + this.referenceAttribute = referenceAttribute; + } + + @Override + public void decorate(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, RedissonNamespaceParserSupport helper) { + for (String nestedElement : nestedElements) { + parseNested(element, nestedElement, parserContext, builder, helper); + } + } + + private void parseNested(Element element, String eltType, ParserContext parserContext, BeanDefinitionBuilder builder, RedissonNamespaceParserSupport helper) { + NodeList list = element.getElementsByTagNameNS( + RedissonNamespaceParserSupport.REDISSON_NAMESPACE, eltType); + if (list.getLength() == 1) { + Element elt = (Element) list.item(0); + if (StringUtils.hasText(referenceAttribute)) { + helper.setAttribute(elt, referenceAttribute, + helper.getAttribute(element, + RedissonNamespaceParserSupport.ID_ATTRIBUTE)); + helper.setAttribute(elt, RedissonNamespaceParserSupport.REDISSON_REF_ATTRIBUTE, + helper.getAttribute(element, + RedissonNamespaceParserSupport.REDISSON_REF_ATTRIBUTE)); + } + parserContext.getDelegate() + .parseCustomElement(elt, builder.getRawBeanDefinition()); + } + } +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonRPCClientDefinitionParser.java b/redisson/src/main/java/org/redisson/spring/support/RedissonRPCClientDefinitionParser.java new file mode 100644 index 000000000..35d3258a7 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonRPCClientDefinitionParser.java @@ -0,0 +1,65 @@ +/** + * 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.spring.support; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.MethodInvokingFactoryBean; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Element; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class RedissonRPCClientDefinitionParser + extends AbstractRedissonNamespaceDefinitionParser { + + public RedissonRPCClientDefinitionParser(RedissonNamespaceParserSupport helper, RedissonNamespaceDecorator decorator) { + super(helper, + RedissonNamespaceParserSupport.REMOTE_SERVICE_REF_ATTRIBUTE, + decorator); + } + + @Override + protected void parseNested(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, BeanDefinition bd) { + Class apiClass; + try { + apiClass = Class.forName(helper.getAttribute(element, + RedissonNamespaceParserSupport.API_CLASS_ATTRIBUTE)); + } catch (ClassNotFoundException ex) { + throw new IllegalArgumentException( + "The class [" + helper.getAttribute(element, + RedissonNamespaceParserSupport.API_CLASS_ATTRIBUTE) + + "] specified in \"" + + RedissonNamespaceParserSupport.API_CLASS_ATTRIBUTE + + "\" attribute has not " + + "found. Please check the class path.", ex); + } + builder.addPropertyValue("targetObject", new RuntimeBeanReference( + helper.getAttribute(element, + RedissonNamespaceParserSupport.REMOTE_SERVICE_REF_ATTRIBUTE))); + builder.addPropertyValue("targetMethod", "get"); + builder.addPropertyValue("arguments", new Object[] {apiClass}); + } + + @Override + protected Class getBeanClass(Element element) { + return MethodInvokingFactoryBean.class; + } + +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonRPCServerDefinitionParser.java b/redisson/src/main/java/org/redisson/spring/support/RedissonRPCServerDefinitionParser.java new file mode 100644 index 000000000..d4e6586b8 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonRPCServerDefinitionParser.java @@ -0,0 +1,96 @@ +/** + * 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.spring.support; + +import org.redisson.spring.misc.BeanMethodInvoker; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.ManagedList; +import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; +import reactor.core.support.Assert; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class RedissonRPCServerDefinitionParser + extends AbstractRedissonNamespaceDefinitionParser { + + public RedissonRPCServerDefinitionParser(RedissonNamespaceParserSupport helper) { + super(helper, RedissonNamespaceParserSupport.REMOTE_SERVICE_REF_ATTRIBUTE); + } + + @Override + protected void parseNested(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, BeanDefinition bd) { + Class apiClass; + try { + apiClass = Class.forName(helper.getAttribute(element, + RedissonNamespaceParserSupport.API_CLASS_ATTRIBUTE)); + } catch (ClassNotFoundException ex) { + throw new IllegalArgumentException( + "The class [" + helper.getAttribute(element, + RedissonNamespaceParserSupport.API_CLASS_ATTRIBUTE) + + "] specified in \"" + + RedissonNamespaceParserSupport.API_CLASS_ATTRIBUTE + + "\" attribute has not " + + "found. Please check the class path.", ex); + } + builder.addPropertyValue("targetObject", new RuntimeBeanReference( + helper.getAttribute(element, + RedissonNamespaceParserSupport.REMOTE_SERVICE_REF_ATTRIBUTE))); + builder.addPropertyValue("targetMethod", "register"); + ManagedList args = new ManagedList(); + args.add(apiClass); + args.add(new RuntimeBeanReference( + helper.getAttribute(element, + BeanDefinitionParserDelegate.BEAN_REF_ATTRIBUTE))); + String workers = null; + if (helper.hasAttribute(element, + RedissonNamespaceParserSupport.CONCURRENT_WORKERS_ATTRIBUTE)) { + workers = helper.getAttribute(element, + RedissonNamespaceParserSupport.CONCURRENT_WORKERS_ATTRIBUTE); + } + if (StringUtils.hasText(workers)) { + args.add(Integer.parseInt(workers)); + } + if (helper.hasAttribute(element, + RedissonNamespaceParserSupport.EXECUTOR_REF_ATTRIBUTE)) { + Assert.state(helper.hasAttribute(element, + RedissonNamespaceParserSupport.CONCURRENT_WORKERS_ATTRIBUTE), + "The \"" + + RedissonNamespaceParserSupport.CONCURRENT_WORKERS_ATTRIBUTE + + "\" attribute in \"" + + RedissonNamespaceParserSupport.RPC_SERVER_ELEMENT + + "\" element is required when \"" + + RedissonNamespaceParserSupport.EXECUTOR_REF_ATTRIBUTE + + "\" attribute is specified."); + args.add(new RuntimeBeanReference( + helper.getAttribute(element, + RedissonNamespaceParserSupport.EXECUTOR_REF_ATTRIBUTE))); + } + builder.addPropertyValue("arguments", args); + } + + @Override + protected Class getBeanClass(Element element) { + return BeanMethodInvoker.class; + } + +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonReadAndWriteLockDefinitionParser.java b/redisson/src/main/java/org/redisson/spring/support/RedissonReadAndWriteLockDefinitionParser.java new file mode 100644 index 000000000..248af5430 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonReadAndWriteLockDefinitionParser.java @@ -0,0 +1,55 @@ +/** + * 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.spring.support; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.core.Conventions; +import org.w3c.dom.Element; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class RedissonReadAndWriteLockDefinitionParser + extends AbstractRedissonNamespaceDefinitionParser { + + public RedissonReadAndWriteLockDefinitionParser(RedissonNamespaceParserSupport helper) { + super(helper, + RedissonNamespaceParserSupport.READ_WRITE_LOCK_REF_ATTRIBUTE); + } + + @Override + protected void parseNested(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, BeanDefinition bd) { + bd.setFactoryBeanName(element.getAttribute( + RedissonNamespaceParserSupport.READ_WRITE_LOCK_REF_ATTRIBUTE)); + String typeName + = Conventions.attributeNameToPropertyName(element.getLocalName()); + bd.setFactoryMethodName(typeName); + } + + @Override + protected Class getBeanClass(Element element) { + try { + return Class.forName(RedissonNamespaceParserSupport.API_CLASS_PATH_PREFIX + + "Lock"); + } catch (ClassNotFoundException ex) { + throw new IllegalArgumentException(ex); + } + } + +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RemoteInvocationOptionDecorator.java b/redisson/src/main/java/org/redisson/spring/support/RemoteInvocationOptionDecorator.java new file mode 100644 index 000000000..a622f8e6f --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RemoteInvocationOptionDecorator.java @@ -0,0 +1,116 @@ +/** + * 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.spring.support; + +import java.util.ArrayList; +import java.util.Arrays; +import org.redisson.api.RemoteInvocationOptions; +import org.springframework.beans.MutablePropertyValues; +import org.springframework.beans.PropertyValue; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.parsing.BeanComponentDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.ManagedList; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Element; +import reactor.core.support.Assert; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class RemoteInvocationOptionDecorator implements RedissonNamespaceDecorator { + + @Override + public void decorate(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, RedissonNamespaceParserSupport helper) { + if (helper.hasElement(element, + RedissonNamespaceParserSupport.REMOTE_INVOCATION_OPTIONS_ELEMENT)) { + Element options = helper.getSingleElement(element, + RedissonNamespaceParserSupport.REMOTE_INVOCATION_OPTIONS_ELEMENT); + String optionBeanId = invokeOptions(options, parserContext, helper); + if (helper.hasElement(element, + RedissonNamespaceParserSupport.REMOTE_NO_ACK_ELEMENT)) { + helper.invoker(optionBeanId, "noAck", null, parserContext); + } + if (helper.hasElement(element, + RedissonNamespaceParserSupport.REMOTE_ACK_ELEMENT)) { + Element remoteAck = helper.getSingleElement(element, + RedissonNamespaceParserSupport.REMOTE_ACK_ELEMENT); + Assert.state(helper.hasAttribute(remoteAck, + RedissonNamespaceParserSupport.WITHIN_ATTRIBUTE), + "Missing \"" + + RedissonNamespaceParserSupport.WITHIN_ATTRIBUTE + + "\" attribute in \"" + + RedissonNamespaceParserSupport.REMOTE_ACK_ELEMENT + + "\" element."); + ArrayList args = new ArrayList(2); + args.add(helper.getAttribute(remoteAck, + RedissonNamespaceParserSupport.WITHIN_ATTRIBUTE)); + if (helper.hasAttribute(remoteAck, + RedissonNamespaceParserSupport.TIME_UNIT_ATTRIBUTE)) { + args.add(helper.getAttribute(remoteAck, + RedissonNamespaceParserSupport.TIME_UNIT_ATTRIBUTE)); + } + helper.invoker(optionBeanId, "expectAckWithin", args.toArray(), + parserContext); + } + if (helper.hasElement(element, + RedissonNamespaceParserSupport.REMOTE_NO_RESULT_ELEMENT)) { + helper.invoker(optionBeanId, "noResult", null, parserContext); + } + if (helper.hasElement(element, + RedissonNamespaceParserSupport.REMOTE_RESULT_ELEMENT)) { + Element remoteResult = helper.getSingleElement(element, + RedissonNamespaceParserSupport.REMOTE_RESULT_ELEMENT); + Assert.state(helper.hasAttribute(remoteResult, + RedissonNamespaceParserSupport.WITHIN_ATTRIBUTE), + "Missing \"" + + RedissonNamespaceParserSupport.WITHIN_ATTRIBUTE + + "\" attribute in \"" + + RedissonNamespaceParserSupport.REMOTE_RESULT_ELEMENT + + "\" element."); + ArrayList args = new ArrayList(2); + args.add(helper.getAttribute(remoteResult, + RedissonNamespaceParserSupport.WITHIN_ATTRIBUTE)); + if (helper.hasAttribute(remoteResult, + RedissonNamespaceParserSupport.TIME_UNIT_ATTRIBUTE)) { + args.add(helper.getAttribute(remoteResult, + RedissonNamespaceParserSupport.TIME_UNIT_ATTRIBUTE)); + } + helper.invoker(optionBeanId, "expectResultWithin", args.toArray(), + parserContext); + } + MutablePropertyValues properties = builder.getRawBeanDefinition() + .getPropertyValues(); + PropertyValue propertyValue + = properties.getPropertyValue("arguments"); + ManagedList args = new ManagedList(); + args.addAll(Arrays.asList( + (Object[]) propertyValue.getValue())); + args.add(new RuntimeBeanReference(optionBeanId)); + properties.removePropertyValue("arguments"); + properties.addPropertyValue("arguments", args); + } + } + + private String invokeOptions(Element element, ParserContext parserContext, RedissonNamespaceParserSupport helper) { + BeanComponentDefinition defaultOption + = helper.factoryInvoker(element, RemoteInvocationOptions.class, + "defaults", null, parserContext); + return defaultOption.getName(); + } + +} diff --git a/redisson/src/main/resources/META-INF/spring.handlers b/redisson/src/main/resources/META-INF/spring.handlers new file mode 100644 index 000000000..dd43eb54c --- /dev/null +++ b/redisson/src/main/resources/META-INF/spring.handlers @@ -0,0 +1 @@ +http\://redisson.org/schema/redisson=org.redisson.spring.support.RedissonNamespaceHandlerSupport diff --git a/redisson/src/main/resources/META-INF/spring.schemas b/redisson/src/main/resources/META-INF/spring.schemas new file mode 100644 index 000000000..1753d1003 --- /dev/null +++ b/redisson/src/main/resources/META-INF/spring.schemas @@ -0,0 +1,2 @@ +http\://redisson.org/schema/redisson.xsd=org/redisson/spring/support/redisson-1.0.xsd +http\://redisson.org/schema/redisson-1.0.xsd=org/redisson/spring/support/redisson-1.0.xsd diff --git a/redisson/src/main/resources/META-INF/spring.tooling b/redisson/src/main/resources/META-INF/spring.tooling new file mode 100644 index 000000000..f57ce295c --- /dev/null +++ b/redisson/src/main/resources/META-INF/spring.tooling @@ -0,0 +1,4 @@ +# Tooling related information for the redisson namespace +http\://redisson.org/schema/redisson@name=redisson Namespace +http\://redisson.org/schema/redisson@prefix=redisson +http\://redisson.org/schema/redisson@icon=org/redisson/spring/support/redisson.gif diff --git a/redisson/src/main/resources/org/redisson/spring/support/redisson-1.0.xsd b/redisson/src/main/resources/org/redisson/spring/support/redisson-1.0.xsd new file mode 100644 index 000000000..49d442556 --- /dev/null +++ b/redisson/src/main/resources/org/redisson/spring/support/redisson-1.0.xsd @@ -0,0 +1,2267 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <qualifier> is not used. + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + timeout time + and current connections amount bigger than minimum idle connections pool + size, then it will closed and removed from pool. + Value in milliseconds. + + Default: 10000 + ]]> + + + + + Node.ping and Node.pingAll + operation. Value in milliseconds. + + Default: 1000 + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + failedAttempts. + + Default: 3 + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + each slave + node. + + Default: 10 + ]]> + + + + + each slave + node. + + Default: 64 + ]]> + + + + + each slave + node. + + Default: 10 + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NB: applications must ensure the JVM DNS cache TTL is low enough to + support this. e.g., http://docs.aws.amazon.com/AWSSdkDocsJava/latest/DeveloperGuide/java-dg-jvm-ttl.html + + Default: false + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true then invalidation + message which removes corresponding entry from cache will be sent to all + other RLocalCachedMap instances on each entry update/remove operation. + if false then invalidation message won't be sent. + ]]> + + + + + LRU - uses cache with LRU (least recently used) eviction + policy. +

LFU - uses cache with LFU (least frequently used) + eviction policy. +

SOFT - uses cache with soft references. The garbage + collector will evict items from the cache when the JVM is + running out of memory. JVM flag -XX:SoftRefLRUPolicyMSPerMB=??? + is required to function. +

NONE - doesn't use eviction policy, but timeToLive and + maxIdleTime params are still working. + ]]> + + + + + 0 then local cache is unbounded. + ]]> + + + + + 0 then timeout is not applied. + + Default unit is MILLISECONDS + ]]> + + + + + + + + + + 0 then timeout is not applied. + + Default unit is MILLISECONDS + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NOT mandatory + since the class will also be registered lazily when it is first used. + + All classed registered with the service is stored in a class cache. + + The cache is independent between different RedissonClient instances. When + a class is registered in one RLiveObjectService instance it is also + accessible in another RLiveObjectService instance so long as they are + created by the same RedissonClient instance. + ]]> + + + + + + + + + + + + + + + + + + NOT mandatory + since the class will also be registered lazily when it is first used. + + All classed registered with the service is stored in a class cache. + + The cache is independent between different RedissonClient instances. When + a class is registered in one RLiveObjectService instance it is also + accessible in another RLiveObjectService instance so long as they are + created by the same RedissonClient instance. + + One of "object-id" or "object-id-ref" attribute is required. + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Set eviction + + Redisson distributed Set for Java with eviction support implemented by + separate RSetCache object which extends RSet interface. It also + implements java.util.Set interface. + + Current redis implementation doesn't has set value eviction + functionality. Therefore expired values are cleaned by + org.redisson.EvictionScheduler. It removes 100 expired values at once. + Task launch time tuned automatically and depends on expired entries + amount deleted in previous time and varies between 1 second to 2 hours. + Thus if clean task deletes 100 values each time it will be executed + every second (minimum execution delay). But if current expired values + amount is lower than previous one then execution delay will be increased + by 1.5 times. + ]]> + + + + + + + + + + + Map eviction + + Redisson distributed Map for Java with eviction support implemented by + separate RMapCache object which extends RMap interface. It keeps + elements in insertion order and implements + java.util.concurrent.ConcurrentMap and java.util.Map interfaces. + Redisson has a Spring Cache integration which based on Map and MapCache + objects. + + Current redis implementation doesn't has map entry eviction + functionality. Therefore expired entries are cleaned by + org.redisson.EvictionScheduler. It removes 100 expired entries at once. + Task launch time tuned automatically and depends on expired entries + amount deleted in previous time and varies between 1 second to 2 hours. + Thus if clean task deletes 100 entries each time it will be executed + every second (minimum execution delay). But if current expired entries + amount is lower than previous one then execution delay will be increased + by 1.5 times. + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Map local cache + + In case when a Map is used mostly for read operations and/or network + roundtrips are undesirable. Redisson offers RLocalCachedMap object which + caches Map entries on Redisson side. + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + interface. + Keeps elements uniqueness via element state comparison. + ]]> + + + + + + + + + + + interface. + Keeps elements uniqueness via element state comparison. + ]]> + + + + + + + + + + + + + + + + + interface. + Keeps elements uniqueness via element state comparison. + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Live distributed object (also abbreviated as live object) refers to a + running instance of a distributed multi-party (or peer-to-peer) protocol, + viewed from the object-oriented perspective, as an entity that has a + distinct identity, may encapsulate internal state and threads of + execution, and that exhibits a well-defined externally visible behavior. + + + Redisson Live Object (RLO) realised this idea by mapping all the fields + inside a Java class to a redis hash through a runtime-constructed proxy + class. All the get/set methods of each field are translated to hget/hset + commands operated on the redis hash, making it accessable to/from any + clients connected to the same redis server. As we all know, the field + values of an object represent its state; having them stored in a remote + repository, redis, makes it a distributed object. This object is a + Redisson Live Object. + + By using RLO, sharing an object between applications and/or servers is + the same as sharing one in a standalone application. This removes the + need for serialization and deserialization, and at the same time reduces + the complexity of the programming model: Changes made to one field + is (almost^) immediately accessable to other processes, applications and + servers. (^Redis' eventual consistant replication rule still applies + when connected to slave nodes) + + Since the redis server is a single-threaded application, all field + access to the live object is automatically executed in atomic fashion: a + value will not be changed when you are reading it. + + With RLO, you can treat the redis server as a shared Heap space for all + connected JVMs. + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Define and create a Redisson instance. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Define and create a RedisClient instance. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/redisson/src/main/resources/org/redisson/spring/support/redisson.gif b/redisson/src/main/resources/org/redisson/spring/support/redisson.gif new file mode 100644 index 000000000..0eae52d5f Binary files /dev/null and b/redisson/src/main/resources/org/redisson/spring/support/redisson.gif differ diff --git a/redisson/src/test/java/org/redisson/BaseTest.java b/redisson/src/test/java/org/redisson/BaseTest.java index 2caa0e612..ac6ba763d 100644 --- a/redisson/src/test/java/org/redisson/BaseTest.java +++ b/redisson/src/test/java/org/redisson/BaseTest.java @@ -38,7 +38,9 @@ public abstract class BaseTest { if (redisson == null) { redisson = defaultRedisson; } - redisson.getKeys().flushall(); + if (flushBetweenTests()) { + redisson.getKeys().flushall(); + } } } @@ -74,4 +76,7 @@ public abstract class BaseTest { return Redisson.create(config); } + protected boolean flushBetweenTests() { + return true; + } } diff --git a/redisson/src/test/java/org/redisson/ClusterRunner.java b/redisson/src/test/java/org/redisson/ClusterRunner.java new file mode 100644 index 000000000..b1c11228b --- /dev/null +++ b/redisson/src/test/java/org/redisson/ClusterRunner.java @@ -0,0 +1,99 @@ +package org.redisson; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class ClusterRunner { + + private final LinkedHashMap nodes = new LinkedHashMap<>(); + + public ClusterRunner addNode(RedisRunner runner) { + nodes.put(runner, getRandomId()); + if (!runner.hasOption(RedisRunner.REDIS_OPTIONS.CLUSTER_ENABLED)) { + runner.clusterEnabled(true); + } + if (!runner.hasOption(RedisRunner.REDIS_OPTIONS.CLUSTER_NODE_TIMEOUT)) { + runner.clusterNodeTimeout(5000); + } + if (!runner.hasOption(RedisRunner.REDIS_OPTIONS.PORT)) { + runner.randomPort(1); + runner.port(RedisRunner.findFreePort()); + } + if (!runner.hasOption(RedisRunner.REDIS_OPTIONS.BIND)) { + runner.bind("127.0.0.1"); + } + return this; + } + + public List run() throws IOException, InterruptedException, RedisRunner.FailedToStartRedisException { + ArrayList processes = new ArrayList<>(); + for (RedisRunner runner : nodes.keySet()) { + List options = getClusterConfig(runner); + String confFile = runner.defaultDir() + File.pathSeparator + nodes.get(runner) + ".conf"; + System.out.println("WRITING CONFIG: for " + nodes.get(runner)); + try (PrintWriter printer = new PrintWriter(new FileWriter(confFile))) { + options.stream().forEach((line) -> { + printer.println(line); + System.out.println(line); + }); + } + processes.add(runner.clusterConfigFile(confFile).run()); + } + Thread.sleep(1000); + for (RedisRunner.RedisProcess process : processes) { + if (!process.isAlive()) { + throw new RedisRunner.FailedToStartRedisException(); + } + } + return processes; + } + + private List getClusterConfig(RedisRunner runner) { + String me = runner.getInitialBindAddr() + ":" + runner.getPort(); + List nodeConfig = new ArrayList<>(); + int c = 0; + for (RedisRunner node : nodes.keySet()) { + StringBuilder sb = new StringBuilder(); + String nodeAddr = node.getInitialBindAddr() + ":" + node.getPort(); + sb.append(nodes.get(node)).append(" "); + sb.append(nodeAddr).append(" "); + sb.append(me.equals(nodeAddr) + ? "myself," + : "").append("master -").append(" "); + sb.append("0").append(" "); + sb.append(me.equals(nodeAddr) + ? "0" + : "1").append(" "); + sb.append(c + 1).append(" "); + sb.append("connected "); + sb.append(getSlots(c, nodes.size())); + c++; + nodeConfig.add(sb.toString()); + } + nodeConfig.add("vars currentEpoch 0 lastVoteEpoch 0"); + return nodeConfig; + } + + private String getSlots(int index, int groupNum) { + final double t = 16383; + int start = index == 0 ? 0 : (int) (t / groupNum * index); + int end = index == groupNum - 1 ? (int) t : (int) (t / groupNum * (index + 1)) - 1; + return start + "-" + end; + } + + private String getRandomId() { + final SecureRandom r = new SecureRandom(); + return new BigInteger(160, r).toString(16); + } +} diff --git a/redisson/src/test/java/org/redisson/RedisRunner.java b/redisson/src/test/java/org/redisson/RedisRunner.java index c7bfd0dde..e0990ffae 100644 --- a/redisson/src/test/java/org/redisson/RedisRunner.java +++ b/redisson/src/test/java/org/redisson/RedisRunner.java @@ -2,8 +2,10 @@ package org.redisson; import java.io.BufferedReader; import java.io.File; +import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; +import java.io.PrintWriter; import java.net.Inet4Address; import java.net.ServerSocket; import java.net.URL; @@ -66,7 +68,7 @@ public class RedisRunner { SLAVE_PRIORITY, MIN_SLAVES_TO_WRITE, MIN_SLAVES_MAX_LAG, - REQUREPASS, + REQUIREPASS, RENAME_COMMAND(true), MAXCLIENTS, MAXMEMORY, @@ -103,7 +105,19 @@ public class RedisRunner { CLIENT_OUTPUT_BUFFER_LIMIT$SLAVE, CLIENT_OUTPUT_BUFFER_LIMIT$PUBSUB, HZ, - AOF_REWRITE_INCREMENTAL_FSYNC; + AOF_REWRITE_INCREMENTAL_FSYNC, + PROTECTED_MODE, + SENTINEL, + SENTINEL$ANNOUNCE_IP, + SENTINEL$ANNOUNCE_PORT, + SENTINEL$MONITOR(true), + SENTINEL$AUTH_PASS(true), + SENTINEL$DOWN_AFTER_MILLISECONDS(true), + SENTINEL$PARALLEL_SYNCS(true), + SENTINEL$FAILOVER_TIMEOUT(true), + SENTINEL$NOTIFICATION_SCRIPT(true), + SENTINEL$CLIENT_RECONFIG_SCRIPT(true) + ; private final boolean allowMutiple; @@ -172,7 +186,7 @@ public class RedisRunner { e, A } - + private final LinkedHashMap options = new LinkedHashMap<>(); protected static RedisRunner.RedisProcess defaultRedisInstance; private static int defaultRedisInstanceExitCode; @@ -184,6 +198,8 @@ public class RedisRunner { private int port = 6379; private int retryCount = Integer.MAX_VALUE; private boolean randomPort = false; + private String sentinelFile; + private String clusterFile; { this.options.put(REDIS_OPTIONS.BINARY_PATH, RedissonRuntimeEnvironment.redisBinaryPath); @@ -253,13 +269,38 @@ public class RedisRunner { } public RedisProcess runAndCheck() throws IOException, InterruptedException, FailedToStartRedisException { - RedisProcess rp = runWithOptions(this, options.values().toArray(new String[0])); - if (rp.redisProcess.waitFor(1000, TimeUnit.MILLISECONDS)) { + List args = new ArrayList(options.values()); + if (sentinelFile != null && sentinelFile.length() > 0) { + String confFile = defaultDir + File.pathSeparator + sentinelFile; + try (PrintWriter printer = new PrintWriter(new FileWriter(confFile))) { + args.stream().forEach((arg) -> { + if (arg.contains("--")) { + printer.println(arg.replace("--", "\n\r")); + } + }); + } + args = args.subList(0, 1); + args.add(confFile); + args.add("--sentinel"); + } + RedisProcess rp = runWithOptions(this, args.toArray(new String[0])); + if (!isCluster() + && rp.redisProcess.waitFor(1000, TimeUnit.MILLISECONDS)) { throw new FailedToStartRedisException(); } + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try { + rp.stop(); + } catch (InterruptedException ex) { + } + })); return rp; } + public boolean hasOption(REDIS_OPTIONS option) { + return options.containsKey(option); + } + private void addConfigOption(REDIS_OPTIONS option, Object... args) { StringBuilder sb = new StringBuilder("--") .append(option.toString() @@ -306,7 +347,7 @@ public class RedisRunner { options.remove(REDIS_OPTIONS.PORT); return this; } - + public int getPort() { return this.port; } @@ -440,6 +481,11 @@ public class RedisRunner { return this; } + public RedisRunner slaveof(String masterip, int port) { + addConfigOption(REDIS_OPTIONS.SLAVEOF, masterip, port); + return this; + } + public RedisRunner masterauth(String masterauth) { addConfigOption(REDIS_OPTIONS.MASTERAUTH, masterauth); return this; @@ -506,7 +552,7 @@ public class RedisRunner { } public RedisRunner requirepass(String requirepass) { - addConfigOption(REDIS_OPTIONS.REQUREPASS, requirepass); + addConfigOption(REDIS_OPTIONS.REQUIREPASS, requirepass); return this; } @@ -582,6 +628,7 @@ public class RedisRunner { public RedisRunner clusterConfigFile(String clusterConfigFile) { addConfigOption(REDIS_OPTIONS.CLUSTER_CONFIG_FILE, clusterConfigFile); + this.clusterFile = clusterConfigFile; return this; } @@ -702,7 +749,70 @@ public class RedisRunner { addConfigOption(REDIS_OPTIONS.AOF_REWRITE_INCREMENTAL_FSYNC, convertBoolean(aofRewriteIncrementalFsync)); return this; } + + public RedisRunner protectedMode(boolean protectedMode) { + addConfigOption(REDIS_OPTIONS.PROTECTED_MODE, convertBoolean(protectedMode)); + return this; + } + + public RedisRunner sentinel() { + sentinelFile = "sentinel_conf_" + UUID.randomUUID() + ".conf"; + return this; + } + + public RedisRunner sentinelAnnounceIP(String sentinelAnnounceIP) { + addConfigOption(REDIS_OPTIONS.SENTINEL$ANNOUNCE_IP, sentinelAnnounceIP); + return this; + } + + public RedisRunner sentinelAnnouncePort(int sentinelAnnouncePort) { + addConfigOption(REDIS_OPTIONS.SENTINEL$ANNOUNCE_PORT, sentinelAnnouncePort); + return this; + } + + public RedisRunner sentinelMonitor(String masterName, String ip, int port, int quorum) { + addConfigOption(REDIS_OPTIONS.SENTINEL$MONITOR, masterName, ip, port, quorum); + return this; + } + + public RedisRunner sentinelAuthPass(String masterName, String password) { + addConfigOption(REDIS_OPTIONS.SENTINEL$AUTH_PASS, masterName, password); + return this; + } + + public RedisRunner sentinelDownAfterMilliseconds(String masterName, long downAfterMilliseconds) { + addConfigOption(REDIS_OPTIONS.SENTINEL$DOWN_AFTER_MILLISECONDS, masterName, downAfterMilliseconds); + return this; + } + + public RedisRunner sentinelParallelSyncs(String masterName, int numSlaves) { + addConfigOption(REDIS_OPTIONS.SENTINEL$PARALLEL_SYNCS, masterName, numSlaves); + return this; + } + + public RedisRunner sentinelFailoverTimeout(String masterName, long failoverTimeout) { + addConfigOption(REDIS_OPTIONS.SENTINEL$FAILOVER_TIMEOUT, masterName, failoverTimeout); + return this; + } + + public RedisRunner sentinelNotificationScript(String masterName, String scriptPath) { + addConfigOption(REDIS_OPTIONS.SENTINEL$NOTIFICATION_SCRIPT, masterName, scriptPath); + return this; + } + + public RedisRunner sentinelClientReconfigScript(String masterName, String scriptPath) { + addConfigOption(REDIS_OPTIONS.SENTINEL$CLIENT_RECONFIG_SCRIPT, masterName, scriptPath); + return this; + } + + public boolean isSentinel() { + return this.sentinelFile != null; + } + public boolean isCluster() { + return this.clusterFile != null; + } + public boolean isRandomDir() { return this.randomDir; } @@ -722,14 +832,32 @@ public class RedisRunner { public boolean deleteDBfileDir() { File f = new File(defaultDir); if (f.exists()) { - System.out.println("REDIS RUNNER: Deleting directory " + defaultDir); + System.out.println("REDIS RUNNER: Deleting directory " + f.getAbsolutePath()); + return f.delete(); + } + return false; + } + + public boolean deleteSentinelFile() { + File f = new File(defaultDir + File.pathSeparator + sentinelFile); + if (f.exists()) { + System.out.println("REDIS RUNNER: Deleting sentinel config file " + f.getAbsolutePath()); + return f.delete(); + } + return false; + } + + public boolean deleteClusterFile() { + File f = new File(clusterFile); + if (f.exists()) { + System.out.println("REDIS RUNNER: Deleting cluster config file " + f.getAbsolutePath()); return f.delete(); } return false; } private void makeRandomDefaultDir() { - File f = new File(RedissonRuntimeEnvironment.tempDir + "/" + UUID.randomUUID()); + File f = new File(RedissonRuntimeEnvironment.tempDir + File.pathSeparator + UUID.randomUUID()); if (f.exists()) { makeRandomDefaultDir(); } else { @@ -761,6 +889,12 @@ public class RedisRunner { } redisProcess.destroy(); int exitCode = redisProcess.isAlive() ? redisProcess.waitFor() : redisProcess.exitValue(); + if (runner.isSentinel()) { + runner.deleteSentinelFile(); + } + if (runner.isCluster()) { + runner.deleteClusterFile(); + } if (runner.isRandomDir()) { runner.deleteDBfileDir(); } @@ -799,18 +933,16 @@ public class RedisRunner { public String getRedisServerAddressAndPort() { return getRedisServerBindAddress() + ":" + getRedisServerPort(); } + + public boolean isAlive() { + return redisProcess.isAlive(); + } } public static RedisRunner.RedisProcess startDefaultRedisServerInstance() throws IOException, InterruptedException, FailedToStartRedisException { if (defaultRedisInstance == null) { System.out.println("REDIS RUNNER: Starting up default instance..."); defaultRedisInstance = new RedisRunner().nosave().randomDir().randomPort().run(); - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - try { - shutDownDefaultRedisServerInstance(); - } catch (InterruptedException ex) { - } - })); } return defaultRedisInstance; } @@ -853,12 +985,11 @@ public class RedisRunner { socket = new ServerSocket(0); socket.setReuseAddress(true); int port = socket.getLocalPort(); - try { - socket.close(); - } catch (IOException e) { - // Ignore IOException on close() + if (port > 55535 && isFreePort(port - 10000)) { + return port - 10000; + } else { + return port; } - return port; } catch (IOException e) { } finally { if (socket != null) { @@ -871,9 +1002,27 @@ public class RedisRunner { throw new IllegalStateException("Could not find a free TCP/IP port."); } + public static boolean isFreePort(int port) { + ServerSocket socket = null; + try { + socket = new ServerSocket(port); + socket.setReuseAddress(true); + return true; + } catch (IOException e) { + } finally { + if (socket != null) { + try { + socket.close(); + } catch (IOException e) { + } + } + } + return false; + } + public static class FailedToStartRedisException extends RuntimeException { - private FailedToStartRedisException() { + public FailedToStartRedisException() { } } diff --git a/redisson/src/test/java/org/redisson/RedissonCodecTest.java b/redisson/src/test/java/org/redisson/RedissonCodecTest.java index 372c9130f..03880cc07 100644 --- a/redisson/src/test/java/org/redisson/RedissonCodecTest.java +++ b/redisson/src/test/java/org/redisson/RedissonCodecTest.java @@ -1,12 +1,23 @@ package org.redisson; -import com.fasterxml.jackson.core.type.TypeReference; +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + import org.junit.Assert; import org.junit.Test; +import org.redisson.api.RBucket; import org.redisson.api.RMap; import org.redisson.api.RedissonClient; import org.redisson.client.codec.Codec; import org.redisson.client.codec.JsonJacksonMapValueCodec; +import org.redisson.codec.AvroJacksonCodec; import org.redisson.codec.CborJacksonCodec; import org.redisson.codec.FstCodec; import org.redisson.codec.JsonJacksonCodec; @@ -18,17 +29,11 @@ import org.redisson.codec.SmileJacksonCodec; import org.redisson.codec.SnappyCodec; import org.redisson.config.Config; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static org.assertj.core.api.Assertions.assertThat; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.dataformat.avro.AvroMapper; +import com.fasterxml.jackson.dataformat.avro.AvroSchema; public class RedissonCodecTest extends BaseTest { - private Codec avroCodec = new SmileJacksonCodec(); private Codec smileCodec = new SmileJacksonCodec(); private Codec codec = new SerializationCodec(); private Codec kryoCodec = new KryoCodec(); @@ -76,14 +81,21 @@ public class RedissonCodecTest extends BaseTest { test(redisson); } - + @Test - public void testAvro() { + public void testAvro() throws IOException { + AvroMapper am = new AvroMapper(); + AvroSchema schema = am.schemaFor(TestObject.class); + Codec avroCodec = new AvroJacksonCodec(TestObject.class, schema); + Config config = createConfig(); config.setCodec(avroCodec); RedissonClient redisson = Redisson.create(config); - test(redisson); + RBucket b = redisson.getBucket("bucket"); + b.set(new TestObject("1", "2")); + + assertThat(b.get()).isEqualTo(new TestObject("1", "2")); } @Test diff --git a/redisson/src/test/java/org/redisson/RedissonDelayedQueueTest.java b/redisson/src/test/java/org/redisson/RedissonDelayedQueueTest.java index 8ab730438..2d0194393 100644 --- a/redisson/src/test/java/org/redisson/RedissonDelayedQueueTest.java +++ b/redisson/src/test/java/org/redisson/RedissonDelayedQueueTest.java @@ -1,6 +1,7 @@ package org.redisson; import static org.assertj.core.api.Assertions.assertThat; + import java.util.Arrays; import java.util.concurrent.TimeUnit; @@ -11,6 +12,41 @@ import org.redisson.api.RQueue; public class RedissonDelayedQueueTest extends BaseTest { + @Test + public void testRemove() throws InterruptedException { + RBlockingFairQueue blockingFairQueue = redisson.getBlockingFairQueue("delay_queue"); + RDelayedQueue delayedQueue = redisson.getDelayedQueue(blockingFairQueue); + + delayedQueue.offer("1_1_1", 3, TimeUnit.SECONDS); + delayedQueue.offer("1_1_2", 7, TimeUnit.SECONDS); + assertThat(delayedQueue.contains("1_1_1")).isTrue(); + assertThat(delayedQueue.remove("1_1_1")).isTrue(); + assertThat(delayedQueue.contains("1_1_1")).isFalse(); + + Thread.sleep(9000); + + assertThat(blockingFairQueue).containsOnly("1_1_2"); + } + + @Test + public void testRemoveAll() throws InterruptedException { + RBlockingFairQueue blockingFairQueue = redisson.getBlockingFairQueue("delay_queue"); + RDelayedQueue delayedQueue = redisson.getDelayedQueue(blockingFairQueue); + + delayedQueue.offer("1_1_1", 3, TimeUnit.SECONDS); + delayedQueue.offer("1_1_2", 7, TimeUnit.SECONDS); + assertThat(delayedQueue.contains("1_1_1")).isTrue(); + assertThat(delayedQueue.contains("1_1_2")).isTrue(); + assertThat(delayedQueue.removeAll(Arrays.asList("1_1_1", "1_1_2"))).isTrue(); + assertThat(delayedQueue.contains("1_1_1")).isFalse(); + assertThat(delayedQueue.contains("1_1_2")).isFalse(); + + Thread.sleep(9000); + + assertThat(blockingFairQueue.isEmpty()).isTrue(); + } + + @Test public void testDealyedQueueRetainAll() { RBlockingFairQueue queue1 = redisson.getBlockingFairQueue("test"); @@ -28,7 +64,6 @@ public class RedissonDelayedQueueTest extends BaseTest { dealyedQueue.destroy(); } - @Test public void testDealyedQueueReadAll() { diff --git a/redisson/src/test/java/org/redisson/RedissonLocalCachedMapTest.java b/redisson/src/test/java/org/redisson/RedissonLocalCachedMapTest.java index 9a8044c6b..9cbd71abe 100644 --- a/redisson/src/test/java/org/redisson/RedissonLocalCachedMapTest.java +++ b/redisson/src/test/java/org/redisson/RedissonLocalCachedMapTest.java @@ -18,10 +18,10 @@ import org.redisson.RedissonMapTest.SimpleKey; import org.redisson.RedissonMapTest.SimpleValue; import org.redisson.api.LocalCachedMapOptions; import org.redisson.api.LocalCachedMapOptions.EvictionPolicy; +import org.redisson.cache.Cache; import org.redisson.api.RLocalCachedMap; import org.redisson.api.RMap; import org.redisson.api.RedissonClient; -import org.redisson.misc.Cache; import mockit.Deencapsulation; diff --git a/redisson/src/test/java/org/redisson/RedissonReadWriteLockTest.java b/redisson/src/test/java/org/redisson/RedissonReadWriteLockTest.java index eb2429e49..d6d903a2d 100644 --- a/redisson/src/test/java/org/redisson/RedissonReadWriteLockTest.java +++ b/redisson/src/test/java/org/redisson/RedissonReadWriteLockTest.java @@ -1,5 +1,6 @@ package org.redisson; +import static com.jayway.awaitility.Awaitility.await; import static org.assertj.core.api.Assertions.assertThat; import java.security.SecureRandom; @@ -16,6 +17,84 @@ import org.redisson.api.RReadWriteLock; public class RedissonReadWriteLockTest extends BaseConcurrentTest { + @Test + public void testReadLockLeaseTimeoutDiffThreadsWRR() throws InterruptedException { + RLock writeLock = redisson.getReadWriteLock("my_read_write_lock").writeLock(); + Assert.assertTrue(writeLock.tryLock(1, 10, TimeUnit.SECONDS)); + + final AtomicInteger executed = new AtomicInteger(); + Thread t1 = new Thread(() -> { + RLock readLock = redisson.getReadWriteLock("my_read_write_lock").readLock(); + readLock.lock(); + executed.incrementAndGet(); + }); + + Thread t2 = new Thread(() -> { + RLock readLock = redisson.getReadWriteLock("my_read_write_lock").readLock(); + readLock.lock(); + executed.incrementAndGet(); + }); + + t1.start(); + t2.start(); + + await().atMost(11, TimeUnit.SECONDS).until(() -> executed.get() == 2); + } + + @Test + public void testReadLockLeaseTimeoutDiffThreadsRRW() throws InterruptedException { + new Thread(() -> { + RLock readLock = redisson.getReadWriteLock("my_read_write_lock").readLock(); + try { + Assert.assertTrue(readLock.tryLock(1, 10, TimeUnit.SECONDS)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }).start(); + + Thread.sleep(5000); + + new Thread(() -> { + RLock readLock2 = redisson.getReadWriteLock("my_read_write_lock").readLock(); + try { + Assert.assertTrue(readLock2.tryLock(1, 10, TimeUnit.SECONDS)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + readLock2.unlock(); + }).start(); + + final AtomicBoolean executed = new AtomicBoolean(); + new Thread(() -> { + RLock writeLock = redisson.getReadWriteLock("my_read_write_lock").writeLock(); + try { + boolean locked = writeLock.tryLock(10, 10, TimeUnit.SECONDS); + executed.set(locked); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }).start(); + + await().atMost(6, TimeUnit.SECONDS).untilTrue(executed); + } + + + @Test + public void testReadLockLeaseTimeout() throws InterruptedException { + RLock readLock = redisson.getReadWriteLock("my_read_write_lock").readLock(); + Assert.assertTrue(readLock.tryLock(1, 4, TimeUnit.SECONDS)); + + Thread.sleep(3000); + RLock readLock2 = redisson.getReadWriteLock("my_read_write_lock").readLock(); + Assert.assertTrue(readLock2.tryLock(1, 4, TimeUnit.SECONDS)); + readLock2.unlock(); + + Thread.sleep(2000); + + RLock writeLock = redisson.getReadWriteLock("my_read_write_lock").writeLock(); + Assert.assertTrue(writeLock.tryLock()); + } + @Test public void testWriteReadReentrancy() throws InterruptedException { RReadWriteLock readWriteLock = redisson.getReadWriteLock("TEST"); diff --git a/redisson/src/test/java/org/redisson/RedissonRemoteServiceTest.java b/redisson/src/test/java/org/redisson/RedissonRemoteServiceTest.java index e80c18ffa..c9dec60ae 100644 --- a/redisson/src/test/java/org/redisson/RedissonRemoteServiceTest.java +++ b/redisson/src/test/java/org/redisson/RedissonRemoteServiceTest.java @@ -1,7 +1,5 @@ package org.redisson; -import static org.assertj.core.api.Assertions.assertThat; - import java.io.IOException; import java.io.NotSerializableException; import java.io.Serializable; @@ -133,7 +131,7 @@ public class RedissonRemoteServiceTest extends BaseTest { } - public class RemoteImpl implements RemoteInterface { + public static class RemoteImpl implements RemoteInterface { private AtomicInteger iterations; diff --git a/redisson/src/test/java/org/redisson/misc/LFUCacheMapTest.java b/redisson/src/test/java/org/redisson/misc/LFUCacheMapTest.java index f272cc366..bc149256d 100644 --- a/redisson/src/test/java/org/redisson/misc/LFUCacheMapTest.java +++ b/redisson/src/test/java/org/redisson/misc/LFUCacheMapTest.java @@ -5,6 +5,8 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.concurrent.TimeUnit; import org.junit.Test; +import org.redisson.cache.Cache; +import org.redisson.cache.LFUCacheMap; public class LFUCacheMapTest { diff --git a/redisson/src/test/java/org/redisson/misc/LRUCacheMapTest.java b/redisson/src/test/java/org/redisson/misc/LRUCacheMapTest.java index 530827416..c11328862 100644 --- a/redisson/src/test/java/org/redisson/misc/LRUCacheMapTest.java +++ b/redisson/src/test/java/org/redisson/misc/LRUCacheMapTest.java @@ -5,6 +5,8 @@ import static org.assertj.core.api.Assertions.*; import java.util.concurrent.TimeUnit; import org.junit.Test; +import org.redisson.cache.Cache; +import org.redisson.cache.LRUCacheMap; public class LRUCacheMapTest { diff --git a/redisson/src/test/java/org/redisson/misc/NoneCacheMapTest.java b/redisson/src/test/java/org/redisson/misc/NoneCacheMapTest.java index 63f5a58e8..57446e042 100644 --- a/redisson/src/test/java/org/redisson/misc/NoneCacheMapTest.java +++ b/redisson/src/test/java/org/redisson/misc/NoneCacheMapTest.java @@ -1,10 +1,15 @@ package org.redisson.misc; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import java.util.ArrayList; +import java.util.Objects; import java.util.concurrent.TimeUnit; import org.junit.Test; +import org.redisson.cache.Cache; +import org.redisson.cache.NoneCacheMap; public class NoneCacheMapTest { diff --git a/redisson/src/test/java/org/redisson/misc/SoftCacheMapTest.java b/redisson/src/test/java/org/redisson/misc/SoftCacheMapTest.java new file mode 100644 index 000000000..dd074932e --- /dev/null +++ b/redisson/src/test/java/org/redisson/misc/SoftCacheMapTest.java @@ -0,0 +1,68 @@ +package org.redisson.misc; + +import org.junit.Test; +import org.redisson.cache.Cache; +import org.redisson.cache.SoftCacheMap; + +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SoftCacheMapTest { + + @Test + public void testMaxIdleTimeEviction() throws InterruptedException { + Cache map = new SoftCacheMap(0, 0); + map.put(1, 0, 0, TimeUnit.MILLISECONDS, 400, TimeUnit.MILLISECONDS); + assertThat(map.get(1)).isEqualTo(0); + Thread.sleep(200); + assertThat(map.get(1)).isEqualTo(0); + Thread.sleep(200); + assertThat(map.get(1)).isEqualTo(0); + Thread.sleep(200); + assertThat(map.get(1)).isEqualTo(0); + Thread.sleep(410); + assertThat(map.keySet()).isEmpty(); + } + + @Test + public void testTTLEviction() throws InterruptedException { + Cache map = new SoftCacheMap(0, 0); + map.put(1, 0, 500, TimeUnit.MILLISECONDS, 0, TimeUnit.MILLISECONDS); + assertThat(map.get(1)).isEqualTo(0); + Thread.sleep(100); + assertThat(map.get(1)).isEqualTo(0); + assertThat(map.keySet()).containsOnly(1); + Thread.sleep(500); + assertThat(map.keySet()).isEmpty(); + } + + @Test + public void testSizeEviction() { + Cache map = new SoftCacheMap(0, 0); + map.put(1, 0); + map.put(2, 0); + + assertThat(map.keySet()).containsOnly(1, 2); + map.put(3, 0); + map.put(4, 0); + + assertThat(map.keySet()).containsOnly(1, 2, 3, 4); + } + + // This test requires using -XX:SoftRefLRUPolicyMSPerMB=0 to pass + @Test + public void testSoftReferences() { + Cache map = new SoftCacheMap(0, 0); + for(int i=0;i<100000;i++) { + map.put(i, new Integer(i)); + } + + assertThat(map.values().stream().filter(Objects::nonNull).count()).isEqualTo(100000); + System.gc(); + assertThat(map.values().stream().filter(Objects::nonNull).count()).isZero(); + assertThat(map.values().size()).isZero(); + } + +} diff --git a/redisson/src/test/java/org/redisson/spring/support/SpringNamespaceObjectTest.java b/redisson/src/test/java/org/redisson/spring/support/SpringNamespaceObjectTest.java new file mode 100644 index 000000000..59f92dd46 --- /dev/null +++ b/redisson/src/test/java/org/redisson/spring/support/SpringNamespaceObjectTest.java @@ -0,0 +1,282 @@ +package org.redisson.spring.support; + +import java.util.Arrays; +import java.util.Collection; +import org.junit.After; +import org.junit.AfterClass; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; +import org.redisson.BaseTest; +import org.redisson.RedisRunner; +import org.redisson.RedissonFairLock; +import org.redisson.RedissonLiveObjectServiceTest.TestREntity; +import org.redisson.RedissonMultiLock; +import org.redisson.RedissonReadLock; +import org.redisson.RedissonRedLock; +import org.redisson.RedissonRuntimeEnvironment; +import org.redisson.RedissonWriteLock; +import org.redisson.api.LocalCachedMapOptions; +import org.redisson.api.RAtomicDouble; +import org.redisson.api.RAtomicLong; +import org.redisson.api.RBinaryStream; +import org.redisson.api.RBitSet; +import org.redisson.api.RBlockingDeque; +import org.redisson.api.RBlockingFairQueue; +import org.redisson.api.RBlockingQueue; +import org.redisson.api.RBloomFilter; +import org.redisson.api.RBoundedBlockingQueue; +import org.redisson.api.RBucket; +import org.redisson.api.RBuckets; +import org.redisson.api.RCountDownLatch; +import org.redisson.api.RDelayedQueue; +import org.redisson.api.RDeque; +import org.redisson.api.RExecutorService; +import org.redisson.api.RGeo; +import org.redisson.api.RHyperLogLog; +import org.redisson.api.RKeys; +import org.redisson.api.RLexSortedSet; +import org.redisson.api.RList; +import org.redisson.api.RListMultimap; +import org.redisson.api.RLiveObject; +import org.redisson.api.RLiveObjectService; +import org.redisson.api.RLocalCachedMap; +import org.redisson.api.RLock; +import org.redisson.api.RMap; +import org.redisson.api.RMapCache; +import org.redisson.api.RObject; +import org.redisson.api.RPatternTopic; +import org.redisson.api.RPermitExpirableSemaphore; +import org.redisson.api.RPriorityDeque; +import org.redisson.api.RPriorityQueue; +import org.redisson.api.RQueue; +import org.redisson.api.RReadWriteLock; +import org.redisson.api.RRemoteService; +import org.redisson.api.RScoredSortedSet; +import org.redisson.api.RScript; +import org.redisson.api.RSemaphore; +import org.redisson.api.RSet; +import org.redisson.api.RSetCache; +import org.redisson.api.RSetMultimap; +import org.redisson.api.RSetMultimapCache; +import org.redisson.api.RSortedSet; +import org.redisson.api.RTopic; +import org.redisson.api.RemoteInvocationOptions; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +@RunWith(Parameterized.class) +public class SpringNamespaceObjectTest extends BaseTest { + + private static ApplicationContext context; + + @BeforeClass + public static void setupClass() throws Exception { + if (!RedissonRuntimeEnvironment.isTravis) { + startContext(); + } + } + + @AfterClass + public static void shutDownClass() { + if (!RedissonRuntimeEnvironment.isTravis) { + stopContext(); + } + } + + @Before + public void setup() throws Exception { + if (RedissonRuntimeEnvironment.isTravis) { + startContext(); + } + } + + @After + public void shutDown() { + if (RedissonRuntimeEnvironment.isTravis) { + stopContext(); + } + } + + @Override + protected boolean flushBetweenTests() { + return false; + } + + public static void startContext() { + TestREntity entity = new TestREntity("live-object"); + entity.setValue("1"); + defaultRedisson.getLiveObjectService().merge(entity); + entity = new TestREntity("live-object-ext"); + entity.setValue("1"); + defaultRedisson.getLiveObjectService().merge(entity); + + System.setProperty("redisAddress", RedisRunner.getDefaultRedisServerBindAddressAndPort()); + context = new ClassPathXmlApplicationContext("classpath:org/redisson/spring/support/redisson_objects.xml"); + } + + public static void stopContext() { + ((ConfigurableApplicationContext) context).close(); + } + + @Parameters(name = "{index}: key=[{0}], class=[{1}], parent=[{2}]") + public static Collection tests() { + return Arrays.asList(new Object[][]{ + {"binary-stream", RBinaryStream.class, null}, + {"geo", RGeo.class, null}, + {"set-cache", RSetCache.class, null}, + {"map-cache", RMapCache.class, null}, + {"bucket", RBucket.class, null}, + {"buckets", RBuckets.class, null}, + {"hyper-log-log", RHyperLogLog.class, null}, + {"list", RList.class, null}, + {"list-multimap", RListMultimap.class, null}, + {"local-cached-map", RLocalCachedMap.class, null}, + {"local-options", LocalCachedMapOptions.class, null}, + {"map", RMap.class, null}, + {"set-multimap", RSetMultimap.class, null}, + {"set-multimap-cache", RSetMultimapCache.class, null}, + {"semaphore", RSemaphore.class, null}, + {"permit-expirable-semaphore", RPermitExpirableSemaphore.class, null}, + {"lock", RLock.class, null}, + {"fair-lock", RedissonFairLock.class, null}, + {"read-write-lock", RReadWriteLock.class, null}, + {"read-lock", RedissonReadLock.class, "read-write-lock"}, + {"write-lock", RedissonWriteLock.class, "read-write-lock"}, + {"multi-lock", RedissonMultiLock.class, null}, + {"lock-1", RLock.class, null}, + {"fair-lock-1", RedissonFairLock.class, null}, + {"read-lock-1", RedissonReadLock.class, "read-write-lock"}, + {"write-lock-1", RedissonWriteLock.class, "read-write-lock"}, + {"red-lock", RedissonRedLock.class, null}, + {"lock-2", RLock.class, null}, + {"fair-lock-2", RedissonFairLock.class, null}, + {"read-lock-2", RedissonReadLock.class, "read-write-lock"}, + {"write-lock-2", RedissonWriteLock.class, "read-write-lock"}, + {"set", RSet.class, null}, + {"sorted-set", RSortedSet.class, null}, + {"scored-sorted-set", RScoredSortedSet.class, null}, + {"lex-sorted-set", RLexSortedSet.class, null}, + {"topic", RTopic.class, null}, + {"pattern-topic", RPatternTopic.class, null}, + {"blocking-fair-queue", RBlockingFairQueue.class, null}, + {"queue", RQueue.class, null}, + {"delayed-queue", RDelayedQueue.class, "queue"}, + {"priority-queue", RPriorityQueue.class, null}, + {"priority-deque", RPriorityDeque.class, null}, + {"blocking-queue", RBlockingQueue.class, null}, + {"bounded-blocking-queue", RBoundedBlockingQueue.class, null}, + {"deque", RDeque.class, null}, + {"blocking-deque", RBlockingDeque.class, null}, + {"atomic-long", RAtomicLong.class, null}, + {"atomic-double", RAtomicDouble.class, null}, + {"count-down-latch", RCountDownLatch.class, null}, + {"bit-set", RBitSet.class, null}, + {"bloom-filter", RBloomFilter.class, null}, + {"script", RScript.class, null}, + {"executor-service", RExecutorService.class, null}, + {"remote-service", RRemoteService.class, null}, + {"rpc-client", org.redisson.RedissonRemoteServiceTest.RemoteInterface.class, null}, + {"options", RemoteInvocationOptions.class, null}, + {"keys", RKeys.class, null}, + {"live-object-service", RLiveObjectService.class, null}, + {"live-object", RLiveObject.class, null}, + {"binary-stream-ext", RBinaryStream.class, null}, + {"geo-ext", RGeo.class, null}, + {"set-cache-ext", RSetCache.class, null}, + {"map-cache-ext", RMapCache.class, null}, + {"bucket-ext", RBucket.class, null}, + {"buckets-ext", RBuckets.class, null}, + {"hyper-log-log-ext", RHyperLogLog.class, null}, + {"list-ext", RList.class, null}, + {"list-multimap-ext", RListMultimap.class, null}, + {"local-cached-map-ext", RLocalCachedMap.class, null}, + {"local-options-ext", LocalCachedMapOptions.class, null}, + {"map-ext", RMap.class, null}, + {"set-multimap-ext", RSetMultimap.class, null}, + {"set-multimap-cache-ext", RSetMultimapCache.class, null}, + {"semaphore-ext", RSemaphore.class, null}, + {"permit-expirable-semaphore-ext", RPermitExpirableSemaphore.class, null}, + {"lock-ext", RLock.class, null}, + {"fair-lock-ext", RedissonFairLock.class, null}, + {"read-write-lock-ext", RReadWriteLock.class, null}, + {"read-lock-ext", RedissonReadLock.class, "read-write-lock-ext"}, + {"write-lock-ext", RedissonWriteLock.class, "read-write-lock-ext"}, + {"multi-lock-ext", RedissonMultiLock.class, null}, + {"lock-1-ext", RLock.class, null}, + {"fair-lock-1-ext", RedissonFairLock.class, null}, + {"read-lock-1-ext", RedissonReadLock.class, "read-write-lock-ext"}, + {"write-lock-1-ext", RedissonWriteLock.class, "read-write-lock-ext"}, + {"red-lock-ext", RedissonRedLock.class, null}, + {"lock-2-ext", RLock.class, null}, + {"fair-lock-2-ext", RedissonFairLock.class, null}, + {"read-lock-2-ext", RedissonReadLock.class, "read-write-lock-ext"}, + {"write-lock-2-ext", RedissonWriteLock.class, "read-write-lock-ext"}, + {"set-ext", RSet.class, null}, + {"sorted-set-ext", RSortedSet.class, null}, + {"scored-sorted-set-ext", RScoredSortedSet.class, null}, + {"lex-sorted-set-ext", RLexSortedSet.class, null}, + {"topic-ext", RTopic.class, null}, + {"pattern-topic-ext", RPatternTopic.class, null}, + {"blocking-fair-queue-ext", RBlockingFairQueue.class, null}, + {"queue-ext", RQueue.class, null}, + {"delayed-queue-ext", RDelayedQueue.class, "queue-ext"}, + {"priority-queue-ext", RPriorityQueue.class, null}, + {"priority-deque-ext", RPriorityDeque.class, null}, + {"blocking-queue-ext", RBlockingQueue.class, null}, + {"bounded-blocking-queue-ext", RBoundedBlockingQueue.class, null}, + {"deque-ext", RDeque.class, null}, + {"blocking-deque-ext", RBlockingDeque.class, null}, + {"atomic-long-ext", RAtomicLong.class, null}, + {"atomic-double-ext", RAtomicDouble.class, null}, + {"count-down-latch-ext", RCountDownLatch.class, null}, + {"bit-set-ext", RBitSet.class, null}, + {"bloom-filter-ext", RBloomFilter.class, null}, + {"script-ext", RScript.class, null}, + {"executor-service-ext", RExecutorService.class, null}, + {"remote-service-ext", RRemoteService.class, null}, + {"rpc-client-ext", org.redisson.RedissonRemoteServiceTest.RemoteInterface.class, null}, + {"options-ext", RemoteInvocationOptions.class, null}, + {"keys-ext", RKeys.class, null}, + {"live-object-service-ext", RLiveObjectService.class, null}, + {"live-object-ext", RLiveObject.class, null}, + }); + } + + @Parameter + public String key; + + @Parameter(1) + public Class cls; + + @Parameter(2) + public String parentKey; + + @Test + public void testRObjects() { + Object bean = context.getBean(key); + assertTrue(cls.isInstance(bean)); + if (RObject.class.isAssignableFrom(cls)) { + assertEquals(parentKey == null ? key : parentKey, RObject.class.cast(bean).getName()); + } + if (RTopic.class.isAssignableFrom(cls)) { + assertEquals(key, RTopic.class.cast(bean).getChannelNames().get(0)); + } + if (RPatternTopic.class.isAssignableFrom(cls)) { + assertEquals(key, RPatternTopic.class.cast(bean).getPatternNames().get(0)); + } + if (RLiveObject.class.isAssignableFrom(cls)) { + assertEquals(key, RLiveObject.class.cast(bean).getLiveObjectId()); + } + } +} diff --git a/redisson/src/test/java/org/redisson/spring/support/SpringNamespaceTest.java b/redisson/src/test/java/org/redisson/spring/support/SpringNamespaceTest.java new file mode 100644 index 000000000..a2e36842c --- /dev/null +++ b/redisson/src/test/java/org/redisson/spring/support/SpringNamespaceTest.java @@ -0,0 +1,368 @@ +package org.redisson.spring.support; + +import java.util.List; +import org.junit.After; +import org.junit.AfterClass; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.redisson.BaseTest; +import org.redisson.ClusterRunner; +import org.redisson.RedisRunner; +import org.redisson.Redisson; +import org.redisson.RedissonRuntimeEnvironment; +import org.redisson.api.RedissonClient; +import org.redisson.client.RedisClient; +import org.redisson.client.RedisConnection; +import org.redisson.codec.MsgPackJacksonCodec; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class SpringNamespaceTest extends BaseTest { + + private static ApplicationContext context; + + @BeforeClass + public static void setupClass() throws Exception { + if (!RedissonRuntimeEnvironment.isTravis) { + startContext(); + } + } + + @AfterClass + public static void shutDownClass() throws Exception { + if (!RedissonRuntimeEnvironment.isTravis) { + stopContext(); + } + } + + @Before + public void setup() throws Exception { + if (RedissonRuntimeEnvironment.isTravis) { + startContext(); + } + } + + @After + public void shutDown() throws Exception { + if (RedissonRuntimeEnvironment.isTravis) { + stopContext(); + } + } + + public static void startContext() throws Exception { + System.setProperty("redisAddress", RedisRunner.getDefaultRedisServerBindAddressAndPort()); + + //Needs a instance running on the default port, launch it if there isn't one already + if (RedisRunner.isFreePort(6379)) { + new RedisRunner() + .nosave() + .randomDir() + .run(); + } + + RedisRunner.RedisProcess slave1 = new RedisRunner() + .nosave() + .randomDir() + .randomPort() + .slaveof( + RedisRunner.getDefaultRedisServerInstance().getRedisServerBindAddress(), + RedisRunner.getDefaultRedisServerInstance().getRedisServerPort()) + .run(); + System.setProperty("slave1Address", slave1.getRedisServerAddressAndPort()); + + RedisRunner.RedisProcess slave2 = new RedisRunner() + .nosave() + .randomDir() + .randomPort() + .slaveof( + RedisRunner.getDefaultRedisServerInstance().getRedisServerBindAddress(), + RedisRunner.getDefaultRedisServerInstance().getRedisServerPort()) + .run(); + System.setProperty("slave2Address", slave2.getRedisServerAddressAndPort()); + + RedisRunner.RedisProcess sentinel1 = new RedisRunner() + .nosave() + .randomDir() + .randomPort() + .sentinel() + .sentinelMonitor( + "myMaster", + RedisRunner.getDefaultRedisServerInstance().getRedisServerBindAddress(), + RedisRunner.getDefaultRedisServerInstance().getRedisServerPort(), + 2).run(); + System.setProperty("sentinel1Address", sentinel1.getRedisServerAddressAndPort()); + + RedisRunner.RedisProcess sentinel2 = new RedisRunner() + .nosave() + .randomDir() + .randomPort() + .sentinel() + .sentinelMonitor( + "myMaster", + RedisRunner.getDefaultRedisServerInstance().getRedisServerBindAddress(), + RedisRunner.getDefaultRedisServerInstance().getRedisServerPort(), + 2).run(); + System.setProperty("sentinel2Address", sentinel2.getRedisServerAddressAndPort()); + + RedisRunner.RedisProcess sentinel3 = new RedisRunner() + .nosave() + .randomDir() + .randomPort() + .sentinel() + .sentinelMonitor( + "myMaster", + RedisRunner.getDefaultRedisServerInstance().getRedisServerBindAddress(), + RedisRunner.getDefaultRedisServerInstance().getRedisServerPort(), + 2).run(); + System.setProperty("sentinel3Address", sentinel3.getRedisServerAddressAndPort()); + + ClusterRunner clusterRunner = new ClusterRunner() + .addNode(new RedisRunner().randomPort().randomDir().nosave()) + .addNode(new RedisRunner().randomPort().randomDir().nosave()) + .addNode(new RedisRunner().randomPort().randomDir().nosave()); + List nodes = clusterRunner.run(); + nodes.stream().forEach((node) -> { + System.setProperty("node" + (nodes.indexOf(node) + 1) + "Address", node.getRedisServerAddressAndPort()); + }); + + context = new ClassPathXmlApplicationContext("classpath:org/redisson/spring/support/namespace.xml"); + } + + public static void stopContext() throws Exception { + ((ConfigurableApplicationContext) context).close(); + } + + public static class AutowireRedisson { + + @Autowired + @Qualifier("redisson1") + private Redisson redisson1; + + @Autowired + @Qualifier("redisson2") + private RedissonClient redisson2; + + @Autowired + @Qualifier("redisson3") + private Redisson redisson3; + + @Autowired + @Qualifier("redisson4") + private RedissonClient redisson4; + + @Autowired + @Qualifier("myRedisson1") + private Redisson redisson5; + + @Autowired + @Qualifier("myRedisson2") + private Redisson redisson6; + + @Autowired + @Qualifier("qualifier1") + private RedissonClient redisson7; + + @Autowired + @Qualifier("qualifier2") + private RedissonClient redisson8; + + /** + * @return the redisson1 + */ + public Redisson getRedisson1() { + return redisson1; + } + + /** + * @param redisson1 the redisson1 to set + */ + public void setRedisson1(Redisson redisson1) { + this.redisson1 = redisson1; + } + + /** + * @return the redisson2 + */ + public RedissonClient getRedisson2() { + return redisson2; + } + + /** + * @param redisson2 the redisson2 to set + */ + public void setRedisson2(RedissonClient redisson2) { + this.redisson2 = redisson2; + } + + /** + * @return the redisson3 + */ + public Redisson getRedisson3() { + return redisson3; + } + + /** + * @param redisson3 the redisson3 to set + */ + public void setRedisson3(Redisson redisson3) { + this.redisson3 = redisson3; + } + + /** + * @return the redisson4 + */ + public RedissonClient getRedisson4() { + return redisson4; + } + + /** + * @param redisson4 the redisson4 to set + */ + public void setRedisson4(RedissonClient redisson4) { + this.redisson4 = redisson4; + } + + /** + * @return the redisson5 + */ + public Redisson getRedisson5() { + return redisson5; + } + + /** + * @param redisson5 the redisson5 to set + */ + public void setRedisson5(Redisson redisson5) { + this.redisson5 = redisson5; + } + + /** + * @return the redisson6 + */ + public Redisson getRedisson6() { + return redisson6; + } + + /** + * @param redisson6 the redisson6 to set + */ + public void setRedisson6(Redisson redisson6) { + this.redisson6 = redisson6; + } + + /** + * @return the redisson7 + */ + public RedissonClient getRedisson7() { + return redisson7; + } + + /** + * @param redisson7 the redisson7 to set + */ + public void setRedisson7(RedissonClient redisson7) { + this.redisson7 = redisson7; + } + + /** + * @return the redisson8 + */ + public RedissonClient getRedisson8() { + return redisson8; + } + + /** + * @param redisson8 the redisson8 to set + */ + public void setRedisson8(RedissonClient redisson8) { + this.redisson8 = redisson8; + } + + } + + @Test + public void testNamespace() { + Object bean = context.getBean("myRedisson1"); + assertTrue(bean instanceof Redisson); + } + + @Test + public void testAlias() { + Object origin = context.getBean("myRedisson1"); + assertTrue(origin instanceof Redisson); + Object bean = context.getBean("redisson1"); + assertTrue(bean instanceof Redisson); + assertEquals(origin, bean); + bean = context.getBean("redisson2"); + assertTrue(bean instanceof Redisson); + assertEquals(origin, bean); + } + + @Test + public void testAutowire() { + AutowireRedisson bean = context.getAutowireCapableBeanFactory().getBean(AutowireRedisson.class); + assertNotNull(bean.getRedisson1()); + assertNotNull(bean.getRedisson2()); + assertNotNull(bean.getRedisson3()); + assertNotNull(bean.getRedisson4()); + assertNotNull(bean.getRedisson5()); + assertNotNull(bean.getRedisson6()); + assertNotNull(bean.getRedisson7()); + assertNotNull(bean.getRedisson8()); + assertEquals(bean.getRedisson1(), bean.getRedisson2()); + assertEquals(bean.getRedisson1(), bean.getRedisson5()); + assertNotEquals(bean.getRedisson1(), bean.getRedisson7()); + assertNotEquals(bean.getRedisson1(), bean.getRedisson8()); + assertEquals(bean.getRedisson3(), bean.getRedisson4()); + assertEquals(bean.getRedisson3(), bean.getRedisson6()); + assertNotEquals(bean.getRedisson3(), bean.getRedisson7()); + assertNotEquals(bean.getRedisson3(), bean.getRedisson8()); + assertNotEquals(bean.getRedisson7(), bean.getRedisson8()); + } + + @Test + public void testBeanRef() { + AutowireRedisson bean = context.getAutowireCapableBeanFactory().getBean(AutowireRedisson.class); + assertTrue(bean.getRedisson1().getConfig().getCodec() instanceof MsgPackJacksonCodec); + assertFalse(bean.getRedisson3().getConfig().getCodec() instanceof MsgPackJacksonCodec); + assertFalse(bean.getRedisson7().getConfig().getCodec() instanceof MsgPackJacksonCodec); + assertFalse(bean.getRedisson8().getConfig().getCodec() instanceof MsgPackJacksonCodec); + } + + public static class AutowireRedis { + + @Autowired + private RedisClient redisClient; + + /** + * @return the redisClient + */ + public RedisClient getRedisClient() { + return redisClient; + } + + /** + * @param redisClient the redisClient to set + */ + public void setRedisClient(RedisClient redisClient) { + this.redisClient = redisClient; + } + } + + @Test + public void testAutowireRedis() { + AutowireRedis bean = context.getAutowireCapableBeanFactory().getBean(AutowireRedis.class); + RedisConnection connection = bean.getRedisClient().connect(); + assertTrue(connection.isActive()); + connection.closeAsync().awaitUninterruptibly(); + } +} diff --git a/redisson/src/test/java/org/redisson/spring/support/SpringNamespaceWikiTest.java b/redisson/src/test/java/org/redisson/spring/support/SpringNamespaceWikiTest.java new file mode 100644 index 000000000..df1c38bf3 --- /dev/null +++ b/redisson/src/test/java/org/redisson/spring/support/SpringNamespaceWikiTest.java @@ -0,0 +1,195 @@ +package org.redisson.spring.support; + +import java.io.IOException; +import java.util.List; +import org.junit.Test; +import org.redisson.ClusterRunner; +import org.redisson.RedisRunner; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class SpringNamespaceWikiTest { + + @Test + public void testSingle() throws Exception { + RedisRunner.RedisProcess run = new RedisRunner() + .requirepass("do_not_use_if_it_is_not_set") + .nosave() + .randomDir() + .run(); + try { + ((ConfigurableApplicationContext) + new ClassPathXmlApplicationContext("classpath:org/redisson/spring/support/namespace_wiki_single.xml")) + .close(); + } finally { + run.stop(); + } + } + + @Test + public void testMasterSlave() throws Exception { + RedisRunner.RedisProcess master = new RedisRunner() + .requirepass("do_not_use_if_it_is_not_set") + .nosave() + .randomDir() + .run(); + RedisRunner.RedisProcess slave1 = new RedisRunner() + .requirepass("do_not_use_if_it_is_not_set") + .masterauth("do_not_use_if_it_is_not_set") + .port(6380) + .nosave() + .randomDir() + .slaveof("127.0.0.1", 6379) + .run(); + RedisRunner.RedisProcess slave2 = new RedisRunner() + .requirepass("do_not_use_if_it_is_not_set") + .masterauth("do_not_use_if_it_is_not_set") + .port(6381) + .nosave() + .randomDir() + .slaveof("127.0.0.1", 6379) + .run(); + try { + ((ConfigurableApplicationContext) + new ClassPathXmlApplicationContext("classpath:org/redisson/spring/support/namespace_wiki_master_slave.xml")) + .close(); + } finally { + master.stop(); + slave1.stop(); + slave2.stop(); + } + } + + @Test + public void testSentinel() throws Exception { + RedisRunner.RedisProcess master = new RedisRunner() + .requirepass("do_not_use_if_it_is_not_set") + .nosave() + .randomDir() + .run(); + RedisRunner.RedisProcess slave1 = new RedisRunner() + .requirepass("do_not_use_if_it_is_not_set") + .masterauth("do_not_use_if_it_is_not_set") + .port(6380) + .nosave() + .randomDir() + .slaveof("127.0.0.1", 6379) + .run(); + RedisRunner.RedisProcess slave2 = new RedisRunner() + .requirepass("do_not_use_if_it_is_not_set") + .masterauth("do_not_use_if_it_is_not_set") + .port(6381) + .nosave() + .randomDir() + .slaveof("127.0.0.1", 6379) + .run(); + RedisRunner.RedisProcess sentinel1 = new RedisRunner() +// .requirepass("do_not_use_if_it_is_not_set") + .nosave() + .randomDir() + .port(26379) + .sentinel() + .sentinelMonitor("myMaster", "127.0.0.1", 6379, 2) + .sentinelAuthPass("myMaster", "do_not_use_if_it_is_not_set") + .run(); + RedisRunner.RedisProcess sentinel2 = new RedisRunner() +// .requirepass("do_not_use_if_it_is_not_set") + .nosave() + .randomDir() + .port(26380) + .sentinel() + .sentinelMonitor("myMaster", "127.0.0.1", 6379, 2) + .sentinelAuthPass("myMaster", "do_not_use_if_it_is_not_set") + .run(); + RedisRunner.RedisProcess sentinel3 = new RedisRunner() +// .requirepass("do_not_use_if_it_is_not_set") + .nosave() + .randomDir() + .port(26381) + .sentinel() + .sentinelMonitor("myMaster", "127.0.0.1", 6379, 2) + .sentinelAuthPass("myMaster", "do_not_use_if_it_is_not_set") + .run(); + try { + ((ConfigurableApplicationContext) + new ClassPathXmlApplicationContext("classpath:org/redisson/spring/support/namespace_wiki_sentinel.xml")) + .close(); + } finally { + master.stop(); + slave1.stop(); + slave2.stop(); + sentinel1.stop(); + sentinel2.stop(); + sentinel3.stop(); + } + } + + @Test + public void testReplicated() throws Exception { + RedisRunner.RedisProcess master = new RedisRunner() + .requirepass("do_not_use_if_it_is_not_set") + .nosave() + .randomDir() + .run(); + RedisRunner.RedisProcess slave1 = new RedisRunner() + .requirepass("do_not_use_if_it_is_not_set") + .masterauth("do_not_use_if_it_is_not_set") + .port(6380) + .nosave() + .randomDir() + .slaveof("127.0.0.1", 6379) + .run(); + RedisRunner.RedisProcess slave2 = new RedisRunner() + .requirepass("do_not_use_if_it_is_not_set") + .masterauth("do_not_use_if_it_is_not_set") + .port(6381) + .nosave() + .randomDir() + .slaveof("127.0.0.1", 6379) + .run(); + try { + ((ConfigurableApplicationContext) + new ClassPathXmlApplicationContext("classpath:org/redisson/spring/support/namespace_wiki_replicated.xml")) + .close(); + } finally { + master.stop(); + slave1.stop(); + slave2.stop(); + } + } + + @Test + public void testCluster() throws Exception { + ClusterRunner clusterRunner = new ClusterRunner() + .addNode(new RedisRunner() + .requirepass("do_not_use_if_it_is_not_set") + .port(6379) + .randomDir() + .nosave()) + .addNode(new RedisRunner() + .requirepass("do_not_use_if_it_is_not_set") + .port(6380) + .randomDir() + .nosave()) + .addNode(new RedisRunner() + .requirepass("do_not_use_if_it_is_not_set") + .port(6381) + .randomDir() + .nosave()); + List nodes = clusterRunner.run(); + + try { + ((ConfigurableApplicationContext) + new ClassPathXmlApplicationContext("classpath:org/redisson/spring/support/namespace_wiki_cluster.xml")) + .close(); + } finally { + for (RedisRunner.RedisProcess node : nodes) { + node.stop(); + } + } + } +} diff --git a/redisson/src/test/resources/org/redisson/spring/support/namespace.xml b/redisson/src/test/resources/org/redisson/spring/support/namespace.xml new file mode 100644 index 000000000..928ee1847 --- /dev/null +++ b/redisson/src/test/resources/org/redisson/spring/support/namespace.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/redisson/src/test/resources/org/redisson/spring/support/namespace_wiki_cluster.xml b/redisson/src/test/resources/org/redisson/spring/support/namespace_wiki_cluster.xml new file mode 100644 index 000000000..1689951e5 --- /dev/null +++ b/redisson/src/test/resources/org/redisson/spring/support/namespace_wiki_cluster.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/redisson/src/test/resources/org/redisson/spring/support/namespace_wiki_master_slave.xml b/redisson/src/test/resources/org/redisson/spring/support/namespace_wiki_master_slave.xml new file mode 100644 index 000000000..12003cbe3 --- /dev/null +++ b/redisson/src/test/resources/org/redisson/spring/support/namespace_wiki_master_slave.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/redisson/src/test/resources/org/redisson/spring/support/namespace_wiki_replicated.xml b/redisson/src/test/resources/org/redisson/spring/support/namespace_wiki_replicated.xml new file mode 100644 index 000000000..450ea9d98 --- /dev/null +++ b/redisson/src/test/resources/org/redisson/spring/support/namespace_wiki_replicated.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/redisson/src/test/resources/org/redisson/spring/support/namespace_wiki_sentinel.xml b/redisson/src/test/resources/org/redisson/spring/support/namespace_wiki_sentinel.xml new file mode 100644 index 000000000..49d188775 --- /dev/null +++ b/redisson/src/test/resources/org/redisson/spring/support/namespace_wiki_sentinel.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/redisson/src/test/resources/org/redisson/spring/support/namespace_wiki_single.xml b/redisson/src/test/resources/org/redisson/spring/support/namespace_wiki_single.xml new file mode 100644 index 000000000..4c49a82fe --- /dev/null +++ b/redisson/src/test/resources/org/redisson/spring/support/namespace_wiki_single.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + diff --git a/redisson/src/test/resources/org/redisson/spring/support/redisson_objects.xml b/redisson/src/test/resources/org/redisson/spring/support/redisson_objects.xml new file mode 100644 index 000000000..3e3b64504 --- /dev/null +++ b/redisson/src/test/resources/org/redisson/spring/support/redisson_objects.xml @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +