Merge branch 'master' into gradle-build-ng
Conflicts: pom.xml spring-data-redis/pom.xml spring-data-redis/template.mf
This commit is contained in:
@@ -4,6 +4,10 @@ Spring Data - Key Value
|
||||
The primary goal of the [Spring Data](http://www.springsource.org/spring-data) project is to make it easier to build Spring-powered applications that use new data access technologies such as non-relational databases, map-reduce frameworks, and cloud based data services.
|
||||
As the name implies, the **Key Value** modules provides integration with key value stores such as [Redis](http://code.google.com/p/redis/) and [Riak](http://www.basho.com/Riak.html).
|
||||
|
||||
Examples
|
||||
--------
|
||||
For examples on using the Spring Data Key Value, see the dedicated project, also available on [GitHub](https://github.com/SpringSource/spring-data-keyvalue-examples)
|
||||
|
||||
Getting Help
|
||||
------------
|
||||
|
||||
|
||||
@@ -2,6 +2,32 @@ SPRING DATA KEY/VALUE INTEGRATION CHANGELOG
|
||||
===========================================
|
||||
http://www.springsource.org/spring-data
|
||||
|
||||
Changes in version 1.0.0.M3 (2011-04-06)
|
||||
----------------------------------------
|
||||
|
||||
Redis
|
||||
-----
|
||||
|
||||
General
|
||||
* Added support for RJC (new Redis client)
|
||||
* Added dedicated SORT and SORT/GET support
|
||||
* Introduced HashMapper feature for mapping objects to and from maps
|
||||
* Improved exception hierarchy to be more consistent with Spring DAO
|
||||
* Made several Redis dependencies optional to eliminate unnecessary jars from the classpath
|
||||
|
||||
Package o.s.d.k.redis.connection
|
||||
* Added support for indexes to RedisConnectionFactories
|
||||
* Added new key operations to KeyOperations (formerly KeyBound)
|
||||
* Improved handling of Jedis exceptions
|
||||
|
||||
Package o.s.d.k.redis.core
|
||||
* Serializers are exposed to RedisCallback
|
||||
* Added missing operations (move, select) to RedisTemplate
|
||||
* Fixed the signature of various method
|
||||
|
||||
Package o.s.d.k.redis.support.atomic
|
||||
* Fixed incorrect serialization leading to error for RedisAtomicInteger & RedisAtomicLong
|
||||
|
||||
|
||||
Changes in version 1.0.0.M2 (2011-02-10)
|
||||
----------------------------------------
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
<section id="redis:requirements">
|
||||
<title>Redis Requirements</title>
|
||||
<para>SDKV requires Redis 2.0 or above (Redis 2.2 is recommended) and Java SE 6.0 or above.
|
||||
In terms of language bindings (or connectors), SDKV integrates with <ulink url="http://github.com/xetorthio/jedis">Jedis</ulink> and
|
||||
<ulink url="http://github.com/alphazero/jredis">JRedis</ulink>, two popular open source Java libraries for Redis. If you are aware of
|
||||
any other connector that we should be integrating is, please send us feedback.
|
||||
In terms of language bindings (or connectors), SDKV integrates with <ulink url="http://github.com/xetorthio/jedis">Jedis</ulink>,
|
||||
<ulink url="http://github.com/alphazero/jredis">JRedis</ulink> and <ulink url="https://github.com/e-mzungu/rjc">RJC</ulink>, three popular open source Java libraries for Redis.
|
||||
If you are aware of any other connector that we should be integrating is, please send us feedback.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
</xsl:for-each>
|
||||
</fo:block>
|
||||
<fo:block font-family="Helvetica" font-size="12pt" padding="10mm">
|
||||
<xsl:text>Copyright © 2006-2009</xsl:text>
|
||||
<xsl:text>Copyright © 2010-2011</xsl:text>
|
||||
</fo:block>
|
||||
|
||||
<fo:block font-family="Helvetica" font-size="10pt" padding="1mm">
|
||||
@@ -106,7 +106,7 @@
|
||||
<xsl:param name="gentext-key" select="''"/>
|
||||
<xsl:variable name="Version">
|
||||
<xsl:if test="//releaseinfo">
|
||||
<xsl:text>Spring Data Redis (</xsl:text><xsl:value-of select="//releaseinfo" /><xsl:text>)</xsl:text>
|
||||
<xsl:text>Spring Data Key Value (</xsl:text><xsl:value-of select="//releaseinfo" /><xsl:text>)</xsl:text>
|
||||
</xsl:if>
|
||||
</xsl:variable>
|
||||
<xsl:choose>
|
||||
|
||||
@@ -23,9 +23,9 @@ import org.springframework.data.keyvalue.UncategorizedKeyvalueStoreException;
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class UncategorizedRedisException extends UncategorizedKeyvalueStoreException {
|
||||
public class RedisSystemException extends UncategorizedKeyvalueStoreException {
|
||||
|
||||
public UncategorizedRedisException(String msg, Throwable cause) {
|
||||
public RedisSystemException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.config;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
|
||||
import org.springframework.data.keyvalue.redis.support.collections.RedisCollectionFactoryBean;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Parser for the Redis <code><collection></code> element.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class RedisCollectionParser extends AbstractSimpleBeanDefinitionParser {
|
||||
|
||||
@Override
|
||||
protected Class<?> getBeanClass(Element element) {
|
||||
return RedisCollectionFactoryBean.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postProcess(BeanDefinitionBuilder beanDefinition, Element element) {
|
||||
String template = element.getAttribute("template");
|
||||
if (StringUtils.hasText(template)) {
|
||||
beanDefinition.addPropertyReference("template", template);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEligibleAttribute(String attributeName) {
|
||||
return super.isEligibleAttribute(attributeName) && (!"template".equals(attributeName));
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NamedNodeMap;
|
||||
|
||||
/**
|
||||
* Parser for the JMS <code><listener-container></code> element.
|
||||
* Parser for the Redis <code><listener-container></code> element.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
|
||||
@@ -28,5 +28,6 @@ class RedisNamespaceHandler extends NamespaceHandlerSupport {
|
||||
@Override
|
||||
public void init() {
|
||||
registerBeanDefinitionParser("listener-container", new RedisListenerContainerParser());
|
||||
registerBeanDefinitionParser("collection", new RedisCollectionParser());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Default implementation for {@link SortParameters}.
|
||||
*
|
||||
@@ -126,32 +125,42 @@ public class DefaultSortParameters implements SortParameters {
|
||||
// builder like methods
|
||||
//
|
||||
|
||||
public SortParameters order(Order order) {
|
||||
public DefaultSortParameters order(Order order) {
|
||||
setOrder(order);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SortParameters alpha() {
|
||||
public DefaultSortParameters alpha() {
|
||||
setAlphabetic(true);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SortParameters numeric() {
|
||||
public DefaultSortParameters asc() {
|
||||
setOrder(Order.ASC);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DefaultSortParameters desc() {
|
||||
setOrder(Order.DESC);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DefaultSortParameters numeric() {
|
||||
setAlphabetic(false);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SortParameters get(byte[] pattern) {
|
||||
public DefaultSortParameters get(byte[] pattern) {
|
||||
addGetPattern(pattern);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SortParameters by(byte[] pattern) {
|
||||
public DefaultSortParameters by(byte[] pattern) {
|
||||
setByPattern(pattern);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SortParameters limit(long start, long count) {
|
||||
public DefaultSortParameters limit(long start, long count) {
|
||||
setLimit(new Range(start, count));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.keyvalue.redis.connection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
@@ -24,8 +23,9 @@ import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.UncategorizedRedisException;
|
||||
import org.springframework.data.keyvalue.redis.RedisSystemException;
|
||||
import org.springframework.data.keyvalue.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.keyvalue.redis.serializer.SerializationUtils;
|
||||
import org.springframework.data.keyvalue.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -88,7 +88,7 @@ public class DefaultStringRedisConnection implements StringRedisConnection {
|
||||
return delegate.bRPopLPush(timeout, srcKey, dstKey);
|
||||
}
|
||||
|
||||
public void close() throws UncategorizedRedisException {
|
||||
public void close() throws RedisSystemException {
|
||||
delegate.close();
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ public class DefaultStringRedisConnection implements StringRedisConnection {
|
||||
return delegate.getNativeConnection();
|
||||
}
|
||||
|
||||
public byte[] getRange(byte[] key, int start, int end) {
|
||||
public byte[] getRange(byte[] key, long start, long end) {
|
||||
return delegate.getRange(key, start, end);
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ public class DefaultStringRedisConnection implements StringRedisConnection {
|
||||
return delegate.isSubscribed();
|
||||
}
|
||||
|
||||
public Collection<byte[]> keys(byte[] pattern) {
|
||||
public Set<byte[]> keys(byte[] pattern) {
|
||||
return delegate.keys(pattern);
|
||||
}
|
||||
|
||||
@@ -308,6 +308,10 @@ public class DefaultStringRedisConnection implements StringRedisConnection {
|
||||
return delegate.persist(key);
|
||||
}
|
||||
|
||||
public Boolean move(byte[] key, int dbIndex) {
|
||||
return delegate.move(key, dbIndex);
|
||||
}
|
||||
|
||||
public String ping() {
|
||||
return delegate.ping();
|
||||
}
|
||||
@@ -396,8 +400,8 @@ public class DefaultStringRedisConnection implements StringRedisConnection {
|
||||
return delegate.setNX(key, value);
|
||||
}
|
||||
|
||||
public void setRange(byte[] key, int start, int end) {
|
||||
delegate.setRange(key, start, end);
|
||||
public void setRange(byte[] key, byte[] value, long start) {
|
||||
delegate.setRange(key, value, start);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
@@ -576,7 +580,7 @@ public class DefaultStringRedisConnection implements StringRedisConnection {
|
||||
byte[][] ret = new byte[keys.length][];
|
||||
|
||||
for (int i = 0; i < ret.length; i++) {
|
||||
byte[] bs = serializer.serialize(keys[i]);
|
||||
ret[i] = serializer.serialize(keys[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -593,20 +597,12 @@ public class DefaultStringRedisConnection implements StringRedisConnection {
|
||||
}
|
||||
|
||||
|
||||
private List<String> deserialize(Collection<byte[]> data) {
|
||||
List<String> result = new ArrayList<String>(data.size());
|
||||
for (byte[] raw : data) {
|
||||
result.add(serializer.deserialize(raw));
|
||||
}
|
||||
return result;
|
||||
private List<String> deserialize(List<byte[]> data) {
|
||||
return SerializationUtils.deserialize(data, serializer);
|
||||
}
|
||||
|
||||
private Set<String> deserialize(Set<byte[]> data) {
|
||||
Set<String> result = new LinkedHashSet<String>(data.size());
|
||||
for (byte[] raw : data) {
|
||||
result.add(serializer.deserialize(raw));
|
||||
}
|
||||
return result;
|
||||
return SerializationUtils.deserialize(data, serializer);
|
||||
}
|
||||
|
||||
private String deserialize(byte[] data) {
|
||||
@@ -614,6 +610,9 @@ public class DefaultStringRedisConnection implements StringRedisConnection {
|
||||
}
|
||||
|
||||
private Set<StringTuple> deserializeTuple(Set<Tuple> data) {
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
Set<StringTuple> result = new LinkedHashSet<StringTuple>(data.size());
|
||||
for (Tuple raw : data) {
|
||||
result.add(new DefaultStringTuple(raw, serializer.deserialize(raw.getValue())));
|
||||
@@ -688,7 +687,7 @@ public class DefaultStringRedisConnection implements StringRedisConnection {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRange(String key, int start, int end) {
|
||||
public String getRange(String key, long start, long end) {
|
||||
return deserialize(delegate.getRange(serialize(key), start, end));
|
||||
}
|
||||
|
||||
@@ -843,6 +842,11 @@ public class DefaultStringRedisConnection implements StringRedisConnection {
|
||||
return delegate.persist(serialize(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean move(String key, int dbIndex) {
|
||||
return delegate.move(serialize(key), dbIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pSubscribe(MessageListener listener, String... patterns) {
|
||||
delegate.pSubscribe(listener, serializeMulti(patterns));
|
||||
@@ -924,8 +928,8 @@ public class DefaultStringRedisConnection implements StringRedisConnection {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRange(String key, int start, int end) {
|
||||
delegate.setRange(serialize(key), start, end);
|
||||
public void setRange(String key, String value, long start) {
|
||||
delegate.setRange(serialize(key), serialize(value), start);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,47 +16,13 @@
|
||||
|
||||
package org.springframework.data.keyvalue.redis.connection;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Interface for the commands supported by Redis.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface RedisCommands extends RedisTxCommands, RedisStringCommands, RedisListCommands, RedisSetCommands,
|
||||
RedisZSetCommands, RedisHashCommands, RedisServerCommands, RedisPubSubCommands {
|
||||
|
||||
Boolean exists(byte[] key);
|
||||
|
||||
Long del(byte[]... keys);
|
||||
|
||||
DataType type(byte[] key);
|
||||
|
||||
Collection<byte[]> keys(byte[] pattern);
|
||||
|
||||
byte[] randomKey();
|
||||
|
||||
void rename(byte[] oldName, byte[] newName);
|
||||
|
||||
Boolean renameNX(byte[] oldName, byte[] newName);
|
||||
|
||||
Boolean expire(byte[] key, long seconds);
|
||||
|
||||
Boolean expireAt(byte[] key, long unixTime);
|
||||
|
||||
Boolean persist(byte[] key);
|
||||
|
||||
Long ttl(byte[] key);
|
||||
|
||||
void select(int dbIndex);
|
||||
|
||||
byte[] echo(byte[] message);
|
||||
|
||||
String ping();
|
||||
|
||||
// sort commands
|
||||
List<byte[]> sort(byte[] key, SortParameters params);
|
||||
|
||||
Long sort(byte[] key, SortParameters params, byte[] storeKey);
|
||||
public interface RedisCommands extends RedisKeyCommands, RedisStringCommands, RedisListCommands, RedisSetCommands,
|
||||
RedisZSetCommands, RedisHashCommands, RedisTxCommands, RedisPubSubCommands, RedisConnectionCommands,
|
||||
RedisServerCommands {
|
||||
}
|
||||
@@ -13,17 +13,20 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.keyvalue.redis.serializer;
|
||||
package org.springframework.data.keyvalue.redis.connection;
|
||||
|
||||
|
||||
/**
|
||||
* Minimal class used for sharing pieces of code between the serializers
|
||||
* Connection-specific commands supported by Redis.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
abstract class SerializerUtils {
|
||||
static final byte[] EMPTY_ARRAY = new byte[0];
|
||||
public interface RedisConnectionCommands {
|
||||
|
||||
static boolean isEmpty(byte[] data) {
|
||||
return (data == null || data.length == 0);
|
||||
}
|
||||
}
|
||||
public abstract void select(int dbIndex);
|
||||
|
||||
public abstract byte[] echo(byte[] message);
|
||||
|
||||
public abstract String ping();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.connection;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
|
||||
/**
|
||||
* Exception thrown when subscribing to an expired/dead {@link Subscription}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class RedisInvalidSubscriptionException extends InvalidDataAccessResourceUsageException {
|
||||
|
||||
/**
|
||||
* Constructs a new <code>RedisInvalidSubscriptionException</code> instance.
|
||||
*
|
||||
* @param msg
|
||||
* @param cause
|
||||
*/
|
||||
public RedisInvalidSubscriptionException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>RedisInvalidSubscriptionException</code> instance.
|
||||
*
|
||||
* @param msg
|
||||
*/
|
||||
public RedisInvalidSubscriptionException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.connection;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* Key-specific commands supported by Redis.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface RedisKeyCommands {
|
||||
|
||||
public abstract Boolean exists(byte[] key);
|
||||
|
||||
public abstract Long del(byte[]... keys);
|
||||
|
||||
public abstract DataType type(byte[] key);
|
||||
|
||||
public abstract Set<byte[]> keys(byte[] pattern);
|
||||
|
||||
public abstract byte[] randomKey();
|
||||
|
||||
public abstract void rename(byte[] oldName, byte[] newName);
|
||||
|
||||
public abstract Boolean renameNX(byte[] oldName, byte[] newName);
|
||||
|
||||
public abstract Boolean expire(byte[] key, long seconds);
|
||||
|
||||
public abstract Boolean expireAt(byte[] key, long unixTime);
|
||||
|
||||
public abstract Boolean persist(byte[] key);
|
||||
|
||||
public abstract Boolean move(byte[] key, int dbIndex);
|
||||
|
||||
public abstract Long ttl(byte[] key);
|
||||
|
||||
// sort commands
|
||||
public abstract List<byte[]> sort(byte[] key, SortParameters params);
|
||||
|
||||
public abstract Long sort(byte[] key, SortParameters params, byte[] storeKey);
|
||||
|
||||
}
|
||||
@@ -52,9 +52,9 @@ public interface RedisStringCommands {
|
||||
|
||||
Long append(byte[] key, byte[] value);
|
||||
|
||||
byte[] getRange(byte[] key, int begin, int end);
|
||||
byte[] getRange(byte[] key, long begin, long end);
|
||||
|
||||
void setRange(byte[] key, int begin, int end);
|
||||
void setRange(byte[] key, byte[] value, long offset);
|
||||
|
||||
Boolean getBit(byte[] key, long offset);
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.keyvalue.redis;
|
||||
package org.springframework.data.keyvalue.redis.connection;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
|
||||
@@ -24,24 +24,24 @@ import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
* @author Costin Leau
|
||||
* @see org.springframework.data.keyvalue.redis.connection.RedisPubSubCommands
|
||||
*/
|
||||
public class SubscribedRedisConnectionException extends InvalidDataAccessApiUsageException {
|
||||
public class RedisSubscribedConnectionException extends InvalidDataAccessApiUsageException {
|
||||
|
||||
/**
|
||||
* Constructs a new <code>SubscribedRedisConnectionException</code> instance.
|
||||
* Constructs a new <code>RedisSubscribedConnectionException</code> instance.
|
||||
*
|
||||
* @param msg
|
||||
* @param cause
|
||||
*/
|
||||
public SubscribedRedisConnectionException(String msg, Throwable cause) {
|
||||
public RedisSubscribedConnectionException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>SubscribedRedisConnectionException</code> instance.
|
||||
* Constructs a new <code>RedisSubscribedConnectionException</code> instance.
|
||||
*
|
||||
* @param msg
|
||||
*/
|
||||
public SubscribedRedisConnectionException(String msg) {
|
||||
public RedisSubscribedConnectionException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,8 @@ public interface StringRedisConnection extends RedisConnection {
|
||||
|
||||
Boolean persist(String key);
|
||||
|
||||
Boolean move(String key, int dbIndex);
|
||||
|
||||
Long ttl(String key);
|
||||
|
||||
String echo(String message);
|
||||
@@ -95,9 +97,9 @@ public interface StringRedisConnection extends RedisConnection {
|
||||
|
||||
Long append(String key, String value);
|
||||
|
||||
String getRange(String key, int start, int end);
|
||||
String getRange(String key, long start, long end);
|
||||
|
||||
void setRange(String key, int start, int end);
|
||||
void setRange(String key, String value, long offset);
|
||||
|
||||
Boolean getBit(String key, long offset);
|
||||
|
||||
|
||||
@@ -18,7 +18,10 @@ package org.springframework.data.keyvalue.redis.connection;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Subscription for Redis channels.
|
||||
* Subscription for Redis channels. Just like the underlying {@link RedisConnection},
|
||||
* it should not be used by multiple threads.
|
||||
*
|
||||
* Note that once a subscription died, it cannot accept any more subscriptions.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
@@ -29,14 +32,14 @@ public interface Subscription {
|
||||
*
|
||||
* @param channels channel names
|
||||
*/
|
||||
void subscribe(byte[]... channels);
|
||||
void subscribe(byte[]... channels) throws RedisInvalidSubscriptionException;
|
||||
|
||||
/**
|
||||
* Adds the given channel patterns to the current subscription.
|
||||
*
|
||||
* @param patterns channel patterns
|
||||
*/
|
||||
void pSubscribe(byte[]... patterns);
|
||||
void pSubscribe(byte[]... patterns) throws RedisInvalidSubscriptionException;
|
||||
|
||||
/**
|
||||
* Cancels the current subscription for all channels given by name.
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.springframework.data.keyvalue.redis.connection.jedis;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -27,10 +26,10 @@ import java.util.Set;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.keyvalue.UncategorizedKeyvalueStoreException;
|
||||
import org.springframework.data.keyvalue.redis.SubscribedRedisConnectionException;
|
||||
import org.springframework.data.keyvalue.redis.connection.DataType;
|
||||
import org.springframework.data.keyvalue.redis.connection.MessageListener;
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisConnection;
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisSubscribedConnectionException;
|
||||
import org.springframework.data.keyvalue.redis.connection.SortParameters;
|
||||
import org.springframework.data.keyvalue.redis.connection.Subscription;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
@@ -72,6 +71,7 @@ public class JedisConnection implements RedisConnection {
|
||||
|
||||
private volatile JedisSubscription subscription;
|
||||
private volatile Pipeline pipeline;
|
||||
private final int dbIndex;
|
||||
|
||||
/**
|
||||
* Constructs a new <code>JedisConnection</code> instance.
|
||||
@@ -79,7 +79,7 @@ public class JedisConnection implements RedisConnection {
|
||||
* @param jedis Jedis entity
|
||||
*/
|
||||
public JedisConnection(Jedis jedis) {
|
||||
this(jedis, null);
|
||||
this(jedis, null, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,13 +89,20 @@ public class JedisConnection implements RedisConnection {
|
||||
* @param jedis
|
||||
* @param pool can be null, if no pool is used
|
||||
*/
|
||||
public JedisConnection(Jedis jedis, Pool<Jedis> pool) {
|
||||
public JedisConnection(Jedis jedis, Pool<Jedis> pool, int dbIndex) {
|
||||
this.jedis = jedis;
|
||||
// extract underlying connection for batch operations
|
||||
client = (Client) ReflectionUtils.getField(CLIENT_FIELD, jedis);
|
||||
transaction = new Transaction(client);
|
||||
|
||||
this.pool = pool;
|
||||
|
||||
this.dbIndex = dbIndex;
|
||||
|
||||
// select the db
|
||||
if (dbIndex > 0) {
|
||||
select(dbIndex);
|
||||
}
|
||||
}
|
||||
|
||||
protected DataAccessException convertJedisAccessException(Exception ex) {
|
||||
@@ -122,6 +129,11 @@ public class JedisConnection implements RedisConnection {
|
||||
pool.returnBrokenResource(jedis);
|
||||
}
|
||||
else {
|
||||
// reset the connection
|
||||
if (dbIndex > 0) {
|
||||
select(0);
|
||||
}
|
||||
|
||||
pool.returnResource(jedis);
|
||||
}
|
||||
}
|
||||
@@ -178,10 +190,14 @@ public class JedisConnection implements RedisConnection {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public List<Object> closePipeline() {
|
||||
if (pipeline != null) {
|
||||
return pipeline.execute();
|
||||
List execute = pipeline.execute();
|
||||
if (execute != null && !execute.isEmpty()) {
|
||||
return execute;
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@@ -209,7 +225,7 @@ public class JedisConnection implements RedisConnection {
|
||||
else {
|
||||
pipeline.sort(key);
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
return (sortParams != null ? jedis.sort(key, sortParams) : jedis.sort(key));
|
||||
@@ -565,7 +581,7 @@ public class JedisConnection implements RedisConnection {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<byte[]> keys(byte[] pattern) {
|
||||
public Set<byte[]> keys(byte[] pattern) {
|
||||
try {
|
||||
if (isQueueing()) {
|
||||
transaction.keys(pattern);
|
||||
@@ -614,6 +630,23 @@ public class JedisConnection implements RedisConnection {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean move(byte[] key, int dbIndex) {
|
||||
try {
|
||||
if (isQueueing()) {
|
||||
client.move(key, dbIndex);
|
||||
return null;
|
||||
}
|
||||
if (isPipelined()) {
|
||||
client.move(key, dbIndex);
|
||||
return null;
|
||||
}
|
||||
return (jedis.move(key, dbIndex) == 1);
|
||||
} catch (Exception ex) {
|
||||
throw convertJedisAccessException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] randomKey() {
|
||||
try {
|
||||
@@ -733,7 +766,8 @@ public class JedisConnection implements RedisConnection {
|
||||
for (byte[] key : keys) {
|
||||
if (isPipelined()) {
|
||||
pipeline.watch(key);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
jedis.watch(key);
|
||||
}
|
||||
}
|
||||
@@ -902,7 +936,7 @@ public class JedisConnection implements RedisConnection {
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getRange(byte[] key, int start, int end) {
|
||||
public byte[] getRange(byte[] key, long start, long end) {
|
||||
try {
|
||||
if (isQueueing()) {
|
||||
transaction.substr(key, (int) start, (int) end);
|
||||
@@ -1021,7 +1055,7 @@ public class JedisConnection implements RedisConnection {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRange(byte[] key, int start, int end) {
|
||||
public void setRange(byte[] key, byte[] value, long start) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@@ -1088,11 +1122,11 @@ public class JedisConnection implements RedisConnection {
|
||||
if (isPipelined()) {
|
||||
final List<byte[]> args = new ArrayList<byte[]>();
|
||||
for (final byte[] arg : keys) {
|
||||
args.add(arg);
|
||||
}
|
||||
args.add(Protocol.toByteArray(timeout));
|
||||
pipeline.blpop(args.toArray(new byte[args.size()][]));
|
||||
return null;
|
||||
args.add(arg);
|
||||
}
|
||||
args.add(Protocol.toByteArray(timeout));
|
||||
pipeline.blpop(args.toArray(new byte[args.size()][]));
|
||||
return null;
|
||||
}
|
||||
return jedis.blpop(timeout, keys);
|
||||
} catch (Exception ex) {
|
||||
@@ -1109,11 +1143,11 @@ public class JedisConnection implements RedisConnection {
|
||||
if (isPipelined()) {
|
||||
final List<byte[]> args = new ArrayList<byte[]>();
|
||||
for (final byte[] arg : keys) {
|
||||
args.add(arg);
|
||||
}
|
||||
args.add(Protocol.toByteArray(timeout));
|
||||
pipeline.brpop(args.toArray(new byte[args.size()][]));
|
||||
return null;
|
||||
args.add(arg);
|
||||
}
|
||||
args.add(Protocol.toByteArray(timeout));
|
||||
pipeline.brpop(args.toArray(new byte[args.size()][]));
|
||||
return null;
|
||||
}
|
||||
return jedis.brpop(timeout, keys);
|
||||
} catch (Exception ex) {
|
||||
@@ -1758,11 +1792,10 @@ public class JedisConnection implements RedisConnection {
|
||||
public Set<Tuple> zRevRangeWithScore(byte[] key, long start, long end) {
|
||||
try {
|
||||
if (isQueueing()) {
|
||||
transaction.zrangeWithScores(key, (int) start, (int) end);
|
||||
return null;
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
if (isPipelined()) {
|
||||
pipeline.zrangeWithScores(key, (int) start, (int) end);
|
||||
pipeline.zrangeByScoreWithScores(key, (int) start, (int) end);
|
||||
return null;
|
||||
}
|
||||
return JedisUtils.convertJedisTuple(jedis.zrangeByScoreWithScores(key, (int) start, (int) end));
|
||||
@@ -2194,7 +2227,7 @@ public class JedisConnection implements RedisConnection {
|
||||
@Override
|
||||
public void pSubscribe(MessageListener listener, byte[]... patterns) {
|
||||
if (isSubscribed()) {
|
||||
throw new SubscribedRedisConnectionException(
|
||||
throw new RedisSubscribedConnectionException(
|
||||
"Connection already subscribed; use the connection Subscription to cancel or add new channels");
|
||||
}
|
||||
|
||||
@@ -2210,6 +2243,7 @@ public class JedisConnection implements RedisConnection {
|
||||
|
||||
subscription = new JedisSubscription(listener, jedisPubSub, null, patterns);
|
||||
jedis.psubscribe(jedisPubSub, patterns);
|
||||
|
||||
} catch (Exception ex) {
|
||||
throw convertJedisAccessException(ex);
|
||||
}
|
||||
@@ -2218,7 +2252,7 @@ public class JedisConnection implements RedisConnection {
|
||||
@Override
|
||||
public void subscribe(MessageListener listener, byte[]... channels) {
|
||||
if (isSubscribed()) {
|
||||
throw new SubscribedRedisConnectionException(
|
||||
throw new RedisSubscribedConnectionException(
|
||||
"Connection already subscribed; use the connection Subscription to cancel or add new channels");
|
||||
}
|
||||
|
||||
@@ -2234,6 +2268,7 @@ public class JedisConnection implements RedisConnection {
|
||||
|
||||
subscription = new JedisSubscription(listener, jedisPubSub, channels, null);
|
||||
jedis.subscribe(jedisPubSub, channels);
|
||||
|
||||
} catch (Exception ex) {
|
||||
throw convertJedisAccessException(ex);
|
||||
}
|
||||
@@ -2241,7 +2276,7 @@ public class JedisConnection implements RedisConnection {
|
||||
|
||||
private void checkSubscription() {
|
||||
if (isSubscribed()) {
|
||||
throw new SubscribedRedisConnectionException("Cannot execute command - connection is subscribed");
|
||||
throw new RedisSubscribedConnectionException("Cannot execute command - connection is subscribed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisConnection;
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import redis.clients.jedis.Jedis;
|
||||
@@ -33,7 +34,7 @@ import redis.clients.jedis.JedisShardInfo;
|
||||
import redis.clients.jedis.Protocol;
|
||||
|
||||
/**
|
||||
* Connection factory using creating <a href="http://github.com/xetorthio/jedis">Jedis</a> based connections.
|
||||
* Connection factory creating <a href="http://github.com/xetorthio/jedis">Jedis</a> based connections.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
@@ -51,6 +52,8 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
private JedisPool pool = null;
|
||||
private JedisPoolConfig poolConfig = new JedisPoolConfig();
|
||||
|
||||
private int dbIndex = 0;
|
||||
|
||||
/**
|
||||
* Constructs a new <code>JedisConnectionFactory</code> instance
|
||||
* with default settings (default connection pooling, no shard information).
|
||||
@@ -99,6 +102,18 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Post process a newly retrieved connection. Useful for decorating or executing
|
||||
* initialization commands on a new connection.
|
||||
* This implementation simply returns the connection.
|
||||
*
|
||||
* @param connection
|
||||
* @return processed connection
|
||||
*/
|
||||
protected JedisConnection postProcessConnection(JedisConnection connection) {
|
||||
return connection;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
if (shardInfo == null) {
|
||||
shardInfo = new JedisShardInfo(hostName, port);
|
||||
@@ -113,8 +128,8 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
}
|
||||
|
||||
if (usePool) {
|
||||
pool = new JedisPool(poolConfig, shardInfo.getHost(), shardInfo.getPort(),
|
||||
shardInfo.getTimeout(), shardInfo.getPassword());
|
||||
pool = new JedisPool(poolConfig, shardInfo.getHost(), shardInfo.getPort(), shardInfo.getTimeout(),
|
||||
shardInfo.getPassword());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +146,8 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
|
||||
public JedisConnection getConnection() {
|
||||
Jedis jedis = fetchJedisConnector();
|
||||
return (usePool ? new JedisConnection(jedis, pool) : new JedisConnection(jedis));
|
||||
return postProcessConnection((usePool ? new JedisConnection(jedis, pool, dbIndex) : new JedisConnection(jedis,
|
||||
null, dbIndex)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -263,4 +279,25 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
public void setPoolConfig(JedisPoolConfig poolConfig) {
|
||||
this.poolConfig = poolConfig;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the index of the database.
|
||||
*
|
||||
* @return Returns the database index
|
||||
*/
|
||||
public int getDatabase() {
|
||||
return dbIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the index of the database used by this connection factory.
|
||||
* Default is 0.
|
||||
*
|
||||
* @param index database index
|
||||
*/
|
||||
public void setDatabase(int index) {
|
||||
Assert.isTrue(index >= 0, "invalid DB index (a positive index required)");
|
||||
this.dbIndex = index;
|
||||
}
|
||||
}
|
||||
@@ -15,13 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.keyvalue.redis.connection.jedis;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.connection.MessageListener;
|
||||
import org.springframework.data.keyvalue.redis.connection.Subscription;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.data.keyvalue.redis.connection.util.AbstractSubscription;
|
||||
|
||||
import redis.clients.jedis.BinaryJedisPubSub;
|
||||
|
||||
@@ -30,132 +25,48 @@ import redis.clients.jedis.BinaryJedisPubSub;
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class JedisSubscription implements Subscription {
|
||||
class JedisSubscription extends AbstractSubscription {
|
||||
|
||||
private final MessageListener listener;
|
||||
private final BinaryJedisPubSub jedisPubSub;
|
||||
|
||||
private final Collection<byte[]> channels = new ArrayList<byte[]>(2);
|
||||
private final Collection<byte[]> patterns = new ArrayList<byte[]>(2);
|
||||
|
||||
JedisSubscription(MessageListener listener, BinaryJedisPubSub jedisPubSub, byte[][] channels, byte[][] patterns) {
|
||||
Assert.notNull(listener);
|
||||
this.listener = listener;
|
||||
super(listener, channels, patterns);
|
||||
this.jedisPubSub = jedisPubSub;
|
||||
|
||||
if (!ObjectUtils.isEmpty(channels)) {
|
||||
synchronized (this.channels) {
|
||||
for (byte[] bs : channels) {
|
||||
this.channels.add(bs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ObjectUtils.isEmpty(patterns)) {
|
||||
synchronized (this.patterns) {
|
||||
for (byte[] bs : patterns) {
|
||||
this.patterns.add(bs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<byte[]> getChannels() {
|
||||
synchronized (channels) {
|
||||
return new ArrayList<byte[]>(channels);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageListener getListener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<byte[]> getPatterns() {
|
||||
synchronized (patterns) {
|
||||
return new ArrayList<byte[]>(patterns);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pSubscribe(byte[]... patterns) {
|
||||
Assert.notEmpty(patterns, "at least one pattern required");
|
||||
|
||||
synchronized (this.patterns) {
|
||||
for (byte[] bs : patterns) {
|
||||
this.patterns.add(bs);
|
||||
}
|
||||
}
|
||||
|
||||
jedisPubSub.psubscribe(patterns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pUnsubscribe() {
|
||||
synchronized (patterns) {
|
||||
patterns.clear();
|
||||
}
|
||||
protected void doClose() {
|
||||
jedisPubSub.unsubscribe();
|
||||
jedisPubSub.punsubscribe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pUnsubscribe(byte[]... patterns) {
|
||||
if (ObjectUtils.isEmpty(patterns)) {
|
||||
unsubscribe();
|
||||
protected void doPsubscribe(byte[]... patterns) {
|
||||
jedisPubSub.psubscribe(patterns);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPUnsubscribe(boolean all, byte[]... patterns) {
|
||||
if (all) {
|
||||
jedisPubSub.punsubscribe();
|
||||
}
|
||||
|
||||
else {
|
||||
synchronized (this.patterns) {
|
||||
for (byte[] bs : patterns) {
|
||||
this.patterns.remove(bs);
|
||||
}
|
||||
}
|
||||
|
||||
jedisPubSub.punsubscribe(patterns);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribe(byte[]... channels) {
|
||||
Assert.notEmpty(channels, "at least one channel required");
|
||||
|
||||
synchronized (this.channels) {
|
||||
for (byte[] bs : channels) {
|
||||
this.channels.add(bs);
|
||||
}
|
||||
}
|
||||
|
||||
protected void doSubscribe(byte[]... channels) {
|
||||
jedisPubSub.subscribe(channels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
synchronized (channels) {
|
||||
channels.clear();
|
||||
}
|
||||
jedisPubSub.unsubscribe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribe(byte[]... channels) {
|
||||
if (ObjectUtils.isEmpty(channels)) {
|
||||
unsubscribe();
|
||||
protected void doUnsubscribe(boolean all, byte[]... channels) {
|
||||
if (all) {
|
||||
jedisPubSub.unsubscribe();
|
||||
}
|
||||
else {
|
||||
synchronized (this.channels) {
|
||||
for (byte[] bs : channels) {
|
||||
this.channels.remove(bs);
|
||||
}
|
||||
}
|
||||
|
||||
jedisPubSub.unsubscribe(channels);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlive() {
|
||||
return jedisPubSub.isSubscribed();
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ import java.util.concurrent.TimeoutException;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.keyvalue.redis.RedisConnectionFailureException;
|
||||
import org.springframework.data.keyvalue.redis.UncategorizedRedisException;
|
||||
import org.springframework.data.keyvalue.redis.RedisSystemException;
|
||||
import org.springframework.data.keyvalue.redis.connection.DefaultTuple;
|
||||
import org.springframework.data.keyvalue.redis.connection.MessageListener;
|
||||
import org.springframework.data.keyvalue.redis.connection.SortParameters;
|
||||
@@ -55,8 +55,8 @@ public abstract class JedisUtils {
|
||||
|
||||
private static final String OK_CODE = "OK";
|
||||
private static final String OK_MULTI_CODE = "+OK";
|
||||
private static final byte[] ONE = new byte[] { 0 };
|
||||
private static final byte[] ZERO = new byte[] { 1 };
|
||||
private static final byte[] ONE = new byte[] { 1 };
|
||||
private static final byte[] ZERO = new byte[] { 0 };
|
||||
|
||||
/**
|
||||
* Converts the given, native Jedis exception to Spring's DAO hierarchy.
|
||||
@@ -87,7 +87,7 @@ public abstract class JedisUtils {
|
||||
return convertJedisAccessException((JedisException) ex);
|
||||
}
|
||||
|
||||
return new UncategorizedRedisException("Unknown exception", ex);
|
||||
return new RedisSystemException("Unknown exception", ex);
|
||||
}
|
||||
|
||||
static DataAccessException convertJedisAccessException(IOException ex) {
|
||||
@@ -194,10 +194,13 @@ public abstract class JedisUtils {
|
||||
|
||||
static Properties info(String string) {
|
||||
Properties info = new Properties();
|
||||
StringReader stringReader = new StringReader(string);
|
||||
try {
|
||||
info.load(new StringReader(string));
|
||||
info.load(stringReader);
|
||||
} catch (Exception ex) {
|
||||
throw new UncategorizedRedisException("Cannot read Redis info", ex);
|
||||
throw new RedisSystemException("Cannot read Redis info", ex);
|
||||
} finally {
|
||||
stringReader.close();
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
package org.springframework.data.keyvalue.redis.connection.jredis;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
@@ -32,7 +31,7 @@ import org.jredis.Query.Support;
|
||||
import org.jredis.ri.alphazero.JRedisService;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.keyvalue.UncategorizedKeyvalueStoreException;
|
||||
import org.springframework.data.keyvalue.redis.UncategorizedRedisException;
|
||||
import org.springframework.data.keyvalue.redis.RedisSystemException;
|
||||
import org.springframework.data.keyvalue.redis.connection.DataType;
|
||||
import org.springframework.data.keyvalue.redis.connection.MessageListener;
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisConnection;
|
||||
@@ -76,13 +75,17 @@ public class JredisConnection implements RedisConnection {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws UncategorizedRedisException {
|
||||
public void close() throws RedisSystemException {
|
||||
isClosed = true;
|
||||
|
||||
// don't actually close the connection
|
||||
// if a pool is used
|
||||
if (!isPool) {
|
||||
jredis.quit();
|
||||
try {
|
||||
jredis.quit();
|
||||
} catch (Exception ex) {
|
||||
throw convertJredisAccessException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,9 +303,9 @@ public class JredisConnection implements RedisConnection {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<byte[]> keys(byte[] pattern) {
|
||||
public Set<byte[]> keys(byte[] pattern) {
|
||||
try {
|
||||
return JredisUtils.convertCollection(jredis.keys(JredisUtils.decode(pattern)));
|
||||
return JredisUtils.convertToSet(jredis.keys(JredisUtils.decode(pattern)));
|
||||
} catch (Exception ex) {
|
||||
throw convertJredisAccessException(ex);
|
||||
}
|
||||
@@ -318,6 +321,16 @@ public class JredisConnection implements RedisConnection {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Boolean move(byte[] key, int dbIndex) {
|
||||
try {
|
||||
return jredis.move(JredisUtils.decode(key), dbIndex);
|
||||
} catch (Exception ex) {
|
||||
throw convertJredisAccessException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] randomKey() {
|
||||
try {
|
||||
@@ -460,7 +473,7 @@ public class JredisConnection implements RedisConnection {
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getRange(byte[] key, int start, int end) {
|
||||
public byte[] getRange(byte[] key, long start, long end) {
|
||||
try {
|
||||
return jredis.substr(JredisUtils.decode(key), start, end);
|
||||
} catch (Exception ex) {
|
||||
@@ -515,7 +528,7 @@ public class JredisConnection implements RedisConnection {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRange(byte[] key, int start, int end) {
|
||||
public void setRange(byte[] key, byte[] value, long start) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@@ -1029,7 +1042,7 @@ public class JredisConnection implements RedisConnection {
|
||||
@Override
|
||||
public Set<byte[]> hKeys(byte[] key) {
|
||||
try {
|
||||
return new LinkedHashSet<byte[]>(JredisUtils.convertCollection(jredis.hkeys(JredisUtils.decode(key))));
|
||||
return new LinkedHashSet<byte[]>(JredisUtils.convertToSet(jredis.hkeys(JredisUtils.decode(key))));
|
||||
} catch (Exception ex) {
|
||||
throw convertJredisAccessException(ex);
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ public class JredisConnectionFactory implements InitializingBean, DisposableBean
|
||||
private int timeout;
|
||||
|
||||
private boolean usePool = true;
|
||||
private int dbIndex = DEFAULT_REDIS_DB;
|
||||
|
||||
private JRedisService pool = null;
|
||||
// taken from JRedis code
|
||||
@@ -75,7 +76,7 @@ public class JredisConnectionFactory implements InitializingBean, DisposableBean
|
||||
public void afterPropertiesSet() {
|
||||
if (connectionSpec == null) {
|
||||
Assert.hasText(hostName);
|
||||
connectionSpec = DefaultConnectionSpec.newSpec(hostName, port, DEFAULT_REDIS_DB, DEFAULT_REDIS_PASSWORD);
|
||||
connectionSpec = DefaultConnectionSpec.newSpec(hostName, port, dbIndex, DEFAULT_REDIS_PASSWORD);
|
||||
connectionSpec.setConnectionFlag(Connection.Flag.RELIABLE, false);
|
||||
|
||||
if (StringUtils.hasLength(password)) {
|
||||
@@ -105,10 +106,22 @@ public class JredisConnectionFactory implements InitializingBean, DisposableBean
|
||||
|
||||
@Override
|
||||
public RedisConnection getConnection() {
|
||||
return new JredisConnection((usePool ? pool : new JRedisClient(connectionSpec)));
|
||||
return postProcessConnection(new JredisConnection((usePool ? pool : new JRedisClient(connectionSpec))));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Post process a newly retrieved connection. Useful for decorating or executing
|
||||
* initialization commands on a new connection.
|
||||
* This implementation simply returns the connection.
|
||||
*
|
||||
* @param connection
|
||||
* @return processed connection
|
||||
*/
|
||||
protected RedisConnection postProcessConnection(JredisConnection connection) {
|
||||
return connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
|
||||
if (ex instanceof ClientRuntimeException) {
|
||||
@@ -210,4 +223,24 @@ public class JredisConnectionFactory implements InitializingBean, DisposableBean
|
||||
this.poolSize = poolSize;
|
||||
usePool = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the database.
|
||||
*
|
||||
* @return Returns the database index
|
||||
*/
|
||||
public int getDatabase() {
|
||||
return dbIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the index of the database used by this connection factory.
|
||||
* Can be between 0 (default) and 15.
|
||||
*
|
||||
* @param index database index
|
||||
*/
|
||||
public void setDatabase(int index) {
|
||||
Assert.isTrue(index >= 0, "invalid DB index (a positive index required)");
|
||||
this.dbIndex = index;
|
||||
}
|
||||
}
|
||||
@@ -16,11 +16,10 @@
|
||||
|
||||
package org.springframework.data.keyvalue.redis.connection.jredis;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jredis.ClientRuntimeException;
|
||||
import org.jredis.RedisException;
|
||||
@@ -33,6 +32,7 @@ import org.springframework.data.keyvalue.redis.connection.DataType;
|
||||
import org.springframework.data.keyvalue.redis.connection.SortParameters;
|
||||
import org.springframework.data.keyvalue.redis.connection.SortParameters.Order;
|
||||
import org.springframework.data.keyvalue.redis.connection.SortParameters.Range;
|
||||
import org.springframework.data.keyvalue.redis.connection.util.DecodeUtils;
|
||||
|
||||
/**
|
||||
* Helper class featuring methods for JRedis connection handling, providing support for exception translation.
|
||||
@@ -81,47 +81,28 @@ public abstract class JredisUtils {
|
||||
}
|
||||
|
||||
static String decode(byte[] bytes) {
|
||||
return Base64.encodeToString(bytes, false);
|
||||
}
|
||||
|
||||
static String[] decodeMultiple(byte[]... bytes) {
|
||||
String[] result = new String[bytes.length];
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
result[i] = decode(bytes[i]);
|
||||
}
|
||||
return result;
|
||||
return DecodeUtils.decode(bytes);
|
||||
}
|
||||
|
||||
static byte[] encode(String string) {
|
||||
return Base64.decode(string);
|
||||
return DecodeUtils.encode(string);
|
||||
}
|
||||
|
||||
static String[] decodeMultiple(byte[]... bytes) {
|
||||
return DecodeUtils.decodeMultiple(bytes);
|
||||
}
|
||||
|
||||
static Map<byte[], byte[]> encodeMap(Map<String, byte[]> map) {
|
||||
Map<byte[], byte[]> result = new LinkedHashMap<byte[], byte[]>(map.size());
|
||||
for (Map.Entry<String, byte[]> entry : map.entrySet()) {
|
||||
result.put(encode(entry.getKey()), entry.getValue());
|
||||
}
|
||||
return result;
|
||||
return DecodeUtils.encodeMap(map);
|
||||
}
|
||||
|
||||
static Collection<byte[]> convertCollection(Collection<String> keys) {
|
||||
Collection<byte[]> list = new ArrayList<byte[]>(keys.size());
|
||||
|
||||
for (String string : keys) {
|
||||
list.add(Base64.decode(string));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
static Map<String, byte[]> decodeMap(Map<byte[], byte[]> tuple) {
|
||||
Map<String, byte[]> result = new LinkedHashMap<String, byte[]>(tuple.size());
|
||||
for (Map.Entry<byte[], byte[]> entry : tuple.entrySet()) {
|
||||
result.put(decode(entry.getKey()), entry.getValue());
|
||||
}
|
||||
return result;
|
||||
return DecodeUtils.decodeMap(tuple);
|
||||
}
|
||||
|
||||
static Set<byte[]> convertToSet(Collection<String> keys) {
|
||||
return DecodeUtils.convertToSet(keys);
|
||||
}
|
||||
|
||||
static Sort applySortingParams(Sort jredisSort, SortParameters params, byte[] storeKey) {
|
||||
if (params != null) {
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.connection.rjc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.List;
|
||||
|
||||
import org.idevlab.rjc.ds.RedisConnection;
|
||||
import org.idevlab.rjc.message.RedisNodeSubscriber;
|
||||
import org.idevlab.rjc.protocol.Protocol.Command;
|
||||
|
||||
/**
|
||||
* Basic decorator suppressing close() calls to the underlying connection.
|
||||
* Used for reusing arbitrary connections with {@link RedisNodeSubscriber} without
|
||||
* resorting to connection pooling.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class CloseSuppressingRjcConnection implements RedisConnection {
|
||||
|
||||
private final RedisConnection delegate;
|
||||
|
||||
/**
|
||||
* Constructs a new <code>CloseSuppressingRjcConnection</code> instance.
|
||||
*
|
||||
* @param delegate
|
||||
*/
|
||||
CloseSuppressingRjcConnection(RedisConnection delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
public void connect() throws UnknownHostException, IOException {
|
||||
delegate.connect();
|
||||
}
|
||||
|
||||
public List<Object> getAll() {
|
||||
return delegate.getAll();
|
||||
}
|
||||
|
||||
public String getBulkReply() {
|
||||
return delegate.getBulkReply();
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return delegate.getHost();
|
||||
}
|
||||
|
||||
public Long getIntegerReply() {
|
||||
return delegate.getIntegerReply();
|
||||
}
|
||||
|
||||
public List<String> getMultiBulkReply() {
|
||||
return delegate.getMultiBulkReply();
|
||||
}
|
||||
|
||||
public List<Object> getObjectMultiBulkReply() {
|
||||
return delegate.getObjectMultiBulkReply();
|
||||
}
|
||||
|
||||
public Object getOne() {
|
||||
return delegate.getOne();
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return delegate.getPort();
|
||||
}
|
||||
|
||||
public String getStatusCodeReply() {
|
||||
return delegate.getStatusCodeReply();
|
||||
}
|
||||
|
||||
public int getTimeout() {
|
||||
return delegate.getTimeout();
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return delegate.isConnected();
|
||||
}
|
||||
|
||||
public void rollbackTimeout() {
|
||||
delegate.rollbackTimeout();
|
||||
}
|
||||
|
||||
public void sendCommand(Command arg0, byte[]... arg1) {
|
||||
delegate.sendCommand(arg0, arg1);
|
||||
}
|
||||
|
||||
public void sendCommand(Command arg0, String... arg1) {
|
||||
delegate.sendCommand(arg0, arg1);
|
||||
}
|
||||
|
||||
public void sendCommand(Command arg0) {
|
||||
delegate.sendCommand(arg0);
|
||||
}
|
||||
|
||||
public void setTimeoutInfinite() {
|
||||
delegate.setTimeoutInfinite();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.connection.rjc;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.idevlab.rjc.ds.DataSource;
|
||||
import org.idevlab.rjc.ds.PoolableDataSource;
|
||||
import org.idevlab.rjc.ds.SimpleDataSource;
|
||||
import org.idevlab.rjc.protocol.Protocol;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisConnection;
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.keyvalue.redis.connection.jedis.JedisConnectionFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Connection factory creating <a href="http://github.com/e-mzungu/rjc/">rjc</a> based connections.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class RjcConnectionFactory implements InitializingBean, DisposableBean, RedisConnectionFactory {
|
||||
|
||||
private final static Log log = LogFactory.getLog(JedisConnectionFactory.class);
|
||||
|
||||
private String hostName = "localhost";
|
||||
private int port = Protocol.DEFAULT_PORT;
|
||||
private int timeout = Protocol.DEFAULT_TIMEOUT;
|
||||
private String password;
|
||||
|
||||
private boolean usePool = true;
|
||||
private int dbIndex = 0;
|
||||
private DataSource dataSource;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new <code>RjcConnectionFactory</code> instance
|
||||
* with default settings (default connection pooling, no shard information).
|
||||
*/
|
||||
public RjcConnectionFactory() {
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
if (usePool) {
|
||||
PoolableDataSource pool = new PoolableDataSource();
|
||||
pool.setHost(hostName);
|
||||
pool.setPort(port);
|
||||
pool.setPassword(password);
|
||||
pool.setTimeout(timeout);
|
||||
|
||||
pool.init();
|
||||
|
||||
dataSource = pool;
|
||||
|
||||
}
|
||||
else {
|
||||
dataSource = new SimpleDataSource(hostName, port, timeout, password);
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
if (usePool && dataSource != null) {
|
||||
try {
|
||||
((PoolableDataSource) dataSource).close();
|
||||
} catch (Exception ex) {
|
||||
log.warn("Cannot properly close Rjc pool", ex);
|
||||
}
|
||||
dataSource = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisConnection getConnection() {
|
||||
return postProcessConnection(new RjcConnection(dataSource.getConnection(), dbIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
* Post process a newly retrieved connection. Useful for decorating or executing
|
||||
* initialization commands on a new connection.
|
||||
* This implementation simply returns the connection.
|
||||
*
|
||||
* @param connection
|
||||
* @return processed connection
|
||||
*/
|
||||
protected RjcConnection postProcessConnection(RjcConnection connection) {
|
||||
return connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
|
||||
return RjcUtils.convertRjcAccessException(ex);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the Redis hostName.
|
||||
*
|
||||
* @return Returns the hostName
|
||||
*/
|
||||
public String getHostName() {
|
||||
return hostName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Redis hostName.
|
||||
*
|
||||
* @param hostName The hostName to set.
|
||||
*/
|
||||
public void setHostName(String hostName) {
|
||||
this.hostName = hostName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the password used for authenticating with the Redis server.
|
||||
*
|
||||
* @return password for authentication
|
||||
*/
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the password used for authenticating with the Redis server.
|
||||
*
|
||||
* @param password the password to set
|
||||
*/
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the port used to connect to the Redis instance.
|
||||
*
|
||||
* @return Redis port.
|
||||
*/
|
||||
public int getPort() {
|
||||
return port;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the port used to connect to the Redis instance.
|
||||
*
|
||||
* @param port Redis port
|
||||
*/
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
/**
|
||||
* Returns the timeout.
|
||||
*
|
||||
* @return Returns the timeout
|
||||
*/
|
||||
public int getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param timeout The timeout to set.
|
||||
*/
|
||||
public void setTimeout(int timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the use of a connection pool.
|
||||
*
|
||||
* @return Returns the use of connection pooling.
|
||||
*/
|
||||
public boolean getUsePool() {
|
||||
return usePool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on or off the use of connection pooling.
|
||||
*
|
||||
* @param usePool The usePool to set.
|
||||
*/
|
||||
public void setUsePool(boolean usePool) {
|
||||
this.usePool = usePool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the database.
|
||||
*
|
||||
* @return Returns the database index
|
||||
*/
|
||||
public int getDatabase() {
|
||||
return dbIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the index of the database used by this connection factory.
|
||||
* Can be between 0 (default) and 15.
|
||||
*
|
||||
* @param index database index
|
||||
*/
|
||||
public void setDatabase(int index) {
|
||||
Assert.isTrue(index >= 0, "invalid DB index (a positive index required)");
|
||||
this.dbIndex = index;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.connection.rjc;
|
||||
|
||||
import org.idevlab.rjc.message.MessageListener;
|
||||
import org.idevlab.rjc.message.PMessageListener;
|
||||
import org.springframework.data.keyvalue.redis.connection.DefaultMessage;
|
||||
|
||||
/**
|
||||
* Message listener adapter for RJC library.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class RjcMessageListener implements MessageListener, PMessageListener {
|
||||
|
||||
private final org.springframework.data.keyvalue.redis.connection.MessageListener listener;
|
||||
|
||||
RjcMessageListener(org.springframework.data.keyvalue.redis.connection.MessageListener messageListener) {
|
||||
this.listener = messageListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(String channel, String message) {
|
||||
listener.onMessage(new DefaultMessage(RjcUtils.encode(channel), RjcUtils.encode(message)), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(String pattern, String channel, String message) {
|
||||
listener.onMessage(new DefaultMessage(RjcUtils.encode(channel), RjcUtils.encode(message)),
|
||||
RjcUtils.encode(pattern));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.connection.rjc;
|
||||
|
||||
import org.idevlab.rjc.message.RedisNodeSubscriber;
|
||||
import org.springframework.data.keyvalue.redis.connection.MessageListener;
|
||||
import org.springframework.data.keyvalue.redis.connection.util.AbstractSubscription;
|
||||
|
||||
/**
|
||||
* Message subscription on top of RJC.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class RjcSubscription extends AbstractSubscription {
|
||||
|
||||
private final RedisNodeSubscriber subscriber;
|
||||
|
||||
RjcSubscription(MessageListener listener, RedisNodeSubscriber subscriber) {
|
||||
super(listener);
|
||||
this.subscriber = subscriber;
|
||||
subscriber.setMessageListener(new RjcMessageListener(listener));
|
||||
subscriber.setPMessageListener(new RjcMessageListener(listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doClose() {
|
||||
subscriber.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPsubscribe(byte[]... patterns) {
|
||||
subscriber.psubscribe(RjcUtils.decodeMultiple(patterns));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPUnsubscribe(boolean all, byte[]... patterns) {
|
||||
subscriber.punsubscribe(RjcUtils.decodeMultiple(patterns));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSubscribe(byte[]... channels) {
|
||||
subscriber.subscribe(RjcUtils.decodeMultiple(channels));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doUnsubscribe(boolean all, byte[]... channels) {
|
||||
subscriber.punsubscribe(RjcUtils.decodeMultiple(channels));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.connection.rjc;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.idevlab.rjc.ElementScore;
|
||||
import org.idevlab.rjc.RedisException;
|
||||
import org.idevlab.rjc.SortingParams;
|
||||
import org.idevlab.rjc.ZParams;
|
||||
import org.idevlab.rjc.Client.LIST_POSITION;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.keyvalue.redis.RedisSystemException;
|
||||
import org.springframework.data.keyvalue.redis.connection.DataType;
|
||||
import org.springframework.data.keyvalue.redis.connection.DefaultTuple;
|
||||
import org.springframework.data.keyvalue.redis.connection.SortParameters;
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisListCommands.Position;
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisZSetCommands.Aggregate;
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisZSetCommands.Tuple;
|
||||
import org.springframework.data.keyvalue.redis.connection.SortParameters.Order;
|
||||
import org.springframework.data.keyvalue.redis.connection.SortParameters.Range;
|
||||
import org.springframework.data.keyvalue.redis.connection.util.DecodeUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Helper class featuring methods for RJC connection handling, providing support for exception translation.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public abstract class RjcUtils {
|
||||
|
||||
private static final String ONE = "1";
|
||||
private static final String ZERO = "0";
|
||||
|
||||
|
||||
public static DataAccessException convertRjcAccessException(RuntimeException ex) {
|
||||
if (ex instanceof RedisException) {
|
||||
return convertRjcAccessException((RedisException) ex);
|
||||
}
|
||||
|
||||
return new RedisSystemException("Unknown exception", ex);
|
||||
}
|
||||
|
||||
public static DataAccessException convertRjcAccessException(RedisException ex) {
|
||||
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
static DataType convertDataType(String type) {
|
||||
if ("string".equals(type)) {
|
||||
return DataType.STRING;
|
||||
}
|
||||
else if ("list".equals(type)) {
|
||||
return DataType.LIST;
|
||||
}
|
||||
else if ("set".equals(type)) {
|
||||
return DataType.SET;
|
||||
}
|
||||
else if ("zset".equals(type)) {
|
||||
return DataType.ZSET;
|
||||
}
|
||||
else if ("hash".equals(type)) {
|
||||
return DataType.HASH;
|
||||
}
|
||||
else if ("none".equals(type)) {
|
||||
return DataType.NONE;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static String decode(byte[] bytes) {
|
||||
return DecodeUtils.decode(bytes);
|
||||
}
|
||||
|
||||
static byte[] encode(String string) {
|
||||
return DecodeUtils.encode(string);
|
||||
}
|
||||
|
||||
static String[] decodeMultiple(byte[]... bytes) {
|
||||
return DecodeUtils.decodeMultiple(bytes);
|
||||
}
|
||||
|
||||
static String[] flatten(Map<byte[], byte[]> tuple) {
|
||||
String[] result = new String[tuple.size() * 2];
|
||||
int index = 0;
|
||||
for (Map.Entry<byte[], byte[]> entry : tuple.entrySet()) {
|
||||
result[index++] = decode(entry.getKey());
|
||||
result[index++] = decode(entry.getValue());
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
static Set<byte[]> convertToSet(Collection<String> keys) {
|
||||
if (keys == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return DecodeUtils.convertToSet(keys);
|
||||
}
|
||||
|
||||
static List<byte[]> convertToList(Collection<String> keys) {
|
||||
if (keys == null) {
|
||||
return null;
|
||||
}
|
||||
return DecodeUtils.convertToList(keys);
|
||||
}
|
||||
|
||||
static SortingParams convertSortParams(SortParameters params) {
|
||||
SortingParams rjcSort = null;
|
||||
|
||||
if (params != null) {
|
||||
rjcSort = new SortingParams();
|
||||
|
||||
byte[] byPattern = params.getByPattern();
|
||||
if (byPattern != null) {
|
||||
rjcSort.by(DecodeUtils.decode(byPattern));
|
||||
}
|
||||
byte[][] getPattern = params.getGetPattern();
|
||||
|
||||
if (getPattern != null && getPattern.length > 0) {
|
||||
for (byte[] bs : getPattern) {
|
||||
rjcSort.get(DecodeUtils.decode(bs));
|
||||
}
|
||||
}
|
||||
Range limit = params.getLimit();
|
||||
if (limit != null) {
|
||||
rjcSort.limit((int) limit.getStart(), (int) limit.getCount());
|
||||
}
|
||||
Order order = params.getOrder();
|
||||
if (order != null && order.equals(Order.DESC)) {
|
||||
rjcSort.desc();
|
||||
}
|
||||
Boolean isAlpha = params.isAlphabetic();
|
||||
if (isAlpha != null && isAlpha) {
|
||||
rjcSort.alpha();
|
||||
}
|
||||
}
|
||||
return rjcSort;
|
||||
}
|
||||
|
||||
static Properties info(String string) {
|
||||
Properties info = new Properties();
|
||||
StringReader stringReader = new StringReader(string);
|
||||
try {
|
||||
info.load(stringReader);
|
||||
} catch (Exception ex) {
|
||||
throw new RedisSystemException("Cannot read Redis info", ex);
|
||||
} finally {
|
||||
stringReader.close();
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
static String asBit(boolean value) {
|
||||
return (value ? ONE : ZERO);
|
||||
}
|
||||
|
||||
static LIST_POSITION convertPosition(Position where) {
|
||||
switch (where) {
|
||||
case BEFORE:
|
||||
return LIST_POSITION.BEFORE;
|
||||
|
||||
case AFTER:
|
||||
return LIST_POSITION.AFTER;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static ZParams toZParams(Aggregate aggregate, int[] weights) {
|
||||
return new ZParams().weights(weights).aggregate(ZParams.Aggregate.valueOf(aggregate.name()));
|
||||
}
|
||||
|
||||
static Set<Tuple> convertElementScore(List<ElementScore> tuples) {
|
||||
Set<Tuple> value = new LinkedHashSet<Tuple>(tuples.size());
|
||||
for (ElementScore tuple : tuples) {
|
||||
value.add(new DefaultTuple(encode(tuple.getElement()), Double.valueOf(tuple.getScore())));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static Map<byte[], byte[]> encodeMap(Map<String, String> map) {
|
||||
Map<byte[], byte[]> result = new LinkedHashMap<byte[], byte[]>(map.size());
|
||||
for (Map.Entry<String, String> entry : map.entrySet()) {
|
||||
result.put(encode(entry.getKey()), encode(entry.getValue()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static Map<String, String> decodeMap(Map<byte[], byte[]> map) {
|
||||
Map<String, String> result = new LinkedHashMap<String, String>(map.size());
|
||||
for (Map.Entry<byte[], byte[]> entry : map.entrySet()) {
|
||||
result.put(decode(entry.getKey()), decode(entry.getValue()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static Double convert(String zscore) {
|
||||
return (zscore == null ? null : Double.valueOf(zscore));
|
||||
}
|
||||
|
||||
|
||||
static String[] addArray(String[] one, String[] two) {
|
||||
if (ObjectUtils.isEmpty(one)) {
|
||||
return two;
|
||||
}
|
||||
if (ObjectUtils.isEmpty(two)) {
|
||||
return one;
|
||||
}
|
||||
|
||||
String[] result = Arrays.copyOf(one, one.length + two.length);
|
||||
System.arraycopy(two, 0, result, one.length, two.length);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -13,29 +13,26 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.keyvalue.redis.core;
|
||||
package org.springframework.data.keyvalue.redis.connection.rjc;
|
||||
|
||||
import org.idevlab.rjc.ds.DataSource;
|
||||
import org.idevlab.rjc.ds.RedisConnection;
|
||||
|
||||
/**
|
||||
* Default {@link KeyBound} implementation.
|
||||
* Meant for internal usage.
|
||||
* Basic data source that always returns the same connection.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class DefaultKeyBound<K> implements KeyBound<K> {
|
||||
class SingleDataSource implements DataSource {
|
||||
|
||||
private K key;
|
||||
private final RedisConnection connection;
|
||||
|
||||
public DefaultKeyBound(K key) {
|
||||
setKey(key);
|
||||
SingleDataSource(RedisConnection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
protected void setKey(K key) {
|
||||
this.key = key;
|
||||
public RedisConnection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Connection package for <a href="https://github.com/e-mzungu/rjc">RJC</a> library.
|
||||
*/
|
||||
package org.springframework.data.keyvalue.redis.connection.rjc;
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.connection.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.connection.MessageListener;
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisInvalidSubscriptionException;
|
||||
import org.springframework.data.keyvalue.redis.connection.Subscription;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Base implementation for a subscription handling the channel/pattern registration so subclasses only have to deal
|
||||
* with the actual registration/unregistration.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public abstract class AbstractSubscription implements Subscription {
|
||||
|
||||
private final Collection<ByteArrayWrapper> channels = new ArrayList<ByteArrayWrapper>(2);
|
||||
private final Collection<ByteArrayWrapper> patterns = new ArrayList<ByteArrayWrapper>(2);
|
||||
private final AtomicBoolean alive = new AtomicBoolean(true);
|
||||
private final MessageListener listener;
|
||||
|
||||
protected AbstractSubscription(MessageListener listener) {
|
||||
this(listener, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>AbstractSubscription</code> instance. Allows channels and patterns to be added
|
||||
* to the subscription w/o triggering a subscription action (as some clients (Jedis) require an initial call
|
||||
* before entering into listening mode).
|
||||
*
|
||||
* @param listener
|
||||
* @param channels
|
||||
* @param patterns
|
||||
*/
|
||||
protected AbstractSubscription(MessageListener listener, byte[][] channels, byte[][] patterns) {
|
||||
Assert.notNull(listener);
|
||||
this.listener = listener;
|
||||
|
||||
synchronized (this.channels) {
|
||||
add(this.channels, channels);
|
||||
}
|
||||
synchronized (this.patterns) {
|
||||
add(this.patterns, patterns);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to the given channels.
|
||||
*
|
||||
* @param channels channels to subscribe to
|
||||
*/
|
||||
protected abstract void doSubscribe(byte[]... channels);
|
||||
|
||||
/**
|
||||
* Channel unsubscribe.
|
||||
*
|
||||
* @param all true if all the channels are unsubscribed (used as a hint for the underlying implementation).
|
||||
* @param channels channels to be unsubscribed
|
||||
*/
|
||||
protected abstract void doUnsubscribe(boolean all, byte[]... channels);
|
||||
|
||||
/**
|
||||
* Subscribe to the given patterns
|
||||
*
|
||||
* @param patterns patterns to subscribe to
|
||||
*/
|
||||
protected abstract void doPsubscribe(byte[]... patterns);
|
||||
|
||||
/**
|
||||
* Pattern unsubscribe.
|
||||
*
|
||||
* @param all true if all the patterns are unsubscribed (used as a hint for the underlying implementation).
|
||||
* @param patterns patterns to be unsubscribed
|
||||
*/
|
||||
protected abstract void doPUnsubscribe(boolean all, byte[]... patterns);
|
||||
|
||||
/**
|
||||
* Shutdown the subscription and free any resources held.
|
||||
*/
|
||||
protected abstract void doClose();
|
||||
|
||||
@Override
|
||||
public MessageListener getListener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<byte[]> getChannels() {
|
||||
synchronized (channels) {
|
||||
return clone(channels);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<byte[]> getPatterns() {
|
||||
synchronized (patterns) {
|
||||
return clone(patterns);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pSubscribe(byte[]... patterns) {
|
||||
checkPulse();
|
||||
|
||||
Assert.notEmpty(patterns, "at least one pattern required");
|
||||
|
||||
synchronized (this.patterns) {
|
||||
add(this.patterns, patterns);
|
||||
}
|
||||
|
||||
doPsubscribe(patterns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pUnsubscribe() {
|
||||
pUnsubscribe((byte[][]) null);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void subscribe(byte[]... channels) {
|
||||
checkPulse();
|
||||
|
||||
Assert.notEmpty(channels, "at least one channel required");
|
||||
|
||||
synchronized (this.channels) {
|
||||
add(this.channels, channels);
|
||||
}
|
||||
|
||||
doSubscribe(channels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
unsubscribe((byte[][]) null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pUnsubscribe(byte[]... patts) {
|
||||
if (!isAlive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// shortcut for unsubscribing all patterns
|
||||
if (ObjectUtils.isEmpty(patts)) {
|
||||
if (!this.patterns.isEmpty()) {
|
||||
patts = getPatterns().toArray(new byte[this.patterns.size()][]);
|
||||
synchronized (this.patterns) {
|
||||
this.patterns.clear();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// nothing to unsubscribe from
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
synchronized (this.patterns) {
|
||||
remove(this.patterns, patts);
|
||||
}
|
||||
}
|
||||
|
||||
if (isWorking()) {
|
||||
doPUnsubscribe(this.patterns.isEmpty(), patts);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribe(byte[]... chans) {
|
||||
if (!isAlive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// shortcut for unsubscribing all channels
|
||||
if (ObjectUtils.isEmpty(chans)) {
|
||||
if (!this.channels.isEmpty()) {
|
||||
chans = getPatterns().toArray(new byte[this.channels.size()][]);
|
||||
synchronized (this.channels) {
|
||||
this.channels.clear();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// nothing to unsubscribe from
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
synchronized (this.channels) {
|
||||
remove(this.channels, chans);
|
||||
}
|
||||
}
|
||||
|
||||
if (isWorking()) {
|
||||
doUnsubscribe(this.channels.isEmpty(), chans);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlive() {
|
||||
return alive.get();
|
||||
}
|
||||
|
||||
private void checkPulse() {
|
||||
if (!isAlive()) {
|
||||
throw new RedisInvalidSubscriptionException("Subscription has been unsubscribed and cannot be used anymore");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isWorking() {
|
||||
if (channels.isEmpty() && patterns.isEmpty()) {
|
||||
alive.set(false);
|
||||
doClose();
|
||||
}
|
||||
return isAlive();
|
||||
}
|
||||
|
||||
|
||||
private static Collection<byte[]> clone(Collection<ByteArrayWrapper> col) {
|
||||
Collection<byte[]> list = new ArrayList<byte[]>(col.size());
|
||||
for (ByteArrayWrapper wrapper : col) {
|
||||
list.add(wrapper.getArray().clone());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
private static void add(Collection<ByteArrayWrapper> col, byte[]... bytes) {
|
||||
if (!ObjectUtils.isEmpty(bytes)) {
|
||||
for (byte[] bs : bytes) {
|
||||
col.add(new ByteArrayWrapper(bs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void remove(Collection<ByteArrayWrapper> col, byte[]... bytes) {
|
||||
if (!ObjectUtils.isEmpty(bytes)) {
|
||||
for (byte[] bs : bytes) {
|
||||
col.remove(new ByteArrayWrapper(bs));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.data.keyvalue.redis.connection.jredis;
|
||||
package org.springframework.data.keyvalue.redis.connection.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.connection.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Simple wrapper class used for wrapping arrays so they can be used as keys inside maps.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class ByteArrayWrapper {
|
||||
|
||||
private final byte[] array;
|
||||
private final int hashCode;
|
||||
|
||||
public ByteArrayWrapper(byte[] array) {
|
||||
this.array = array;
|
||||
this.hashCode = Arrays.hashCode(array);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof ByteArrayWrapper) {
|
||||
return Arrays.equals(array, ((ByteArrayWrapper) obj).array);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array.
|
||||
*
|
||||
* @return Returns the array
|
||||
*/
|
||||
public byte[] getArray() {
|
||||
return array;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.connection.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Simple class containing various decoding utilities.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public abstract class DecodeUtils {
|
||||
|
||||
public static String decode(byte[] bytes) {
|
||||
return Base64.encodeToString(bytes, false);
|
||||
}
|
||||
|
||||
public static String[] decodeMultiple(byte[]... bytes) {
|
||||
String[] result = new String[bytes.length];
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
result[i] = decode(bytes[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static byte[] encode(String string) {
|
||||
return (string == null ? null : Base64.decode(string));
|
||||
}
|
||||
|
||||
public static Map<byte[], byte[]> encodeMap(Map<String, byte[]> map) {
|
||||
Map<byte[], byte[]> result = new LinkedHashMap<byte[], byte[]>(map.size());
|
||||
for (Map.Entry<String, byte[]> entry : map.entrySet()) {
|
||||
result.put(encode(entry.getKey()), entry.getValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Map<String, byte[]> decodeMap(Map<byte[], byte[]> tuple) {
|
||||
Map<String, byte[]> result = new LinkedHashMap<String, byte[]>(tuple.size());
|
||||
for (Map.Entry<byte[], byte[]> entry : tuple.entrySet()) {
|
||||
result.put(decode(entry.getKey()), entry.getValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Set<byte[]> convertToSet(Collection<String> keys) {
|
||||
Set<byte[]> set = new LinkedHashSet<byte[]>(keys.size());
|
||||
|
||||
for (String string : keys) {
|
||||
set.add(encode(string));
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
public static List<byte[]> convertToList(Collection<String> keys) {
|
||||
List<byte[]> set = new ArrayList<byte[]>(keys.size());
|
||||
|
||||
for (String string : keys) {
|
||||
set.add(encode(string));
|
||||
}
|
||||
return set;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Internal utility package for encoding/decoding Strings to byte[] (using Base64) library.
|
||||
*/
|
||||
package org.springframework.data.keyvalue.redis.connection.util;
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisConnection;
|
||||
import org.springframework.data.keyvalue.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.keyvalue.redis.serializer.SerializationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Internal base class used by various RedisTemplate XXXOperations implementations.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
abstract class AbstractOperations<K, V> {
|
||||
|
||||
// utility methods for the template internal methods
|
||||
abstract class ValueDeserializingRedisCallback implements RedisCallback<V> {
|
||||
private Object key;
|
||||
|
||||
public ValueDeserializingRedisCallback(Object key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final V doInRedis(RedisConnection connection) {
|
||||
byte[] result = inRedis(rawKey(key), connection);
|
||||
return deserializeValue(result);
|
||||
}
|
||||
|
||||
protected abstract byte[] inRedis(byte[] rawKey, RedisConnection connection);
|
||||
}
|
||||
|
||||
RedisSerializer keySerializer = null;
|
||||
RedisSerializer valueSerializer = null;
|
||||
RedisSerializer hashKeySerializer = null;
|
||||
RedisSerializer hashValueSerializer = null;
|
||||
RedisSerializer stringSerializer = null;
|
||||
RedisTemplate<K, V> template;
|
||||
|
||||
AbstractOperations(RedisTemplate<K, V> template) {
|
||||
keySerializer = template.getKeySerializer();
|
||||
valueSerializer = template.getValueSerializer();
|
||||
hashKeySerializer = template.getHashKeySerializer();
|
||||
hashValueSerializer = template.getHashValueSerializer();
|
||||
stringSerializer = template.getStringSerializer();
|
||||
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
|
||||
<T> T execute(RedisCallback<T> callback, boolean b) {
|
||||
return template.execute(callback, b);
|
||||
}
|
||||
|
||||
public RedisOperations<K, V> getOperations() {
|
||||
return template;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
byte[] rawKey(Object key) {
|
||||
Assert.notNull(key, "non null key required");
|
||||
return keySerializer.serialize(key);
|
||||
}
|
||||
|
||||
byte[] rawString(String key) {
|
||||
return stringSerializer.serialize(key);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
byte[] rawValue(Object value) {
|
||||
return valueSerializer.serialize(value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
<HK> byte[] rawHashKey(HK hashKey) {
|
||||
Assert.notNull(hashKey, "non null hash key required");
|
||||
return hashKeySerializer.serialize(hashKey);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
<HV> byte[] rawHashValue(HV value) {
|
||||
return hashValueSerializer.serialize(value);
|
||||
}
|
||||
|
||||
byte[][] rawKeys(K key, K otherKey) {
|
||||
final byte[][] rawKeys = new byte[2][];
|
||||
|
||||
|
||||
rawKeys[0] = rawKey(key);
|
||||
rawKeys[1] = rawKey(key);
|
||||
return rawKeys;
|
||||
}
|
||||
|
||||
byte[][] rawKeys(Collection<K> keys) {
|
||||
return rawKeys(null, keys);
|
||||
}
|
||||
|
||||
byte[][] rawKeys(K key, Collection<K> keys) {
|
||||
final byte[][] rawKeys = new byte[keys.size() + (key != null ? 1 : 0)][];
|
||||
|
||||
int i = 0;
|
||||
|
||||
if (key != null) {
|
||||
rawKeys[i++] = rawKey(key);
|
||||
}
|
||||
|
||||
for (K k : keys) {
|
||||
rawKeys[i++] = rawKey(k);
|
||||
}
|
||||
|
||||
return rawKeys;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<V> deserializeValues(Set<byte[]> rawValues) {
|
||||
return SerializationUtils.deserialize(rawValues, valueSerializer);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<V> deserializeValues(List<byte[]> rawValues) {
|
||||
return SerializationUtils.deserialize(rawValues, valueSerializer);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
<T> Set<T> deserializeHashKeys(Set<byte[]> rawKeys) {
|
||||
return SerializationUtils.deserialize(rawKeys, hashKeySerializer);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
<T> List<T> deserializeHashValues(List<byte[]> rawValues) {
|
||||
return SerializationUtils.deserialize(rawValues, hashValueSerializer);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
<HK, HV> Map<HK, HV> deserializeHashMap(Map<byte[], byte[]> entries) {
|
||||
// connection in pipeline/multi mode
|
||||
if (entries == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<HK, HV> map = new LinkedHashMap<HK, HV>(entries.size());
|
||||
|
||||
for (Map.Entry<byte[], byte[]> entry : entries.entrySet()) {
|
||||
map.put((HK) deserializeHashKey(entry.getKey()), (HV) deserializeHashValue(entry.getValue()));
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
K deserializeKey(byte[] value) {
|
||||
return (K) keySerializer.deserialize(value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
V deserializeValue(byte[] value) {
|
||||
return (V) valueSerializer.deserialize(value);
|
||||
}
|
||||
|
||||
String deserializeString(byte[] value) {
|
||||
return (String) stringSerializer.deserialize(value);
|
||||
}
|
||||
|
||||
@SuppressWarnings( { "unchecked" })
|
||||
<HK> HK deserializeHashKey(byte[] value) {
|
||||
return (HK) hashKeySerializer.deserialize(value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
<HV> HV deserializeHashValue(byte[] value) {
|
||||
return (HV) hashValueSerializer.deserialize(value);
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import java.util.Set;
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface BoundHashOperations<H, HK, HV> extends KeyBound<H> {
|
||||
public interface BoundHashOperations<H, HK, HV> extends BoundKeyOperations<H> {
|
||||
|
||||
RedisOperations<H, ?> getOperations();
|
||||
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.core;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.connection.DataType;
|
||||
|
||||
/**
|
||||
* Operations over a Redis key.
|
||||
*
|
||||
* Useful for executing common key-'bound' operations to all implementations.
|
||||
*
|
||||
* <p>As the rest of the APIs, if the underlying connection is pipelined or queued/in multi mode,
|
||||
* all methods will return null.
|
||||
* </p>
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface BoundKeyOperations<K> {
|
||||
|
||||
/**
|
||||
* Returns the key associated with this entity.
|
||||
*
|
||||
* @return key associated with the implementing entity
|
||||
*/
|
||||
K getKey();
|
||||
|
||||
/**
|
||||
* Returns the associated Redis type.
|
||||
*
|
||||
* @return key type
|
||||
*/
|
||||
DataType getType();
|
||||
|
||||
/**
|
||||
* Returns the expiration of this key.
|
||||
*
|
||||
* @return expiration value (in seconds)
|
||||
*/
|
||||
Long getExpire();
|
||||
|
||||
/**
|
||||
* Sets the key time-to-live/expiration.
|
||||
*
|
||||
* @param timeout expiration value
|
||||
* @param unit expiration unit
|
||||
* @return true if expiration was set, false otherwise
|
||||
*/
|
||||
Boolean expire(long timeout, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Sets the key time-to-live/expiration.
|
||||
*
|
||||
* @param date expiration date
|
||||
* @return true if expiration was set, false otherwise
|
||||
*/
|
||||
Boolean expireAt(Date date);
|
||||
|
||||
/**
|
||||
* Removes the expiration (if any) of the key.
|
||||
* @return true if expiration was removed, false otherwise
|
||||
*/
|
||||
Boolean persist();
|
||||
|
||||
/**
|
||||
* Renames the key.
|
||||
*
|
||||
* @param newKey new key
|
||||
*/
|
||||
void rename(K newKey);
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import java.util.concurrent.TimeUnit;
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface BoundListOperations<K, V> extends KeyBound<K> {
|
||||
public interface BoundListOperations<K, V> extends BoundKeyOperations<K> {
|
||||
|
||||
RedisOperations<K, V> getOperations();
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.util.Set;
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface BoundSetOperations<K, V> extends KeyBound<K> {
|
||||
public interface BoundSetOperations<K, V> extends BoundKeyOperations<K> {
|
||||
|
||||
RedisOperations<K, V> getOperations();
|
||||
|
||||
|
||||
@@ -22,27 +22,27 @@ import java.util.concurrent.TimeUnit;
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface BoundValueOperations<K, V> extends KeyBound<K> {
|
||||
public interface BoundValueOperations<K, V> extends BoundKeyOperations<K> {
|
||||
|
||||
RedisOperations<K, V> getOperations();
|
||||
|
||||
void set(V value);
|
||||
|
||||
void set(V value, long offset);
|
||||
|
||||
void set(V value, long timeout, TimeUnit unit);
|
||||
|
||||
Boolean setIfAbsent(V value);
|
||||
|
||||
V get();
|
||||
|
||||
String get(long start, long end);
|
||||
|
||||
V getAndSet(V value);
|
||||
|
||||
Long increment(long delta);
|
||||
|
||||
Integer append(String value);
|
||||
|
||||
String get(int start, int end);
|
||||
|
||||
void set(int start, int end);
|
||||
|
||||
Long size();
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import java.util.Set;
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface BoundZSetOperations<K, V> extends KeyBound<K> {
|
||||
public interface BoundZSetOperations<K, V> extends BoundKeyOperations<K> {
|
||||
|
||||
RedisOperations<K, V> getOperations();
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.core;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisConnection;
|
||||
|
||||
/**
|
||||
* Invocation handler that suppresses close calls on {@link RedisConnection}.
|
||||
* @see RedisConnection#close()
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class CloseSuppressingInvocationHandler implements InvocationHandler {
|
||||
|
||||
private static final String CLOSE = "close";
|
||||
private static final String HASH_CODE = "hashCode";
|
||||
private static final String EQUALS = "equals";
|
||||
|
||||
private final RedisConnection target;
|
||||
|
||||
public CloseSuppressingInvocationHandler(RedisConnection target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
|
||||
if (method.getName().equals(EQUALS)) {
|
||||
// Only consider equal when proxies are identical.
|
||||
return (proxy == args[0]);
|
||||
}
|
||||
else if (method.getName().equals(HASH_CODE)) {
|
||||
// Use hashCode of PersistenceManager proxy.
|
||||
return System.identityHashCode(proxy);
|
||||
}
|
||||
else if (method.getName().equals(CLOSE)) {
|
||||
// Handle close method: suppress, not valid.
|
||||
return null;
|
||||
}
|
||||
|
||||
// Invoke method on target RedisConnection.
|
||||
try {
|
||||
Object retVal = method.invoke(this.target, args);
|
||||
return retVal;
|
||||
} catch (InvocationTargetException ex) {
|
||||
throw ex.getTargetException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,12 +19,14 @@ import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.connection.DataType;
|
||||
|
||||
/**
|
||||
* Default implementation for {@link HashOperations}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class DefaultBoundHashOperations<H, HK, HV> extends DefaultKeyBound<H> implements BoundHashOperations<H, HK, HV> {
|
||||
class DefaultBoundHashOperations<H, HK, HV> extends DefaultBoundKeyOperations<H> implements BoundHashOperations<H, HK, HV> {
|
||||
|
||||
private final HashOperations<H, HK, HV> ops;
|
||||
|
||||
@@ -35,7 +37,7 @@ class DefaultBoundHashOperations<H, HK, HV> extends DefaultKeyBound<H> implement
|
||||
* @param template
|
||||
*/
|
||||
public DefaultBoundHashOperations(H key, RedisOperations<H, ?> operations) {
|
||||
super(key);
|
||||
super(key, operations);
|
||||
this.ops = operations.opsForHash();
|
||||
}
|
||||
|
||||
@@ -103,4 +105,9 @@ class DefaultBoundHashOperations<H, HK, HV> extends DefaultKeyBound<H> implement
|
||||
public Map<HK, HV> entries() {
|
||||
return ops.entries(getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getType() {
|
||||
return DataType.HASH;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.core;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
||||
/**
|
||||
* Default {@link BoundKeyOperations} implementation.
|
||||
* Meant for internal usage.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
abstract class DefaultBoundKeyOperations<K> implements BoundKeyOperations<K> {
|
||||
|
||||
private K key;
|
||||
private final RedisOperations<K, ?> ops;
|
||||
|
||||
public DefaultBoundKeyOperations(K key, RedisOperations<K, ?> operations) {
|
||||
setKey(key);
|
||||
this.ops = operations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
protected void setKey(K key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean expire(long timeout, TimeUnit unit) {
|
||||
return ops.expire(key, timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean expireAt(Date date) {
|
||||
return ops.expireAt(key, date);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getExpire() {
|
||||
return ops.getExpire(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean persist() {
|
||||
return ops.persist(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rename(K newKey) {
|
||||
ops.rename(key, newKey);
|
||||
key = newKey;
|
||||
}
|
||||
}
|
||||
@@ -18,13 +18,15 @@ package org.springframework.data.keyvalue.redis.core;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.connection.DataType;
|
||||
|
||||
|
||||
/**
|
||||
* Default implementation for {@link BoundListOperations}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class DefaultBoundListOperations<K, V> extends DefaultKeyBound<K> implements BoundListOperations<K, V> {
|
||||
class DefaultBoundListOperations<K, V> extends DefaultBoundKeyOperations<K> implements BoundListOperations<K, V> {
|
||||
|
||||
private final ListOperations<K, V> ops;
|
||||
|
||||
@@ -35,7 +37,7 @@ class DefaultBoundListOperations<K, V> extends DefaultKeyBound<K> implements Bou
|
||||
* @param operations
|
||||
*/
|
||||
public DefaultBoundListOperations(K key, RedisOperations<K, V> operations) {
|
||||
super(key);
|
||||
super(key, operations);
|
||||
this.ops = operations.opsForList();
|
||||
}
|
||||
|
||||
@@ -124,4 +126,9 @@ class DefaultBoundListOperations<K, V> extends DefaultKeyBound<K> implements Bou
|
||||
public void set(long index, V value) {
|
||||
ops.set(getKey(), index, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getType() {
|
||||
return DataType.LIST;
|
||||
}
|
||||
}
|
||||
@@ -19,12 +19,14 @@ package org.springframework.data.keyvalue.redis.core;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.connection.DataType;
|
||||
|
||||
/**
|
||||
* Default implementation for {@link BoundSetOperations}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class DefaultBoundSetOperations<K, V> extends DefaultKeyBound<K> implements BoundSetOperations<K, V> {
|
||||
class DefaultBoundSetOperations<K, V> extends DefaultBoundKeyOperations<K> implements BoundSetOperations<K, V> {
|
||||
|
||||
private final SetOperations<K, V> ops;
|
||||
|
||||
@@ -36,7 +38,7 @@ class DefaultBoundSetOperations<K, V> extends DefaultKeyBound<K> implements Boun
|
||||
* @param operations
|
||||
*/
|
||||
DefaultBoundSetOperations(K key, RedisOperations<K, V> operations) {
|
||||
super(key);
|
||||
super(key, operations);
|
||||
this.ops = operations.opsForSet();
|
||||
}
|
||||
|
||||
@@ -146,4 +148,9 @@ class DefaultBoundSetOperations<K, V> extends DefaultKeyBound<K> implements Boun
|
||||
public void unionAndStore(Collection<K> keys, K destKey) {
|
||||
ops.unionAndStore(getKey(), keys, destKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getType() {
|
||||
return DataType.SET;
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,12 @@ package org.springframework.data.keyvalue.redis.core;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.connection.DataType;
|
||||
|
||||
/**
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class DefaultBoundValueOperations<K, V> extends DefaultKeyBound<K> implements BoundValueOperations<K, V> {
|
||||
class DefaultBoundValueOperations<K, V> extends DefaultBoundKeyOperations<K> implements BoundValueOperations<K, V> {
|
||||
|
||||
private final ValueOperations<K, V> ops;
|
||||
|
||||
@@ -31,7 +33,7 @@ class DefaultBoundValueOperations<K, V> extends DefaultKeyBound<K> implements Bo
|
||||
* @param operations
|
||||
*/
|
||||
public DefaultBoundValueOperations(K key, RedisOperations<K, V> operations) {
|
||||
super(key);
|
||||
super(key, operations);
|
||||
this.ops = operations.opsForValue();
|
||||
}
|
||||
|
||||
@@ -56,7 +58,7 @@ class DefaultBoundValueOperations<K, V> extends DefaultKeyBound<K> implements Bo
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(int start, int end) {
|
||||
public String get(long start, long end) {
|
||||
return ops.get(getKey(), start, end);
|
||||
}
|
||||
|
||||
@@ -76,8 +78,8 @@ class DefaultBoundValueOperations<K, V> extends DefaultKeyBound<K> implements Bo
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(int start, int end) {
|
||||
ops.set(getKey(), start, end);
|
||||
public void set(V value, long offset) {
|
||||
ops.set(getKey(), value, offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -89,4 +91,9 @@ class DefaultBoundValueOperations<K, V> extends DefaultKeyBound<K> implements Bo
|
||||
public RedisOperations<K, V> getOperations() {
|
||||
return ops.getOperations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getType() {
|
||||
return DataType.STRING;
|
||||
}
|
||||
}
|
||||
@@ -19,12 +19,14 @@ package org.springframework.data.keyvalue.redis.core;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.connection.DataType;
|
||||
|
||||
/**
|
||||
* Default implementation for {@link BoundZSetOperations}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class DefaultBoundZSetOperations<K, V> extends DefaultKeyBound<K> implements BoundZSetOperations<K, V> {
|
||||
class DefaultBoundZSetOperations<K, V> extends DefaultBoundKeyOperations<K> implements BoundZSetOperations<K, V> {
|
||||
|
||||
private final ZSetOperations<K, V> ops;
|
||||
|
||||
@@ -34,9 +36,9 @@ class DefaultBoundZSetOperations<K, V> extends DefaultKeyBound<K> implements Bou
|
||||
* @param key
|
||||
* @param oeprations
|
||||
*/
|
||||
public DefaultBoundZSetOperations(K key, RedisOperations<K, V> oeprations) {
|
||||
super(key);
|
||||
this.ops = oeprations.opsForZSet();
|
||||
public DefaultBoundZSetOperations(K key, RedisOperations<K, V> operations) {
|
||||
super(key, operations);
|
||||
this.ops = operations.opsForZSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -128,4 +130,9 @@ class DefaultBoundZSetOperations<K, V> extends DefaultKeyBound<K> implements Bou
|
||||
public void unionAndStore(Collection<K> otherKeys, K destKey) {
|
||||
ops.unionAndStore(getKey(), otherKeys, destKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getType() {
|
||||
return DataType.ZSET;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisConnection;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link HashOperations}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class DefaultHashOperations<K, HK, HV> extends AbstractOperations<K, Object> implements HashOperations<K, HK, HV> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
DefaultHashOperations(RedisTemplate<K, ?> template) {
|
||||
super((RedisTemplate<K, Object>) template);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public HV get(K key, Object hashKey) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawHashKey = rawHashKey(hashKey);
|
||||
|
||||
byte[] rawHashValue = execute(new RedisCallback<byte[]>() {
|
||||
@Override
|
||||
public byte[] doInRedis(RedisConnection connection) {
|
||||
return connection.hGet(rawKey, rawHashKey);
|
||||
}
|
||||
}, true);
|
||||
|
||||
return (HV) deserializeHashValue(rawHashValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean hasKey(K key, Object hashKey) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawHashKey = rawHashKey(hashKey);
|
||||
|
||||
return execute(new RedisCallback<Boolean>() {
|
||||
@Override
|
||||
public Boolean doInRedis(RedisConnection connection) {
|
||||
return connection.hExists(rawKey, rawHashKey);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long increment(K key, HK hashKey, final long delta) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawHashKey = rawHashKey(hashKey);
|
||||
|
||||
return execute(new RedisCallback<Long>() {
|
||||
@Override
|
||||
public Long doInRedis(RedisConnection connection) {
|
||||
return connection.hIncrBy(rawKey, rawHashKey, delta);
|
||||
}
|
||||
}, true);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<HK> keys(K key) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
|
||||
Set<byte[]> rawValues = execute(new RedisCallback<Set<byte[]>>() {
|
||||
@Override
|
||||
public Set<byte[]> doInRedis(RedisConnection connection) {
|
||||
return connection.hKeys(rawKey);
|
||||
}
|
||||
}, true);
|
||||
|
||||
return deserializeHashKeys(rawValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long size(K key) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
|
||||
return execute(new RedisCallback<Long>() {
|
||||
@Override
|
||||
public Long doInRedis(RedisConnection connection) {
|
||||
return connection.hLen(rawKey);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(K key, Map<? extends HK, ? extends HV> m) {
|
||||
if (m.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final byte[] rawKey = rawKey(key);
|
||||
|
||||
final Map<byte[], byte[]> hashes = new LinkedHashMap<byte[], byte[]>(m.size());
|
||||
|
||||
for (Map.Entry<? extends HK, ? extends HV> entry : m.entrySet()) {
|
||||
hashes.put(rawHashKey(entry.getKey()), rawHashValue(entry.getValue()));
|
||||
}
|
||||
|
||||
execute(new RedisCallback<Object>() {
|
||||
@Override
|
||||
public Object doInRedis(RedisConnection connection) {
|
||||
connection.hMSet(rawKey, hashes);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Collection<HV> multiGet(K key, Collection<HK> fields) {
|
||||
if (fields.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final byte[] rawKey = rawKey(key);
|
||||
|
||||
final byte[][] rawHashKeys = new byte[fields.size()][];
|
||||
|
||||
int counter = 0;
|
||||
for (HK hashKey : fields) {
|
||||
rawHashKeys[counter++] = rawHashKey(hashKey);
|
||||
}
|
||||
|
||||
List<byte[]> rawValues = execute(new RedisCallback<List<byte[]>>() {
|
||||
@Override
|
||||
public List<byte[]> doInRedis(RedisConnection connection) {
|
||||
return connection.hMGet(rawKey, rawHashKeys);
|
||||
}
|
||||
}, true);
|
||||
|
||||
return deserializeHashValues(rawValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(K key, HK hashKey, HV value) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawHashKey = rawHashKey(hashKey);
|
||||
final byte[] rawHashValue = rawHashValue(value);
|
||||
|
||||
execute(new RedisCallback<Object>() {
|
||||
@Override
|
||||
public Object doInRedis(RedisConnection connection) {
|
||||
connection.hSet(rawKey, rawHashKey, rawHashValue);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean putIfAbsent(K key, HK hashKey, HV value) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawHashKey = rawHashKey(hashKey);
|
||||
final byte[] rawHashValue = rawHashValue(value);
|
||||
|
||||
return execute(new RedisCallback<Boolean>() {
|
||||
@Override
|
||||
public Boolean doInRedis(RedisConnection connection) {
|
||||
return connection.hSetNX(rawKey, rawHashKey, rawHashValue);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<HV> values(K key) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
|
||||
List<byte[]> rawValues = execute(new RedisCallback<List<byte[]>>() {
|
||||
@Override
|
||||
public List<byte[]> doInRedis(RedisConnection connection) {
|
||||
return connection.hVals(rawKey);
|
||||
}
|
||||
}, true);
|
||||
|
||||
return deserializeHashValues(rawValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(K key, Object hashKey) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawHashKey = rawHashKey(hashKey);
|
||||
|
||||
execute(new RedisCallback<Object>() {
|
||||
@Override
|
||||
public Object doInRedis(RedisConnection connection) {
|
||||
connection.hDel(rawKey, rawHashKey);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<HK, HV> entries(K key) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
|
||||
Map<byte[], byte[]> entries = execute(new RedisCallback<Map<byte[], byte[]>>() {
|
||||
@Override
|
||||
public Map<byte[], byte[]> doInRedis(RedisConnection connection) {
|
||||
return connection.hGetAll(rawKey);
|
||||
}
|
||||
}, true);
|
||||
|
||||
return deserializeHashMap(entries);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.core;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisConnection;
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisListCommands.Position;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ListOperations}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class DefaultListOperations<K, V> extends AbstractOperations<K, V> implements ListOperations<K, V> {
|
||||
|
||||
DefaultListOperations(RedisTemplate<K, V> template) {
|
||||
super(template);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V index(K key, final long index) {
|
||||
return execute(new ValueDeserializingRedisCallback(key) {
|
||||
@Override
|
||||
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
|
||||
return connection.lIndex(rawKey, index);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V leftPop(K key) {
|
||||
return execute(new ValueDeserializingRedisCallback(key) {
|
||||
@Override
|
||||
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
|
||||
return connection.lPop(rawKey);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V leftPop(K key, long timeout, TimeUnit unit) {
|
||||
final int tm = (int) unit.toSeconds(timeout);
|
||||
|
||||
return execute(new ValueDeserializingRedisCallback(key) {
|
||||
@Override
|
||||
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
|
||||
return connection.bLPop(tm, rawKey).get(0);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long leftPush(K key, V value) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawValue = rawValue(value);
|
||||
return execute(new RedisCallback<Long>() {
|
||||
@Override
|
||||
public Long doInRedis(RedisConnection connection) {
|
||||
return connection.lPush(rawKey, rawValue);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long leftPushIfPresent(K key, V value) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawValue = rawValue(value);
|
||||
return execute(new RedisCallback<Long>() {
|
||||
@Override
|
||||
public Long doInRedis(RedisConnection connection) {
|
||||
return connection.lPushX(rawKey, rawValue);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long leftPush(K key, V pivot, V value) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawPivot = rawValue(pivot);
|
||||
final byte[] rawValue = rawValue(value);
|
||||
return execute(new RedisCallback<Long>() {
|
||||
@Override
|
||||
public Long doInRedis(RedisConnection connection) {
|
||||
return connection.lInsert(rawKey, Position.BEFORE, rawPivot, rawValue);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long size(K key) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
return execute(new RedisCallback<Long>() {
|
||||
@Override
|
||||
public Long doInRedis(RedisConnection connection) {
|
||||
return connection.lLen(rawKey);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<V> range(K key, final long start, final long end) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
return execute(new RedisCallback<List<V>>() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public List<V> doInRedis(RedisConnection connection) {
|
||||
return deserializeValues(connection.lRange(rawKey, start, end));
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long remove(K key, final long count, Object value) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawValue = rawValue(value);
|
||||
return execute(new RedisCallback<Long>() {
|
||||
@Override
|
||||
public Long doInRedis(RedisConnection connection) {
|
||||
return connection.lRem(rawKey, count, rawValue);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V rightPop(K key) {
|
||||
return execute(new ValueDeserializingRedisCallback(key) {
|
||||
@Override
|
||||
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
|
||||
return connection.rPop(rawKey);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V rightPop(K key, long timeout, TimeUnit unit) {
|
||||
final int tm = (int) unit.toSeconds(timeout);
|
||||
|
||||
return execute(new ValueDeserializingRedisCallback(key) {
|
||||
@Override
|
||||
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
|
||||
return connection.bRPop(tm, rawKey).get(0);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long rightPush(K key, V value) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawValue = rawValue(value);
|
||||
return execute(new RedisCallback<Long>() {
|
||||
@Override
|
||||
public Long doInRedis(RedisConnection connection) {
|
||||
return connection.rPush(rawKey, rawValue);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long rightPushIfPresent(K key, V value) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawValue = rawValue(value);
|
||||
return execute(new RedisCallback<Long>() {
|
||||
@Override
|
||||
public Long doInRedis(RedisConnection connection) {
|
||||
return connection.rPushX(rawKey, rawValue);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long rightPush(K key, V pivot, V value) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawPivot = rawValue(pivot);
|
||||
final byte[] rawValue = rawValue(value);
|
||||
|
||||
return execute(new RedisCallback<Long>() {
|
||||
@Override
|
||||
public Long doInRedis(RedisConnection connection) {
|
||||
return connection.lInsert(rawKey, Position.AFTER, rawPivot, rawValue);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V rightPopAndLeftPush(K sourceKey, K destinationKey) {
|
||||
final byte[] rawDestKey = rawKey(destinationKey);
|
||||
|
||||
return execute(new ValueDeserializingRedisCallback(sourceKey) {
|
||||
@Override
|
||||
protected byte[] inRedis(byte[] rawSourceKey, RedisConnection connection) {
|
||||
return connection.rPopLPush(rawSourceKey, rawDestKey);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V rightPopAndLeftPush(K sourceKey, K destinationKey, long timeout, TimeUnit unit) {
|
||||
final int tm = (int) unit.toSeconds(timeout);
|
||||
final byte[] rawDestKey = rawKey(destinationKey);
|
||||
|
||||
return execute(new ValueDeserializingRedisCallback(sourceKey) {
|
||||
@Override
|
||||
protected byte[] inRedis(byte[] rawSourceKey, RedisConnection connection) {
|
||||
return connection.bRPopLPush(tm, rawSourceKey, rawDestKey);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(K key, final long index, V value) {
|
||||
final byte[] rawValue = rawValue(value);
|
||||
execute(new ValueDeserializingRedisCallback(key) {
|
||||
@Override
|
||||
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
|
||||
connection.lSet(rawKey, index, rawValue);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trim(K key, final long start, final long end) {
|
||||
execute(new ValueDeserializingRedisCallback(key) {
|
||||
@Override
|
||||
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
|
||||
connection.lTrim(rawKey, start, end);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisConnection;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link SetOperations}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class DefaultSetOperations<K, V> extends AbstractOperations<K, V> implements SetOperations<K, V> {
|
||||
|
||||
public DefaultSetOperations(RedisTemplate<K, V> template) {
|
||||
super(template);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean add(K key, V value) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawValue = rawValue(value);
|
||||
return execute(new RedisCallback<Boolean>() {
|
||||
@Override
|
||||
public Boolean doInRedis(RedisConnection connection) {
|
||||
return connection.sAdd(rawKey, rawValue);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> difference(K key, K otherKey) {
|
||||
return difference(key, Collections.singleton(otherKey));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Set<V> difference(final K key, final Collection<K> otherKeys) {
|
||||
final byte[][] rawKeys = rawKeys(key, otherKeys);
|
||||
Set<byte[]> rawValues = execute(new RedisCallback<Set<byte[]>>() {
|
||||
@Override
|
||||
public Set<byte[]> doInRedis(RedisConnection connection) {
|
||||
return connection.sDiff(rawKeys);
|
||||
}
|
||||
}, true);
|
||||
|
||||
return deserializeValues(rawValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void differenceAndStore(K key, K otherKey, K destKey) {
|
||||
differenceAndStore(key, Collections.singleton(otherKey), destKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void differenceAndStore(final K key, final Collection<K> otherKeys, K destKey) {
|
||||
final byte[][] rawKeys = rawKeys(key, otherKeys);
|
||||
final byte[] rawDestKey = rawKey(destKey);
|
||||
execute(new RedisCallback<Object>() {
|
||||
@Override
|
||||
public Object doInRedis(RedisConnection connection) {
|
||||
connection.sDiffStore(rawDestKey, rawKeys);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> intersect(K key, K otherKey) {
|
||||
return intersect(key, Collections.singleton(otherKey));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Set<V> intersect(K key, Collection<K> otherKeys) {
|
||||
final byte[][] rawKeys = rawKeys(key, otherKeys);
|
||||
Set<byte[]> rawValues = execute(new RedisCallback<Set<byte[]>>() {
|
||||
@Override
|
||||
public Set<byte[]> doInRedis(RedisConnection connection) {
|
||||
return connection.sInter(rawKeys);
|
||||
}
|
||||
}, true);
|
||||
|
||||
return deserializeValues(rawValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void intersectAndStore(K key, K otherKey, K destKey) {
|
||||
intersectAndStore(key, Collections.singleton(otherKey), destKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void intersectAndStore(K key, Collection<K> otherKeys, K destKey) {
|
||||
final byte[][] rawKeys = rawKeys(key, otherKeys);
|
||||
final byte[] rawDestKey = rawKey(destKey);
|
||||
execute(new RedisCallback<Object>() {
|
||||
@Override
|
||||
public Object doInRedis(RedisConnection connection) {
|
||||
connection.sInterStore(rawDestKey, rawKeys);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isMember(K key, Object o) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawValue = rawValue(o);
|
||||
return execute(new RedisCallback<Boolean>() {
|
||||
@Override
|
||||
public Boolean doInRedis(RedisConnection connection) {
|
||||
return connection.sIsMember(rawKey, rawValue);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Set<V> members(K key) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
Set<byte[]> rawValues = execute(new RedisCallback<Set<byte[]>>() {
|
||||
@Override
|
||||
public Set<byte[]> doInRedis(RedisConnection connection) {
|
||||
return connection.sMembers(rawKey);
|
||||
}
|
||||
}, true);
|
||||
|
||||
return deserializeValues(rawValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean move(K key, V value, K destKey) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawDestKey = rawKey(destKey);
|
||||
final byte[] rawValue = rawValue(value);
|
||||
|
||||
return execute(new RedisCallback<Boolean>() {
|
||||
@Override
|
||||
public Boolean doInRedis(RedisConnection connection) {
|
||||
return connection.sMove(rawKey, rawDestKey, rawValue);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V randomMember(K key) {
|
||||
|
||||
return execute(new ValueDeserializingRedisCallback(key) {
|
||||
@Override
|
||||
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
|
||||
return connection.randomKey();
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean remove(K key, Object o) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawValue = rawValue(o);
|
||||
return execute(new RedisCallback<Boolean>() {
|
||||
@Override
|
||||
public Boolean doInRedis(RedisConnection connection) {
|
||||
return connection.sRem(rawKey, rawValue);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V pop(K key) {
|
||||
return execute(new ValueDeserializingRedisCallback(key) {
|
||||
@Override
|
||||
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
|
||||
return connection.sPop(rawKey);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long size(K key) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
return execute(new RedisCallback<Long>() {
|
||||
@Override
|
||||
public Long doInRedis(RedisConnection connection) {
|
||||
return connection.sCard(rawKey);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> union(K key, K otherKey) {
|
||||
return union(key, Collections.singleton(otherKey));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Set<V> union(K key, Collection<K> otherKeys) {
|
||||
final byte[][] rawKeys = rawKeys(key, otherKeys);
|
||||
Set<byte[]> rawValues = execute(new RedisCallback<Set<byte[]>>() {
|
||||
@Override
|
||||
public Set<byte[]> doInRedis(RedisConnection connection) {
|
||||
return connection.sUnion(rawKeys);
|
||||
}
|
||||
}, true);
|
||||
|
||||
return deserializeValues(rawValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unionAndStore(K key, K otherKey, K destKey) {
|
||||
unionAndStore(key, Collections.singleton(otherKey), destKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unionAndStore(K key, Collection<K> otherKeys, K destKey) {
|
||||
final byte[][] rawKeys = rawKeys(key, otherKeys);
|
||||
final byte[] rawDestKey = rawKey(destKey);
|
||||
execute(new RedisCallback<Object>() {
|
||||
@Override
|
||||
public Object doInRedis(RedisConnection connection) {
|
||||
connection.sUnionStore(rawDestKey, rawKeys);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisConnection;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ValueOperations}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class DefaultValueOperations<K, V> extends AbstractOperations<K, V> implements ValueOperations<K, V> {
|
||||
|
||||
DefaultValueOperations(RedisTemplate<K, V> template) {
|
||||
super(template);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(final Object key) {
|
||||
|
||||
return execute(new ValueDeserializingRedisCallback(key) {
|
||||
@Override
|
||||
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
|
||||
return connection.get(rawKey);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getAndSet(K key, V newValue) {
|
||||
final byte[] rawValue = rawValue(newValue);
|
||||
return execute(new ValueDeserializingRedisCallback(key) {
|
||||
@Override
|
||||
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
|
||||
return connection.getSet(rawKey, rawValue);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long increment(K key, final long delta) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
// TODO add conversion service in here ?
|
||||
return execute(new RedisCallback<Long>() {
|
||||
@Override
|
||||
public Long doInRedis(RedisConnection connection) {
|
||||
if (delta == 1) {
|
||||
return connection.incr(rawKey);
|
||||
}
|
||||
|
||||
if (delta == -1) {
|
||||
return connection.decr(rawKey);
|
||||
}
|
||||
|
||||
if (delta < 0) {
|
||||
return connection.decrBy(rawKey, delta);
|
||||
}
|
||||
|
||||
return connection.incrBy(rawKey, delta);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer append(K key, String value) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawString = rawString(value);
|
||||
|
||||
return execute(new RedisCallback<Integer>() {
|
||||
@Override
|
||||
public Integer doInRedis(RedisConnection connection) {
|
||||
return connection.append(rawKey, rawString).intValue();
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(K key, final long start, final long end) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
|
||||
byte[] rawReturn = execute(new RedisCallback<byte[]>() {
|
||||
@Override
|
||||
public byte[] doInRedis(RedisConnection connection) {
|
||||
return connection.getRange(rawKey, start, end);
|
||||
}
|
||||
}, true);
|
||||
|
||||
return deserializeString(rawReturn);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public List<V> multiGet(Collection<K> keys) {
|
||||
if (keys.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final byte[][] rawKeys = new byte[keys.size()][];
|
||||
|
||||
int counter = 0;
|
||||
for (K hashKey : keys) {
|
||||
rawKeys[counter++] = rawKey(hashKey);
|
||||
}
|
||||
|
||||
List<byte[]> rawValues = execute(new RedisCallback<List<byte[]>>() {
|
||||
@Override
|
||||
public List<byte[]> doInRedis(RedisConnection connection) {
|
||||
return connection.mGet(rawKeys);
|
||||
}
|
||||
}, true);
|
||||
|
||||
return deserializeValues(rawValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void multiSet(Map<? extends K, ? extends V> m) {
|
||||
if (m.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Map<byte[], byte[]> rawKeys = new LinkedHashMap<byte[], byte[]>(m.size());
|
||||
|
||||
for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {
|
||||
rawKeys.put(rawKey(entry.getKey()), rawValue(entry.getValue()));
|
||||
}
|
||||
|
||||
execute(new RedisCallback<Object>() {
|
||||
@Override
|
||||
public Object doInRedis(RedisConnection connection) {
|
||||
connection.mSet(rawKeys);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void multiSetIfAbsent(Map<? extends K, ? extends V> m) {
|
||||
if (m.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Map<byte[], byte[]> rawKeys = new LinkedHashMap<byte[], byte[]>(m.size());
|
||||
|
||||
for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {
|
||||
rawKeys.put(rawKey(entry.getKey()), rawValue(entry.getValue()));
|
||||
}
|
||||
|
||||
execute(new RedisCallback<Object>() {
|
||||
@Override
|
||||
public Object doInRedis(RedisConnection connection) {
|
||||
connection.mSetNX(rawKeys);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(K key, V value) {
|
||||
final byte[] rawValue = rawValue(value);
|
||||
execute(new ValueDeserializingRedisCallback(key) {
|
||||
@Override
|
||||
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
|
||||
connection.set(rawKey, rawValue);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(K key, V value, long timeout, TimeUnit unit) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawValue = rawValue(value);
|
||||
final long rawTimeout = unit.toSeconds(timeout);
|
||||
|
||||
execute(new RedisCallback<Object>() {
|
||||
@Override
|
||||
public Object doInRedis(RedisConnection connection) throws DataAccessException {
|
||||
connection.setEx(rawKey, (int) rawTimeout, rawValue);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean setIfAbsent(K key, V value) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawValue = rawValue(value);
|
||||
|
||||
return execute(new RedisCallback<Boolean>() {
|
||||
@Override
|
||||
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
|
||||
return connection.setNX(rawKey, rawValue);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void set(K key, final V value, final long offset) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawValue = rawValue(value);
|
||||
|
||||
execute(new RedisCallback<Object>() {
|
||||
@Override
|
||||
public Object doInRedis(RedisConnection connection) {
|
||||
connection.setRange(rawKey, rawValue, offset);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long size(K key) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
|
||||
return execute(new RedisCallback<Long>() {
|
||||
@Override
|
||||
public Long doInRedis(RedisConnection connection) {
|
||||
return connection.strLen(rawKey);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisConnection;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ZSetOperations}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class DefaultZSetOperations<K, V> extends AbstractOperations<K, V> implements ZSetOperations<K, V> {
|
||||
|
||||
DefaultZSetOperations(RedisTemplate<K, V> template) {
|
||||
super(template);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean add(final K key, final V value, final double score) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawValue = rawValue(value);
|
||||
|
||||
return execute(new RedisCallback<Boolean>() {
|
||||
@Override
|
||||
public Boolean doInRedis(RedisConnection connection) {
|
||||
return connection.zAdd(rawKey, score, rawValue);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double incrementScore(K key, V value, final double delta) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawValue = rawValue(value);
|
||||
|
||||
return execute(new RedisCallback<Double>() {
|
||||
@Override
|
||||
public Double doInRedis(RedisConnection connection) {
|
||||
return connection.zIncrBy(rawKey, delta, rawValue);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void intersectAndStore(K key, K otherKey, K destKey) {
|
||||
intersectAndStore(key, Collections.singleton(otherKey), destKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void intersectAndStore(K key, Collection<K> otherKeys, K destKey) {
|
||||
final byte[][] rawKeys = rawKeys(key, otherKeys);
|
||||
final byte[] rawDestKey = rawKey(destKey);
|
||||
execute(new RedisCallback<Object>() {
|
||||
@Override
|
||||
public Object doInRedis(RedisConnection connection) {
|
||||
connection.zInterStore(rawDestKey, rawKeys);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Set<V> range(K key, final long start, final long end) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
|
||||
Set<byte[]> rawValues = execute(new RedisCallback<Set<byte[]>>() {
|
||||
@Override
|
||||
public Set<byte[]> doInRedis(RedisConnection connection) {
|
||||
return connection.zRange(rawKey, start, end);
|
||||
}
|
||||
}, true);
|
||||
|
||||
return deserializeValues(rawValues);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Set<V> rangeByScore(K key, final double min, final double max) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
|
||||
Set<byte[]> rawValues = execute(new RedisCallback<Set<byte[]>>() {
|
||||
@Override
|
||||
public Set<byte[]> doInRedis(RedisConnection connection) {
|
||||
return connection.zRangeByScore(rawKey, min, max);
|
||||
}
|
||||
}, true);
|
||||
|
||||
return deserializeValues(rawValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long rank(K key, Object o) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawValue = rawValue(o);
|
||||
|
||||
return execute(new RedisCallback<Long>() {
|
||||
@Override
|
||||
public Long doInRedis(RedisConnection connection) {
|
||||
Long zRank = connection.zRank(rawKey, rawValue);
|
||||
return (zRank != null && zRank.longValue() >= 0 ? zRank : null);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long reverseRank(K key, Object o) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawValue = rawValue(o);
|
||||
|
||||
return execute(new RedisCallback<Long>() {
|
||||
@Override
|
||||
public Long doInRedis(RedisConnection connection) {
|
||||
Long zRank = connection.zRevRank(rawKey, rawValue);
|
||||
return (zRank != null && zRank.longValue() >= 0 ? zRank : null);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean remove(K key, Object o) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawValue = rawValue(o);
|
||||
|
||||
return execute(new RedisCallback<Boolean>() {
|
||||
@Override
|
||||
public Boolean doInRedis(RedisConnection connection) {
|
||||
return connection.zRem(rawKey, rawValue);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRange(K key, final long start, final long end) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
execute(new RedisCallback<Object>() {
|
||||
@Override
|
||||
public Object doInRedis(RedisConnection connection) {
|
||||
connection.zRemRange(rawKey, start, end);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRangeByScore(K key, final double min, final double max) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
execute(new RedisCallback<Object>() {
|
||||
@Override
|
||||
public Object doInRedis(RedisConnection connection) {
|
||||
connection.zRemRangeByScore(rawKey, min, max);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Set<V> reverseRange(K key, final long start, final long end) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
|
||||
Set<byte[]> rawValues = execute(new RedisCallback<Set<byte[]>>() {
|
||||
@Override
|
||||
public Set<byte[]> doInRedis(RedisConnection connection) {
|
||||
return connection.zRevRange(rawKey, start, end);
|
||||
}
|
||||
}, true);
|
||||
|
||||
return deserializeValues(rawValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double score(K key, Object o) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final byte[] rawValue = rawValue(o);
|
||||
|
||||
return execute(new RedisCallback<Double>() {
|
||||
@Override
|
||||
public Double doInRedis(RedisConnection connection) {
|
||||
return connection.zScore(rawKey, rawValue);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long count(K key, final double min, final double max) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
|
||||
return execute(new RedisCallback<Long>() {
|
||||
@Override
|
||||
public Long doInRedis(RedisConnection connection) {
|
||||
return connection.zCount(rawKey, min, max);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long size(K key) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
|
||||
return execute(new RedisCallback<Long>() {
|
||||
@Override
|
||||
public Long doInRedis(RedisConnection connection) {
|
||||
return connection.zCard(rawKey);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unionAndStore(K key, K otherKey, K destKey) {
|
||||
unionAndStore(key, Collections.singleton(otherKey), destKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unionAndStore(K key, Collection<K> otherKeys, K destKey) {
|
||||
final byte[][] rawKeys = rawKeys(key, otherKeys);
|
||||
final byte[] rawDestKey = rawKey(destKey);
|
||||
execute(new RedisCallback<Object>() {
|
||||
@Override
|
||||
public Object doInRedis(RedisConnection connection) {
|
||||
connection.zUnionStore(rawDestKey, rawKeys);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.core;
|
||||
|
||||
/**
|
||||
* Contract defining the bind of the implementing entity to a Redis 'key'.
|
||||
* Useful for executing 'bound' operations or operating over Redis 'collection' or 'views'.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface KeyBound<K> {
|
||||
|
||||
/**
|
||||
* Returns the key associated with this entity.
|
||||
*
|
||||
* @return key associated with the implementing entity
|
||||
*/
|
||||
K getKey();
|
||||
}
|
||||
@@ -64,6 +64,17 @@ public interface RedisOperations<K, V> {
|
||||
*/
|
||||
<T> T execute(SessionCallback<T> session);
|
||||
|
||||
// /**
|
||||
// * Executes the given action object on a pipelined connection, returning the results. Note that the callback <b>cannot</b>
|
||||
// * return a non-null value as it gets overwritten by the pipeline.
|
||||
// *
|
||||
// * @param <T> list element return type
|
||||
// * @param action callback object to execute
|
||||
// * @return list of objects returned by the pipeline
|
||||
// */
|
||||
// List<V> executePipelined(RedisCallback<?> action);
|
||||
|
||||
|
||||
Boolean hasKey(K key);
|
||||
|
||||
void delete(K key);
|
||||
@@ -84,7 +95,9 @@ public interface RedisOperations<K, V> {
|
||||
|
||||
Boolean expireAt(K key, Date date);
|
||||
|
||||
void persist(K key);
|
||||
Boolean persist(K key);
|
||||
|
||||
Boolean move(K key, int dbIndex);
|
||||
|
||||
Long getExpire(K key);
|
||||
|
||||
@@ -101,7 +114,7 @@ public interface RedisOperations<K, V> {
|
||||
|
||||
void discard();
|
||||
|
||||
Object exec();
|
||||
List<Object> exec();
|
||||
|
||||
// pubsub functionality on the template
|
||||
void convertAndSend(String destination, Object message);
|
||||
@@ -192,13 +205,15 @@ public interface RedisOperations<K, V> {
|
||||
|
||||
List<V> sort(SortQuery<K> query);
|
||||
|
||||
|
||||
<T> List<T> sort(SortQuery<K> query, RedisSerializer<T> resultSerializer);
|
||||
|
||||
|
||||
<T> List<T> sort(SortQuery<K> query, BulkMapper<T, V> bulkMapper);
|
||||
|
||||
<T, S> List<T> sort(SortQuery<K> query, BulkMapper<T, S> bulkMapper, RedisSerializer<S> resultSerializer);
|
||||
|
||||
Long sort(SortQuery<K> query, K storeKey);
|
||||
|
||||
RedisSerializer<?> getValueSerializer();
|
||||
|
||||
RedisSerializer<?> getKeySerializer();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.keyvalue.redis.core;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
|
||||
/**
|
||||
* Callback executing all operations against a surrogate 'session' (basically against the same underlying Redis connection).
|
||||
* Allows 'transactions' to take place through the use of multi/discard/exec/watch/unwatch commands.
|
||||
@@ -29,5 +31,5 @@ public interface SessionCallback<T> {
|
||||
* @param operations Redis operations
|
||||
* @return return value
|
||||
*/
|
||||
<K, V> T execute(RedisOperations<K, V> operations);
|
||||
<K, V> T execute(RedisOperations<K, V> operations) throws DataAccessException;
|
||||
}
|
||||
|
||||
@@ -51,12 +51,9 @@ public class StringRedisTemplate extends RedisTemplate<String, String> {
|
||||
* @param connectionFactory connection factory for creating new connections
|
||||
*/
|
||||
public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
super(connectionFactory);
|
||||
RedisSerializer<String> stringSerializer = new StringRedisSerializer();
|
||||
setKeySerializer(stringSerializer);
|
||||
setValueSerializer(stringSerializer);
|
||||
setHashKeySerializer(stringSerializer);
|
||||
setHashValueSerializer(stringSerializer);
|
||||
this();
|
||||
setConnectionFactory(connectionFactory);
|
||||
afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.keyvalue.redis.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -40,15 +41,15 @@ public interface ValueOperations<K, V> {
|
||||
|
||||
V getAndSet(K key, V value);
|
||||
|
||||
Collection<V> multiGet(Collection<K> keys);
|
||||
List<V> multiGet(Collection<K> keys);
|
||||
|
||||
Long increment(K key, long delta);
|
||||
|
||||
Integer append(K key, String value);
|
||||
|
||||
String get(K key, int start, int end);
|
||||
String get(K key, long start, long end);
|
||||
|
||||
void set(K key, int start, int end);
|
||||
void set(K key, V value, long offset);
|
||||
|
||||
Long size(K key);
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.core.query;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.connection.DefaultSortParameters;
|
||||
import org.springframework.data.keyvalue.redis.connection.SortParameters;
|
||||
import org.springframework.data.keyvalue.redis.serializer.RedisSerializer;
|
||||
|
||||
/**
|
||||
* Utilities for {@link SortQuery} implementations.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public abstract class QueryUtils {
|
||||
|
||||
public static <K> SortParameters convertQuery(SortQuery<K> query, RedisSerializer<String> stringSerializer) {
|
||||
|
||||
return new DefaultSortParameters(stringSerializer.serialize(query.getBy()), query.getLimit(), serialize(
|
||||
query.getGetPattern(), stringSerializer), query.getOrder(), query.isAlphabetic());
|
||||
}
|
||||
|
||||
private static byte[][] serialize(List<String> strings, RedisSerializer<String> stringSerializer) {
|
||||
List<byte[]> raw = null;
|
||||
|
||||
if (strings == null) {
|
||||
raw = Collections.emptyList();
|
||||
}
|
||||
else {
|
||||
raw = new ArrayList<byte[]>(strings.size());
|
||||
for (String key : strings) {
|
||||
raw.add(stringSerializer.serialize(key));
|
||||
}
|
||||
}
|
||||
return raw.toArray(new byte[raw.size()][]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Query package for Redis template.
|
||||
*/
|
||||
package org.springframework.data.keyvalue.redis.core.query;
|
||||
|
||||
@@ -30,7 +30,7 @@ public class JacksonHashMapper<T> implements HashMapper<T, String, Object> {
|
||||
|
||||
private final ObjectMapper mapper;
|
||||
private final JavaType userType;
|
||||
private final JavaType mapType = TypeFactory.type(Map.class);
|
||||
private final JavaType mapType = TypeFactory.mapType(Map.class, String.class, Object.class);
|
||||
|
||||
public JacksonHashMapper(Class<T> type) {
|
||||
this(type, new ObjectMapper());
|
||||
@@ -47,7 +47,6 @@ public class JacksonHashMapper<T> implements HashMapper<T, String, Object> {
|
||||
return (T) mapper.convertValue(hash, userType);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Map<String, Object> toHash(T object) {
|
||||
return mapper.convertValue(object, mapType);
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Dedicated support package for Redis hashes.
|
||||
*
|
||||
* Provides mapping of objects to hashes/maps (and vice versa).
|
||||
*/
|
||||
package org.springframework.data.keyvalue.redis.hash;
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
package org.springframework.data.keyvalue.redis.listener;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -39,6 +38,7 @@ import org.springframework.data.keyvalue.redis.connection.MessageListener;
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisConnection;
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.keyvalue.redis.connection.Subscription;
|
||||
import org.springframework.data.keyvalue.redis.connection.util.ByteArrayWrapper;
|
||||
import org.springframework.data.keyvalue.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.keyvalue.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.scheduling.SchedulingAwareRunnable;
|
||||
@@ -101,9 +101,9 @@ public class RedisMessageListenerContainer implements InitializingBean, Disposab
|
||||
// to avoid creation of hashes for each message, the maps use raw byte arrays (wrapped to respect the equals/hashcode contract)
|
||||
|
||||
// lookup map between patterns and listeners
|
||||
private final Map<ArrayHolder, Collection<MessageListener>> patternMapping = new ConcurrentHashMap<ArrayHolder, Collection<MessageListener>>();
|
||||
private final Map<ByteArrayWrapper, Collection<MessageListener>> patternMapping = new ConcurrentHashMap<ByteArrayWrapper, Collection<MessageListener>>();
|
||||
// lookup map between channels and listeners
|
||||
private final Map<ArrayHolder, Collection<MessageListener>> channelMapping = new ConcurrentHashMap<ArrayHolder, Collection<MessageListener>>();
|
||||
private final Map<ByteArrayWrapper, Collection<MessageListener>> channelMapping = new ConcurrentHashMap<ByteArrayWrapper, Collection<MessageListener>>();
|
||||
|
||||
private final SubscriptionTask subscriptionTask = new SubscriptionTask();
|
||||
|
||||
@@ -448,7 +448,7 @@ public class RedisMessageListenerContainer implements InitializingBean, Disposab
|
||||
|
||||
for (Topic topic : topics) {
|
||||
|
||||
ArrayHolder holder = new ArrayHolder(serializer.serialize(topic.getTopic()));
|
||||
ByteArrayWrapper holder = new ByteArrayWrapper(serializer.serialize(topic.getTopic()));
|
||||
|
||||
if (topic instanceof ChannelTopic) {
|
||||
Collection<MessageListener> collection = channelMapping.get(holder);
|
||||
@@ -457,7 +457,7 @@ public class RedisMessageListenerContainer implements InitializingBean, Disposab
|
||||
channelMapping.put(holder, collection);
|
||||
}
|
||||
collection.add(listener);
|
||||
channels.add(holder.array);
|
||||
channels.add(holder.getArray());
|
||||
|
||||
if (trace)
|
||||
logger.trace("Adding listener '" + listener + "' on channel '" + topic.getTopic() + "'");
|
||||
@@ -470,7 +470,7 @@ public class RedisMessageListenerContainer implements InitializingBean, Disposab
|
||||
patternMapping.put(holder, collection);
|
||||
}
|
||||
collection.add(listener);
|
||||
patterns.add(holder.array);
|
||||
patterns.add(holder.getArray());
|
||||
|
||||
if (trace)
|
||||
logger.trace("Adding listener '" + listener + "' for pattern '" + topic.getTopic() + "'");
|
||||
@@ -598,7 +598,7 @@ public class RedisMessageListenerContainer implements InitializingBean, Disposab
|
||||
}
|
||||
}
|
||||
|
||||
private byte[][] unwrap(Collection<ArrayHolder> holders) {
|
||||
private byte[][] unwrap(Collection<ByteArrayWrapper> holders) {
|
||||
if (CollectionUtils.isEmpty(holders)) {
|
||||
return new byte[0][];
|
||||
}
|
||||
@@ -606,8 +606,8 @@ public class RedisMessageListenerContainer implements InitializingBean, Disposab
|
||||
byte[][] unwrapped = new byte[holders.size()][];
|
||||
|
||||
int index = 0;
|
||||
for (ArrayHolder arrayHolder : holders) {
|
||||
unwrapped[index++] = arrayHolder.array;
|
||||
for (ByteArrayWrapper arrayHolder : holders) {
|
||||
unwrapped[index++] = arrayHolder.getArray();
|
||||
}
|
||||
|
||||
return unwrapped;
|
||||
@@ -700,12 +700,12 @@ public class RedisMessageListenerContainer implements InitializingBean, Disposab
|
||||
// do channel matching first
|
||||
byte[] channel = message.getChannel();
|
||||
|
||||
Collection<MessageListener> ch = channelMapping.get(new ArrayHolder(channel));
|
||||
Collection<MessageListener> ch = channelMapping.get(new ByteArrayWrapper(channel));
|
||||
Collection<MessageListener> pt = null;
|
||||
|
||||
// followed by pattern matching
|
||||
if (pattern != null && pattern.length > 0) {
|
||||
pt = patternMapping.get(new ArrayHolder(pattern));
|
||||
pt = patternMapping.get(new ByteArrayWrapper(pattern));
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(ch)) {
|
||||
@@ -739,34 +739,4 @@ public class RedisMessageListenerContainer implements InitializingBean, Disposab
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple wrapper class used for wrapping arrays so they can be used as keys inside maps.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
private class ArrayHolder {
|
||||
|
||||
private final byte[] array;
|
||||
private final int hashCode;
|
||||
|
||||
ArrayHolder(byte[] array) {
|
||||
this.array = array;
|
||||
this.hashCode = Arrays.hashCode(array);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof ArrayHolder) {
|
||||
return Arrays.equals(array, ((ArrayHolder) obj).array);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.keyvalue.redis.listener.adapter;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -152,8 +151,7 @@ public class MessageListenerAdapter implements MessageListener {
|
||||
/**
|
||||
* Set the serializer that will convert incoming raw Redis messages to
|
||||
* listener method arguments.
|
||||
* <p>The default converter is a {@link JdkSerializationRedisSerializer}, which is able
|
||||
* to handle {@link Serializable} objects.
|
||||
* <p>The default converter is a {@link StringRedisSerializer}.
|
||||
*/
|
||||
public void setSerializer(RedisSerializer<?> serializer) {
|
||||
this.serializer = serializer;
|
||||
@@ -286,11 +284,11 @@ public class MessageListenerAdapter implements MessageListener {
|
||||
throw (DataAccessException) targetEx;
|
||||
}
|
||||
else {
|
||||
throw new ListenerExecutionFailedException("Listener method '" + methodName + "' threw exception",
|
||||
throw new RedisListenerExecutionFailedException("Listener method '" + methodName + "' threw exception",
|
||||
targetEx);
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
throw new ListenerExecutionFailedException("Failed to invoke target method '" + methodName
|
||||
throw new RedisListenerExecutionFailedException("Failed to invoke target method '" + methodName
|
||||
+ "' with arguments " + ObjectUtils.nullSafeToString(arguments), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,24 +23,24 @@ import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
* @author Costin Leau
|
||||
* @see MessageListenerAdapter
|
||||
*/
|
||||
public class ListenerExecutionFailedException extends InvalidDataAccessApiUsageException {
|
||||
public class RedisListenerExecutionFailedException extends InvalidDataAccessApiUsageException {
|
||||
|
||||
/**
|
||||
* Constructs a new <code>ListenerExecutionFailedException</code> instance.
|
||||
* Constructs a new <code>RedisListenerExecutionFailedException</code> instance.
|
||||
*
|
||||
* @param msg
|
||||
* @param cause
|
||||
*/
|
||||
public ListenerExecutionFailedException(String msg, Throwable cause) {
|
||||
public RedisListenerExecutionFailedException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>ListenerExecutionFailedException</code> instance.
|
||||
* Constructs a new <code>RedisListenerExecutionFailedException</code> instance.
|
||||
*
|
||||
* @param msg
|
||||
*/
|
||||
public ListenerExecutionFailedException(String msg) {
|
||||
public RedisListenerExecutionFailedException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.serializer;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple toString() serializer for the core (lang) numberic JDK types.
|
||||
*
|
||||
* @see String#valueOf(Object)
|
||||
* @see Long#valueOf(String)
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class BasicNumberToStringSerializer<T extends Number> implements RedisSerializer<T> {
|
||||
|
||||
private final Charset charset;
|
||||
private final Constructor<T> ctor;
|
||||
|
||||
public BasicNumberToStringSerializer(Class<T> type) {
|
||||
this(type, Charset.forName("UTF8"));
|
||||
}
|
||||
|
||||
public BasicNumberToStringSerializer(Class<T> type, Charset charset) {
|
||||
Assert.notNull(type);
|
||||
this.charset = charset;
|
||||
|
||||
if (!(Byte.class.isAssignableFrom(type) || Short.class.isAssignableFrom(type)
|
||||
|| Long.class.isAssignableFrom(type) || Integer.class.isAssignableFrom(type)
|
||||
|| Float.class.isAssignableFrom(type) || Double.class.isAssignableFrom(type))) {
|
||||
throw new IllegalArgumentException("Type " + type + " not supported");
|
||||
}
|
||||
|
||||
try {
|
||||
ctor = type.getConstructor(String.class);
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalArgumentException("Cannot find suitable constructor for " + type);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T deserialize(byte[] bytes) {
|
||||
String string = new String(bytes, charset);
|
||||
return BeanUtils.instantiateClass(ctor, string);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serialize(T object) {
|
||||
String string = String.valueOf(object);
|
||||
return string.getBytes(charset);
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -40,7 +41,7 @@ import org.springframework.util.Assert;
|
||||
public class GenericToStringSerializer<T> implements RedisSerializer<T>, BeanFactoryAware {
|
||||
|
||||
private final Charset charset;
|
||||
private Converter converter;
|
||||
private Converter converter = new Converter(ConversionServiceFactory.createDefaultConversionService());
|
||||
private Class<T> type;
|
||||
|
||||
public GenericToStringSerializer(Class<T> type) {
|
||||
@@ -65,12 +66,19 @@ public class GenericToStringSerializer<T> implements RedisSerializer<T>, BeanFac
|
||||
|
||||
@Override
|
||||
public T deserialize(byte[] bytes) {
|
||||
if (bytes == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String string = new String(bytes, charset);
|
||||
return converter.convert(string, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serialize(T object) {
|
||||
if (object == null) {
|
||||
return null;
|
||||
}
|
||||
String string = converter.convert(object, String.class);
|
||||
return string.getBytes(charset);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ import org.springframework.util.Assert;
|
||||
* {@link RedisSerializer} that can read and write JSON using <a href="http://jackson.codehaus.org/">Jackson's</a> {@link ObjectMapper}.
|
||||
*
|
||||
* <p>This converter can be used to bind to typed beans, or untyped {@link java.util.HashMap HashMap} instances.
|
||||
*
|
||||
* <b>Note:</b>Null objects are serialized as empty arrays and vice versa.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
@@ -44,7 +46,7 @@ public class JacksonJsonRedisSerializer<T> implements RedisSerializer<T> {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public T deserialize(byte[] bytes) throws SerializationException {
|
||||
if (SerializerUtils.isEmpty(bytes)) {
|
||||
if (SerializationUtils.isEmpty(bytes)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
@@ -57,7 +59,7 @@ public class JacksonJsonRedisSerializer<T> implements RedisSerializer<T> {
|
||||
@Override
|
||||
public byte[] serialize(Object t) throws SerializationException {
|
||||
if (t == null) {
|
||||
return SerializerUtils.EMPTY_ARRAY;
|
||||
return SerializationUtils.EMPTY_ARRAY;
|
||||
}
|
||||
try {
|
||||
return this.objectMapper.writeValueAsBytes(t);
|
||||
|
||||
@@ -34,6 +34,10 @@ public class JdkSerializationRedisSerializer implements RedisSerializer<Object>
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Object deserialize(byte[] bytes) {
|
||||
if (SerializationUtils.isEmpty(bytes)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return deserializer.convert(bytes);
|
||||
} catch (Exception ex) {
|
||||
@@ -43,6 +47,9 @@ public class JdkSerializationRedisSerializer implements RedisSerializer<Object>
|
||||
|
||||
@Override
|
||||
public byte[] serialize(Object object) {
|
||||
if (object == null) {
|
||||
return SerializationUtils.EMPTY_ARRAY;
|
||||
}
|
||||
try {
|
||||
return serializer.convert(object);
|
||||
} catch (Exception ex) {
|
||||
|
||||
@@ -31,7 +31,7 @@ import org.springframework.util.Assert;
|
||||
* Delegates serialization/deserialization to OXM {@link Marshaller} and
|
||||
* {@link Unmarshaller}.
|
||||
*
|
||||
* <b>Note:</b>Null objects are serialized as empty arrays.
|
||||
* <b>Note:</b>Null objects are serialized as empty arrays and vice versa.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
@@ -72,7 +72,7 @@ public class OxmSerializer implements InitializingBean, RedisSerializer<Object>
|
||||
|
||||
@Override
|
||||
public Object deserialize(byte[] bytes) throws SerializationException {
|
||||
if (SerializerUtils.isEmpty(bytes)) {
|
||||
if (SerializationUtils.isEmpty(bytes)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ public class OxmSerializer implements InitializingBean, RedisSerializer<Object>
|
||||
@Override
|
||||
public byte[] serialize(Object t) throws SerializationException {
|
||||
if (t == null) {
|
||||
return SerializerUtils.EMPTY_ARRAY;
|
||||
return SerializationUtils.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.data.keyvalue.redis.serializer;
|
||||
* Basic interface serialization and deserialization of Objects to byte arrays (binary data).
|
||||
*
|
||||
* It is recommended that implementations are designed to handle null objects/empty arrays on serialization and deserialization side.
|
||||
* Note that Redis does not accept null keys or values but can return null replies (for non existing keys).
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @author Costin Leau
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.serializer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Utility class with various serialization-related methods.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public abstract class SerializationUtils {
|
||||
|
||||
static final byte[] EMPTY_ARRAY = new byte[0];
|
||||
|
||||
static boolean isEmpty(byte[] data) {
|
||||
return (data == null || data.length == 0);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T extends Collection<?>> T deserializeValues(Collection<byte[]> rawValues, Class<T> type, RedisSerializer<?> redisSerializer) {
|
||||
// connection in pipeline/multi mode
|
||||
if (rawValues == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Collection<Object> values = (List.class.isAssignableFrom(type) ? new ArrayList<Object>(rawValues.size())
|
||||
: new LinkedHashSet<Object>(rawValues.size()));
|
||||
for (byte[] bs : rawValues) {
|
||||
values.add(redisSerializer.deserialize(bs));
|
||||
}
|
||||
|
||||
return (T) values;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Set<T> deserialize(Set<byte[]> rawValues, RedisSerializer<T> redisSerializer) {
|
||||
return deserializeValues(rawValues, Set.class, redisSerializer);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> List<T> deserialize(List<byte[]> rawValues, RedisSerializer<T> redisSerializer) {
|
||||
return deserializeValues(rawValues, List.class, redisSerializer);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Collection<T> deserialize(Collection<byte[]> rawValues, RedisSerializer<T> redisSerializer) {
|
||||
return deserializeValues(rawValues, List.class, redisSerializer);
|
||||
}
|
||||
}
|
||||
@@ -25,14 +25,12 @@ import org.springframework.util.Assert;
|
||||
* <p/>
|
||||
* Useful when the interaction with the Redis happens mainly through Strings.
|
||||
*
|
||||
* <p/> Converts null into empty arrays (which get translated into empty strings on deserialization).
|
||||
* <p/> Does not perform any null conversion since empty strings are valid keys/values.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class StringRedisSerializer implements RedisSerializer<String> {
|
||||
|
||||
private final static byte[] EMPTY_ARRAY = new byte[0];
|
||||
private final String EMPTY_STRING = "";
|
||||
private final Charset charset;
|
||||
|
||||
public StringRedisSerializer() {
|
||||
@@ -46,11 +44,11 @@ public class StringRedisSerializer implements RedisSerializer<String> {
|
||||
|
||||
@Override
|
||||
public String deserialize(byte[] bytes) {
|
||||
return (SerializerUtils.isEmpty(bytes) ? EMPTY_STRING : new String(bytes, charset));
|
||||
return (bytes == null ? null : new String(bytes, charset));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serialize(String string) {
|
||||
return (string == null ? EMPTY_ARRAY : string.getBytes(charset));
|
||||
return (string == null ? null : string.getBytes(charset));
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.support.atomic;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.core.RedisOperations;
|
||||
import org.springframework.data.keyvalue.redis.core.SessionCallback;
|
||||
|
||||
/**
|
||||
* Check-And-Set (CAS) utility. Performs the CAS loop until successful pattern using
|
||||
* Redis watch/exec operations.
|
||||
*
|
||||
* The given callback can contain one or multiple reads followed by a multi call
|
||||
* and one or multiple writes:
|
||||
*
|
||||
* <pre>
|
||||
* return CASUtils.execute(ops, key, new Callable<Integer>() {
|
||||
* @Override
|
||||
* public Integer call() throws Exception {
|
||||
* // check
|
||||
* int value = get();
|
||||
* // start MULTI
|
||||
* ops.multi();
|
||||
* // set
|
||||
* ops.increment(key, 1);
|
||||
* return value;
|
||||
* }
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
abstract class CASUtils {
|
||||
|
||||
public static <T, K, V> T execute(final RedisOperations<K, V> ops, final K key, final Callable<T> callback) {
|
||||
return ops.execute(new SessionCallback<T>() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public T execute(RedisOperations operations) {
|
||||
try {
|
||||
for (;;) {
|
||||
operations.watch(Collections.singleton(key));
|
||||
T result = callback.call();
|
||||
if (operations.exec() != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
// includes DataAccessException
|
||||
if (ex instanceof RuntimeException) {
|
||||
throw (RuntimeException) ex;
|
||||
}
|
||||
throw new RuntimeException("Callback threw exception", ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -17,14 +17,17 @@ package org.springframework.data.keyvalue.redis.support.atomic;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.connection.DataType;
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.keyvalue.redis.core.KeyBound;
|
||||
import org.springframework.data.keyvalue.redis.core.BoundKeyOperations;
|
||||
import org.springframework.data.keyvalue.redis.core.RedisOperations;
|
||||
import org.springframework.data.keyvalue.redis.core.RedisTemplate;
|
||||
import org.springframework.data.keyvalue.redis.core.SessionCallback;
|
||||
import org.springframework.data.keyvalue.redis.core.ValueOperations;
|
||||
import org.springframework.data.keyvalue.redis.serializer.BasicNumberToStringSerializer;
|
||||
import org.springframework.data.keyvalue.redis.serializer.GenericToStringSerializer;
|
||||
import org.springframework.data.keyvalue.redis.serializer.StringRedisSerializer;
|
||||
|
||||
/**
|
||||
@@ -34,9 +37,9 @@ import org.springframework.data.keyvalue.redis.serializer.StringRedisSerializer;
|
||||
* @see java.util.concurrent.atomic.AtomicInteger
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class RedisAtomicInteger extends Number implements Serializable, KeyBound<String> {
|
||||
public class RedisAtomicInteger extends Number implements Serializable, BoundKeyOperations<String> {
|
||||
|
||||
private final String key;
|
||||
private volatile String key;
|
||||
private ValueOperations<String, Integer> operations;
|
||||
private RedisOperations<String, Integer> generalOps;
|
||||
|
||||
@@ -48,16 +51,7 @@ public class RedisAtomicInteger extends Number implements Serializable, KeyBound
|
||||
* @param factory connection factory
|
||||
*/
|
||||
public RedisAtomicInteger(String redisCounter, RedisConnectionFactory factory) {
|
||||
RedisTemplate<String, Integer> redisTemplate = new RedisTemplate<String, Integer>(factory);
|
||||
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||
redisTemplate.setValueSerializer(new BasicNumberToStringSerializer<Integer>(Integer.class));
|
||||
redisTemplate.setExposeConnection(true);
|
||||
this.key = redisCounter;
|
||||
this.generalOps = redisTemplate;
|
||||
this.operations = generalOps.opsForValue();
|
||||
if (this.operations.get(redisCounter) == null) {
|
||||
set(0);
|
||||
}
|
||||
this(redisCounter, factory, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,12 +62,29 @@ public class RedisAtomicInteger extends Number implements Serializable, KeyBound
|
||||
* @param initialValue
|
||||
*/
|
||||
public RedisAtomicInteger(String redisCounter, RedisConnectionFactory factory, int initialValue) {
|
||||
RedisTemplate<String, Integer> redisTemplate = new RedisTemplate<String, Integer>(factory);
|
||||
this(redisCounter, factory, Integer.valueOf(initialValue));
|
||||
}
|
||||
|
||||
private RedisAtomicInteger(String redisCounter, RedisConnectionFactory factory, Integer initialValue) {
|
||||
RedisTemplate<String, Integer> redisTemplate = new RedisTemplate<String, Integer>();
|
||||
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||
redisTemplate.setValueSerializer(new GenericToStringSerializer<Integer>(Integer.class));
|
||||
redisTemplate.setExposeConnection(true);
|
||||
redisTemplate.setConnectionFactory(factory);
|
||||
redisTemplate.afterPropertiesSet();
|
||||
|
||||
this.key = redisCounter;
|
||||
this.generalOps = redisTemplate;
|
||||
this.operations = generalOps.opsForValue();
|
||||
this.operations.set(redisCounter, initialValue);
|
||||
|
||||
if (initialValue == null) {
|
||||
if (this.operations.get(redisCounter) == null) {
|
||||
set(0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
set(initialValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,6 +93,8 @@ public class RedisAtomicInteger extends Number implements Serializable, KeyBound
|
||||
*
|
||||
* Use {@link #RedisAtomicInteger(String, RedisOperations, int)} to set the counter to a certain value
|
||||
* as an alternative constructor or {@link #set(int)}.
|
||||
*
|
||||
* Note that integers need to be properly serialized so that Redis can recognized the values as numeric and thus modify their value.
|
||||
*
|
||||
* @param redisCounter
|
||||
* @param operations
|
||||
@@ -98,6 +111,8 @@ public class RedisAtomicInteger extends Number implements Serializable, KeyBound
|
||||
/**
|
||||
* Constructs a new <code>RedisAtomicInteger</code> instance with the given initial value.
|
||||
*
|
||||
* Note that integers need to be properly serialized so that Redis can recognized the values as numeric and thus modify their value.
|
||||
*
|
||||
* @param redisCounter
|
||||
* @param operations
|
||||
* @param initialValue
|
||||
@@ -109,11 +124,6 @@ public class RedisAtomicInteger extends Number implements Serializable, KeyBound
|
||||
this.operations.set(redisCounter, initialValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current value.
|
||||
*
|
||||
@@ -251,4 +261,40 @@ public class RedisAtomicInteger extends Number implements Serializable, KeyBound
|
||||
public double doubleValue() {
|
||||
return (double) get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean expire(long timeout, TimeUnit unit) {
|
||||
return generalOps.expire(key, timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean expireAt(Date date) {
|
||||
return generalOps.expireAt(key, date);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getExpire() {
|
||||
return generalOps.getExpire(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean persist() {
|
||||
return generalOps.persist(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rename(String newKey) {
|
||||
generalOps.rename(key, newKey);
|
||||
key = newKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getType() {
|
||||
return DataType.STRING;
|
||||
}
|
||||
}
|
||||
@@ -17,14 +17,17 @@ package org.springframework.data.keyvalue.redis.support.atomic;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.connection.DataType;
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.keyvalue.redis.core.KeyBound;
|
||||
import org.springframework.data.keyvalue.redis.core.BoundKeyOperations;
|
||||
import org.springframework.data.keyvalue.redis.core.RedisOperations;
|
||||
import org.springframework.data.keyvalue.redis.core.RedisTemplate;
|
||||
import org.springframework.data.keyvalue.redis.core.SessionCallback;
|
||||
import org.springframework.data.keyvalue.redis.core.ValueOperations;
|
||||
import org.springframework.data.keyvalue.redis.serializer.BasicNumberToStringSerializer;
|
||||
import org.springframework.data.keyvalue.redis.serializer.GenericToStringSerializer;
|
||||
import org.springframework.data.keyvalue.redis.serializer.StringRedisSerializer;
|
||||
|
||||
/**
|
||||
@@ -34,9 +37,9 @@ import org.springframework.data.keyvalue.redis.serializer.StringRedisSerializer;
|
||||
* @see java.util.concurrent.atomic.AtomicLong
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class RedisAtomicLong extends Number implements Serializable, KeyBound<String> {
|
||||
public class RedisAtomicLong extends Number implements Serializable, BoundKeyOperations<String> {
|
||||
|
||||
private final String key;
|
||||
private volatile String key;
|
||||
private ValueOperations<String, Long> operations;
|
||||
private RedisOperations<String, Long> generalOps;
|
||||
|
||||
@@ -48,16 +51,7 @@ public class RedisAtomicLong extends Number implements Serializable, KeyBound<St
|
||||
* @param factory connection factory
|
||||
*/
|
||||
public RedisAtomicLong(String redisCounter, RedisConnectionFactory factory) {
|
||||
RedisTemplate<String, Long> redisTemplate = new RedisTemplate<String, Long>(factory);
|
||||
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||
redisTemplate.setValueSerializer(new BasicNumberToStringSerializer<Long>(Long.class));
|
||||
redisTemplate.setExposeConnection(true);
|
||||
this.key = redisCounter;
|
||||
this.generalOps = redisTemplate;
|
||||
this.operations = generalOps.opsForValue();
|
||||
if (this.operations.get(redisCounter) == null) {
|
||||
set(0);
|
||||
}
|
||||
this(redisCounter, factory, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,21 +62,39 @@ public class RedisAtomicLong extends Number implements Serializable, KeyBound<St
|
||||
* @param initialValue
|
||||
*/
|
||||
public RedisAtomicLong(String redisCounter, RedisConnectionFactory factory, long initialValue) {
|
||||
RedisTemplate<String, Long> redisTemplate = new RedisTemplate<String, Long>(factory);
|
||||
this(redisCounter, factory, Long.valueOf(initialValue));
|
||||
}
|
||||
|
||||
private RedisAtomicLong(String redisCounter, RedisConnectionFactory factory, Long initialValue) {
|
||||
RedisTemplate<String, Long> redisTemplate = new RedisTemplate<String, Long>();
|
||||
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||
redisTemplate.setValueSerializer(new GenericToStringSerializer<Long>(Long.class));
|
||||
redisTemplate.setExposeConnection(true);
|
||||
redisTemplate.setConnectionFactory(factory);
|
||||
redisTemplate.afterPropertiesSet();
|
||||
|
||||
this.key = redisCounter;
|
||||
this.generalOps = redisTemplate;
|
||||
this.operations = generalOps.opsForValue();
|
||||
this.operations.set(redisCounter, initialValue);
|
||||
}
|
||||
|
||||
if (initialValue == null) {
|
||||
if (this.operations.get(redisCounter) == null) {
|
||||
set(0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
set(initialValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>RedisAtomicLong</code> instance. Uses as initial value
|
||||
* the data from the backing store (sets the counter to 0 if no value is found).
|
||||
*
|
||||
* Use {@link #RedisAtomicLong(String, RedisOperations, long)} to set the counter to a certain value
|
||||
* as an alternative constructor or {@link #set(long)}.
|
||||
* as an alternative constructor or {@link #set(long)}.
|
||||
*
|
||||
* Note that longs need to be properly serialized so that Redis can recognized the values as numeric and thus modify their value.
|
||||
*
|
||||
* @param redisCounter
|
||||
* @param operations
|
||||
@@ -99,6 +111,8 @@ public class RedisAtomicLong extends Number implements Serializable, KeyBound<St
|
||||
/**
|
||||
* Constructs a new <code>RedisAtomicLong</code> instance with the given initial value.
|
||||
*
|
||||
* Note that longs need to be properly serialized so that Redis can recognized the values as numeric and thus modify their value.
|
||||
*
|
||||
* @param redisCounter
|
||||
* @param operations
|
||||
* @param initialValue
|
||||
@@ -109,11 +123,6 @@ public class RedisAtomicLong extends Number implements Serializable, KeyBound<St
|
||||
this.operations.set(redisCounter, initialValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current value.
|
||||
*
|
||||
@@ -255,4 +264,40 @@ public class RedisAtomicLong extends Number implements Serializable, KeyBound<St
|
||||
public double doubleValue() {
|
||||
return (double) get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean expire(long timeout, TimeUnit unit) {
|
||||
return generalOps.expire(key, timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean expireAt(Date date) {
|
||||
return generalOps.expireAt(key, date);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getExpire() {
|
||||
return generalOps.getExpire(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean persist() {
|
||||
return generalOps.persist(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rename(String newKey) {
|
||||
generalOps.rename(key, newKey);
|
||||
key = newKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getType() {
|
||||
return DataType.STRING;
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,8 @@ package org.springframework.data.keyvalue.redis.support.collections;
|
||||
|
||||
import java.util.AbstractCollection;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.core.RedisOperations;
|
||||
|
||||
@@ -30,7 +32,7 @@ public abstract class AbstractRedisCollection<E> extends AbstractCollection<E> i
|
||||
|
||||
public static final String ENCODING = "UTF-8";
|
||||
|
||||
private final String key;
|
||||
private volatile String key;
|
||||
private final RedisOperations<String, E> operations;
|
||||
|
||||
public <K> AbstractRedisCollection(String key, RedisOperations<String, E> operations) {
|
||||
@@ -116,4 +118,30 @@ public abstract class AbstractRedisCollection<E> extends AbstractCollection<E> i
|
||||
sb.append(getKey());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean expire(long timeout, TimeUnit unit) {
|
||||
return operations.expire(key, timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean expireAt(Date date) {
|
||||
return operations.expireAt(key, date);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getExpire() {
|
||||
return operations.getExpire(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean persist() {
|
||||
return operations.persist(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rename(final String newKey) {
|
||||
CollectionUtils.rename(key, newKey, operations);
|
||||
key = newKey;
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,10 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.keyvalue.redis.core.RedisOperations;
|
||||
import org.springframework.data.keyvalue.redis.core.SessionCallback;
|
||||
|
||||
/**
|
||||
* Utility class used mainly for type conversion by the default collection implementations.
|
||||
* Meant for internal use.
|
||||
@@ -48,4 +52,55 @@ abstract class CollectionUtils {
|
||||
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
|
||||
static <K> void rename(final K key, final K newKey, RedisOperations<K, ?> operations) {
|
||||
operations.execute(new SessionCallback<Object>() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Object execute(RedisOperations operations) throws DataAccessException {
|
||||
do {
|
||||
operations.watch(key);
|
||||
|
||||
if (operations.hasKey(key)) {
|
||||
operations.multi();
|
||||
operations.rename(key, newKey);
|
||||
}
|
||||
else {
|
||||
operations.multi();
|
||||
}
|
||||
} while (operations.exec() == null);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static <K> Boolean renameIfAbsent(final K key, final K newKey, RedisOperations<K, ?> operations) {
|
||||
return operations.execute(new SessionCallback<Boolean>() {
|
||||
@Override
|
||||
public Boolean execute(RedisOperations operations) throws DataAccessException {
|
||||
List<Object> exec = null;
|
||||
do {
|
||||
operations.watch(key);
|
||||
|
||||
if (operations.hasKey(key)) {
|
||||
operations.multi();
|
||||
operations.renameIfAbsent(key, newKey);
|
||||
}
|
||||
else {
|
||||
operations.watch(newKey);
|
||||
operations.multi();
|
||||
operations.hasKey(newKey);
|
||||
operations.hasKey(newKey);
|
||||
}
|
||||
exec = operations.exec();
|
||||
} while (exec == null);
|
||||
|
||||
boolean result = ((Long) exec.get(0) == 1);
|
||||
if (exec.size() > 1) {
|
||||
result = !result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import java.util.ListIterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.connection.DataType;
|
||||
import org.springframework.data.keyvalue.redis.core.BoundListOperations;
|
||||
import org.springframework.data.keyvalue.redis.core.RedisOperations;
|
||||
|
||||
@@ -498,4 +499,9 @@ public class DefaultRedisList<E> extends AbstractRedisCollection<E> implements R
|
||||
public E takeLast() throws InterruptedException {
|
||||
return pollLast(0, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getType() {
|
||||
return DataType.LIST;
|
||||
}
|
||||
}
|
||||
@@ -17,11 +17,14 @@ package org.springframework.data.keyvalue.redis.support.collections;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.connection.DataType;
|
||||
import org.springframework.data.keyvalue.redis.core.BoundHashOperations;
|
||||
import org.springframework.data.keyvalue.redis.core.RedisOperations;
|
||||
|
||||
@@ -84,11 +87,6 @@ public class DefaultRedisMap<K, V> implements RedisMap<K, V> {
|
||||
return hashOps.increment(key, delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return hashOps.getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisOperations<String, ?> getOperations() {
|
||||
return hashOps.getOperations();
|
||||
@@ -295,4 +293,40 @@ public class DefaultRedisMap<K, V> implements RedisMap<K, V> {
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean expire(long timeout, TimeUnit unit) {
|
||||
return hashOps.expire(timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean expireAt(Date date) {
|
||||
return hashOps.expireAt(date);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getExpire() {
|
||||
return hashOps.getExpire();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean persist() {
|
||||
return hashOps.persist();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return hashOps.getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rename(String newKey) {
|
||||
hashOps.rename(newKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getType() {
|
||||
return hashOps.getType();
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.connection.DataType;
|
||||
import org.springframework.data.keyvalue.redis.core.BoundSetOperations;
|
||||
import org.springframework.data.keyvalue.redis.core.RedisOperations;
|
||||
|
||||
@@ -166,4 +167,9 @@ public class DefaultRedisSet<E> extends AbstractRedisCollection<E> implements Re
|
||||
public int size() {
|
||||
return boundSetOps.size().intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getType() {
|
||||
return DataType.SET;
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.connection.DataType;
|
||||
import org.springframework.data.keyvalue.redis.core.BoundZSetOperations;
|
||||
import org.springframework.data.keyvalue.redis.core.RedisOperations;
|
||||
|
||||
@@ -211,4 +212,9 @@ public class DefaultRedisZSet<E> extends AbstractRedisCollection<E> implements R
|
||||
public Double score(Object o) {
|
||||
return boundZSetOps.score(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getType() {
|
||||
return DataType.ZSET;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.support.collections;
|
||||
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.data.keyvalue.redis.connection.DataType;
|
||||
import org.springframework.data.keyvalue.redis.core.RedisTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Factory bean that facilitates creation of Redis-based collections. Supports list, set, zset (or sortedSet), map (or hash) and properties.
|
||||
* Will use the key type if it exists or to create a dedicated collection (Properties vs Map).
|
||||
* Otherwise uses the provided type (default is list).
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class RedisCollectionFactoryBean implements InitializingBean, BeanNameAware, FactoryBean<RedisStore> {
|
||||
|
||||
public enum CollectionType {
|
||||
LIST {
|
||||
@Override
|
||||
public DataType dataType() {
|
||||
return DataType.LIST;
|
||||
}
|
||||
},
|
||||
SET {
|
||||
@Override
|
||||
public DataType dataType() {
|
||||
return DataType.SET;
|
||||
}
|
||||
},
|
||||
ZSET {
|
||||
@Override
|
||||
public DataType dataType() {
|
||||
return DataType.ZSET;
|
||||
}
|
||||
},
|
||||
MAP {
|
||||
@Override
|
||||
public DataType dataType() {
|
||||
return DataType.HASH;
|
||||
}
|
||||
},
|
||||
PROPERTIES {
|
||||
@Override
|
||||
public DataType dataType() {
|
||||
return DataType.HASH;
|
||||
}
|
||||
};
|
||||
|
||||
abstract DataType dataType();
|
||||
}
|
||||
|
||||
|
||||
private RedisStore store;
|
||||
private CollectionType type = null;
|
||||
private RedisTemplate<String, ?> template;
|
||||
private String key;
|
||||
private String beanName;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
if (!StringUtils.hasText(key)) {
|
||||
key = beanName;
|
||||
}
|
||||
|
||||
Assert.hasText(key, "Collection key is required - no key or bean name specified");
|
||||
Assert.notNull(template, "Redis template is required");
|
||||
|
||||
DataType dt = template.type(key);
|
||||
|
||||
// can't create store
|
||||
Assert.isTrue(!DataType.STRING.equals(dt), "Cannot create store on keys of type 'string'");
|
||||
|
||||
store = createStore(dt);
|
||||
|
||||
if (store == null) {
|
||||
if (type == null) {
|
||||
type = CollectionType.LIST;
|
||||
}
|
||||
store = createStore(type.dataType());
|
||||
}
|
||||
}
|
||||
|
||||
private RedisStore createStore(DataType dt) {
|
||||
switch (dt) {
|
||||
case LIST:
|
||||
return new DefaultRedisList(key, template);
|
||||
|
||||
case SET:
|
||||
return new DefaultRedisSet(key, template);
|
||||
|
||||
case ZSET:
|
||||
return new DefaultRedisZSet(key, template);
|
||||
|
||||
case HASH:
|
||||
if (CollectionType.PROPERTIES.equals(type)) {
|
||||
return new RedisProperties(key, template);
|
||||
}
|
||||
return new DefaultRedisMap(key, template);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisStore getObject() {
|
||||
return store;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return (store != null ? store.getClass() : RedisStore.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanName(String name) {
|
||||
this.beanName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the store type. Used if the key does not exist.
|
||||
*
|
||||
* @param type The type to set.
|
||||
*/
|
||||
public void setType(CollectionType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the template used by the resulting store.
|
||||
*
|
||||
* @param template The template to set.
|
||||
*/
|
||||
public void setTemplate(RedisTemplate<String, ?> template) {
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the key of the store.
|
||||
*
|
||||
* @param key The key to set.
|
||||
*/
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.support.collections;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.connection.DataType;
|
||||
import org.springframework.data.keyvalue.redis.core.BoundHashOperations;
|
||||
import org.springframework.data.keyvalue.redis.core.RedisOperations;
|
||||
|
||||
/**
|
||||
* {@link Properties} extension for a Redis back-store. Useful for reading (and storing) properties
|
||||
* inside a Redis hash. Particularly useful inside a Spring container for hooking into Spring's property
|
||||
* placeholder or {@link org.springframework.beans.factory.config.PropertiesFactoryBean}.
|
||||
* <p/>
|
||||
* Note that this implementation only accepts Strings - objects of other type are not supported.
|
||||
*
|
||||
* @see Properties
|
||||
* @see org.springframework.core.io.support.PropertiesLoaderSupport
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class RedisProperties extends Properties implements RedisMap<Object, Object> {
|
||||
|
||||
private final BoundHashOperations<String, String, String> hashOps;
|
||||
private final RedisMap<String, String> delegate;
|
||||
|
||||
/**
|
||||
* Constructs a new <code>RedisProperties</code> instance.
|
||||
*
|
||||
*/
|
||||
public RedisProperties(BoundHashOperations<String, String, String> boundOps) {
|
||||
this(null, boundOps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>RedisProperties</code> instance.
|
||||
*
|
||||
* @param boundOps
|
||||
*/
|
||||
public RedisProperties(String key, RedisOperations<String, ?> operations) {
|
||||
this(null, operations.<String, String> boundHashOps(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>RedisProperties</code> instance.
|
||||
*
|
||||
* @param defaults
|
||||
*/
|
||||
public RedisProperties(Properties defaults, BoundHashOperations<String, String, String> boundOps) {
|
||||
super(defaults);
|
||||
this.hashOps = boundOps;
|
||||
this.delegate = new DefaultRedisMap<String, String>(boundOps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>RedisProperties</code> instance.
|
||||
*
|
||||
* @param defaults
|
||||
* @param boundOps
|
||||
*/
|
||||
public RedisProperties(Properties defaults, String key, RedisOperations<String, ?> operations) {
|
||||
this(defaults, operations.<String, String> boundHashOps(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object get(Object key) {
|
||||
return delegate.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object put(Object key, Object value) {
|
||||
return delegate.put((String) key, (String) value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public synchronized void putAll(Map<? extends Object, ? extends Object> t) {
|
||||
delegate.putAll((Map<? extends String, ? extends String>) t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<?> propertyNames() {
|
||||
Set<String> keys = new LinkedHashSet<String>(delegate.keySet());
|
||||
keys.addAll(defaults.stringPropertyNames());
|
||||
return Collections.enumeration(keys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void clear() {
|
||||
delegate.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object clone() {
|
||||
return new RedisProperties(defaults, hashOps);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean contains(Object value) {
|
||||
return containsValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean containsKey(Object key) {
|
||||
return delegate.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return delegate.containsValue(value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public synchronized Enumeration<Object> elements() {
|
||||
Collection values = delegate.values();
|
||||
return Collections.enumeration(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Set<Entry<Object, Object>> entrySet() {
|
||||
Set entries = delegate.entrySet();
|
||||
return entries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean equals(Object o) {
|
||||
if (o == this)
|
||||
return true;
|
||||
|
||||
if (o instanceof RedisProperties) {
|
||||
return o.hashCode() == hashCode();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int hashCode() {
|
||||
int hash = RedisProperties.class.hashCode();
|
||||
return hash * 17 + delegate.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isEmpty() {
|
||||
return delegate.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Enumeration<Object> keys() {
|
||||
Set<Object> keys = keySet();
|
||||
return Collections.enumeration(keys);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Set<Object> keySet() {
|
||||
Set keys = delegate.keySet();
|
||||
return keys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object remove(Object key) {
|
||||
return delegate.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int size() {
|
||||
return delegate.size();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Collection<Object> values() {
|
||||
Collection vals = delegate.values();
|
||||
return vals;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long increment(Object key, long delta) {
|
||||
return hashOps.increment((String) key, delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisOperations<String, ?> getOperations() {
|
||||
return hashOps.getOperations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean expire(long timeout, TimeUnit unit) {
|
||||
return hashOps.expire(timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean expireAt(Date date) {
|
||||
return hashOps.expireAt(date);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getExpire() {
|
||||
return hashOps.getExpire();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return hashOps.getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getType() {
|
||||
return hashOps.getType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean persist() {
|
||||
return hashOps.persist();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rename(String newKey) {
|
||||
hashOps.rename(newKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object putIfAbsent(Object key, Object value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object key, Object value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replace(Object key, Object oldValue, Object newValue) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object replace(Object key, Object value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void storeToXML(OutputStream os, String comment, String encoding) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void storeToXML(OutputStream os, String comment) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.keyvalue.redis.support.collections;
|
||||
|
||||
import org.springframework.data.keyvalue.redis.core.KeyBound;
|
||||
import org.springframework.data.keyvalue.redis.core.BoundKeyOperations;
|
||||
import org.springframework.data.keyvalue.redis.core.RedisOperations;
|
||||
|
||||
/**
|
||||
@@ -26,7 +26,7 @@ import org.springframework.data.keyvalue.redis.core.RedisOperations;
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface RedisStore extends KeyBound<String> {
|
||||
public interface RedisStore extends BoundKeyOperations<String> {
|
||||
|
||||
/**
|
||||
* Returns the underlying Redis operations used by the backing implementation.
|
||||
|
||||
@@ -149,4 +149,61 @@ listener method arguments. Default is a StringRedisSerializer.
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:element name="collection">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Factory creating collections on top of Redis keys.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation>
|
||||
<tool:exports type="org.springframework.data.keyvalue.redis.support.collections.RedisCollectionFactoryBean"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="id" type="xsd:ID">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The name of the Redis collection.]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="key" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Redis key of the created collection. Defaults to bean id.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="template" type="xsd:string" default="redisTemplate">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A reference to a RedisTemplate bean.Default is "redisTemplate".
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.data.keyvalue.redis.core.RedisTemplate"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="type" default="LIST" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The collection type (default is list).
|
||||
If the key exists, its type takes priority. The type is used to disambiguate the collection type (map vs properties) or
|
||||
specify one in case the key is missing.]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:restriction base="xsd:string">
|
||||
<xsd:enumeration value="LIST"/>
|
||||
<xsd:enumeration value="SET"/>
|
||||
<xsd:enumeration value="ZSET"/>
|
||||
<xsd:enumeration value="MAP"/>
|
||||
<xsd:enumeration value="PROPERTIES"/>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
@@ -40,7 +40,7 @@ public abstract class ConnectionFactoryTracker {
|
||||
for (RedisConnectionFactory connectionFactory : connFactories) {
|
||||
try {
|
||||
((DisposableBean) connectionFactory).destroy();
|
||||
System.out.println("Succesfully cleaned up factory " + connectionFactory);
|
||||
//System.out.println("Succesfully cleaned up factory " + connectionFactory);
|
||||
} catch (Exception ex) {
|
||||
System.err.println("Cannot clean factory " + connectionFactory + ex);
|
||||
}
|
||||
|
||||
@@ -18,15 +18,23 @@ package org.springframework.data.keyvalue.redis.connection;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.BlockingDeque;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.keyvalue.redis.Address;
|
||||
import org.springframework.data.keyvalue.redis.ConnectionFactoryTracker;
|
||||
import org.springframework.data.keyvalue.redis.Person;
|
||||
import org.springframework.data.keyvalue.redis.core.StringRedisTemplate;
|
||||
import org.springframework.data.keyvalue.redis.serializer.JdkSerializationRedisSerializer;
|
||||
import org.springframework.data.keyvalue.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.keyvalue.redis.serializer.StringRedisSerializer;
|
||||
@@ -40,12 +48,21 @@ public abstract class AbstractConnectionIntegrationTests {
|
||||
private static final String listName = "test-list";
|
||||
private static final byte[] EMPTY_ARRAY = new byte[0];
|
||||
|
||||
protected abstract RedisConnectionFactory getConnectionFactory();
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
connection = new DefaultStringRedisConnection(getConnectionFactory().getConnection());
|
||||
ConnectionFactoryTracker.add(getConnectionFactory());
|
||||
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void cleanUp() {
|
||||
ConnectionFactoryTracker.cleanUp();
|
||||
}
|
||||
|
||||
protected abstract RedisConnectionFactory getConnectionFactory();
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
@@ -55,16 +72,19 @@ public abstract class AbstractConnectionIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void testLPush() throws Exception {
|
||||
Long index = connection.lPush(listName.getBytes(), "bar".getBytes());
|
||||
byte[] val = "bar".getBytes();
|
||||
Long index = connection.lPush(listName.getBytes(), val);
|
||||
if (index != null) {
|
||||
assertEquals((Long) (index + 1), connection.lPush(listName.getBytes(), "bar".getBytes()));
|
||||
assertEquals((Long) (index + 1), connection.lPush(listName.getBytes(), val));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetAndGet() {
|
||||
connection.set("foo".getBytes(), "blahblah".getBytes());
|
||||
assertEquals("blahblah", new String(connection.get("foo".getBytes())));
|
||||
String key = "foo";
|
||||
String value = "blabla";
|
||||
connection.set(key.getBytes(), value.getBytes());
|
||||
assertEquals(value, new String(connection.get(key.getBytes())));
|
||||
}
|
||||
|
||||
private boolean isJredis() {
|
||||
@@ -102,8 +122,12 @@ public abstract class AbstractConnectionIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void testNullKey() throws Exception {
|
||||
connection.decr((String) null);
|
||||
connection.decr(EMPTY_ARRAY);
|
||||
try {
|
||||
connection.decr((String) null);
|
||||
} catch (Exception ex) {
|
||||
// excepted
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -140,4 +164,159 @@ public abstract class AbstractConnectionIntegrationTests {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullSerialization() throws Exception {
|
||||
String[] keys = new String[] { "~", "[" };
|
||||
List<String> mGet = connection.mGet(keys);
|
||||
assertEquals(2, mGet.size());
|
||||
assertNull(mGet.get(0));
|
||||
assertNull(mGet.get(1));
|
||||
|
||||
StringRedisTemplate stringTemplate = new StringRedisTemplate(getConnectionFactory());
|
||||
List<String> multiGet = stringTemplate.opsForValue().multiGet(Arrays.asList(keys));
|
||||
assertEquals(2, multiGet.size());
|
||||
assertNull(multiGet.get(0));
|
||||
assertNull(multiGet.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullCollections() throws Exception {
|
||||
connection.openPipeline();
|
||||
assertNull(connection.keys("~*"));
|
||||
assertNull(connection.hKeys("~"));
|
||||
connection.closePipeline();
|
||||
}
|
||||
|
||||
// pub sub test
|
||||
|
||||
@Test
|
||||
public void testPubSub() throws Exception {
|
||||
|
||||
final BlockingDeque<Message> queue = new LinkedBlockingDeque<Message>();
|
||||
|
||||
final MessageListener ml = new MessageListener() {
|
||||
@Override
|
||||
public void onMessage(Message message, byte[] pattern) {
|
||||
queue.add(message);
|
||||
System.out.println("received message");
|
||||
}
|
||||
};
|
||||
|
||||
final byte[] channel = "foo.tv".getBytes();
|
||||
final RedisConnection subConn = getConnectionFactory().getConnection();
|
||||
|
||||
assertNotSame(connection, subConn);
|
||||
|
||||
|
||||
final AtomicBoolean flag = new AtomicBoolean(true);
|
||||
|
||||
Runnable listener = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
subConn.subscribe(ml, channel);
|
||||
System.out.println("Subscribed");
|
||||
while (flag.get()) {
|
||||
try {
|
||||
Thread.currentThread().sleep(2000);
|
||||
} catch (Exception ex) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Thread th = new Thread(listener, "listener");
|
||||
th.start();
|
||||
|
||||
try {
|
||||
Thread.sleep(1500);
|
||||
connection.publish(channel, "one".getBytes());
|
||||
connection.publish(channel, "two".getBytes());
|
||||
connection.publish(channel, "I see you".getBytes());
|
||||
System.out.println("Done publishing...");
|
||||
Thread.sleep(5000);
|
||||
System.out.println("Done waiting ...");
|
||||
} finally {
|
||||
flag.set(false);
|
||||
}
|
||||
System.out.println(queue);
|
||||
assertEquals(3, queue.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPubSubWithNamedChannels() {
|
||||
final byte[] expectedChannel = "channel1".getBytes();
|
||||
final byte[] expectedMessage = "msg".getBytes();
|
||||
|
||||
MessageListener listener = new MessageListener() {
|
||||
|
||||
@Override
|
||||
public void onMessage(Message message, byte[] pattern) {
|
||||
assertArrayEquals(expectedChannel, message.getChannel());
|
||||
assertArrayEquals(expectedMessage, message.getBody());
|
||||
}
|
||||
};
|
||||
|
||||
Thread th = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// sleep 1 second to let the registration happen
|
||||
try {
|
||||
Thread.currentThread().sleep(1000);
|
||||
} catch (InterruptedException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
// open a new connection
|
||||
RedisConnection connection2 = getConnectionFactory().getConnection();
|
||||
connection2.publish(expectedMessage, expectedChannel);
|
||||
connection2.close();
|
||||
// unsubscribe connection
|
||||
connection.getSubscription().unsubscribe();
|
||||
}
|
||||
});
|
||||
|
||||
th.start();
|
||||
connection.subscribe(listener, expectedChannel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPubSubWithPatterns() {
|
||||
final byte[] expectedPattern = "channel*".getBytes();
|
||||
final byte[] expectedMessage = "msg".getBytes();
|
||||
|
||||
MessageListener listener = new MessageListener() {
|
||||
|
||||
@Override
|
||||
public void onMessage(Message message, byte[] pattern) {
|
||||
assertArrayEquals(expectedPattern, pattern);
|
||||
assertArrayEquals(expectedMessage, message.getBody());
|
||||
System.out.println("Received message '" + new String(message.getBody()) + "'");
|
||||
}
|
||||
};
|
||||
|
||||
Thread th = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// sleep 1 second to let the registration happen
|
||||
try {
|
||||
Thread.currentThread().sleep(1000);
|
||||
} catch (InterruptedException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
// open a new connection
|
||||
RedisConnection connection2 = getConnectionFactory().getConnection();
|
||||
connection2.publish(expectedMessage, "channel1".getBytes());
|
||||
connection2.publish(expectedMessage, "channel2".getBytes());
|
||||
connection2.close();
|
||||
// unsubscribe connection
|
||||
connection.getSubscription().pUnsubscribe(expectedPattern);
|
||||
}
|
||||
});
|
||||
|
||||
th.start();
|
||||
connection.pSubscribe(listener, expectedPattern);
|
||||
}
|
||||
}
|
||||
@@ -16,13 +16,9 @@
|
||||
|
||||
package org.springframework.data.keyvalue.redis.connection.jedis;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.keyvalue.redis.SettingsUtils;
|
||||
import org.springframework.data.keyvalue.redis.connection.AbstractConnectionIntegrationTests;
|
||||
import org.springframework.data.keyvalue.redis.connection.Message;
|
||||
import org.springframework.data.keyvalue.redis.connection.MessageListener;
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisConnectionFactory;
|
||||
|
||||
import redis.clients.jedis.BinaryJedis;
|
||||
@@ -47,83 +43,6 @@ public class JedisConnectionIntegrationTests extends AbstractConnectionIntegrati
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPubSubWithNamedChannels() {
|
||||
final byte[] expectedChannel = "channel1".getBytes();
|
||||
final byte[] expectedMessage = "msg".getBytes();
|
||||
|
||||
MessageListener listener = new MessageListener() {
|
||||
|
||||
@Override
|
||||
public void onMessage(Message message, byte[] pattern) {
|
||||
assertArrayEquals(expectedChannel, message.getChannel());
|
||||
assertArrayEquals(expectedMessage, message.getBody());
|
||||
System.out.println("Received message '" + new String(message.getBody()) + "'");
|
||||
}
|
||||
};
|
||||
|
||||
Thread th = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// sleep 1 second to let the registration happen
|
||||
try {
|
||||
Thread.currentThread().sleep(1000);
|
||||
} catch (InterruptedException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
// open a new connection
|
||||
JedisConnection connection2 = factory.getConnection();
|
||||
connection2.publish(expectedMessage, expectedChannel);
|
||||
connection2.close();
|
||||
// unsubscribe connection
|
||||
connection.getSubscription().unsubscribe();
|
||||
}
|
||||
});
|
||||
|
||||
th.start();
|
||||
connection.subscribe(listener, expectedChannel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPubSubWithPatterns() {
|
||||
final byte[] expectedPattern = "channel*".getBytes();
|
||||
final byte[] expectedMessage = "msg".getBytes();
|
||||
|
||||
MessageListener listener = new MessageListener() {
|
||||
|
||||
@Override
|
||||
public void onMessage(Message message, byte[] pattern) {
|
||||
assertArrayEquals(expectedPattern, pattern);
|
||||
assertArrayEquals(expectedMessage, message.getBody());
|
||||
System.out.println("Received message '" + new String(message.getBody()) + "'");
|
||||
}
|
||||
};
|
||||
|
||||
Thread th = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// sleep 1 second to let the registration happen
|
||||
try {
|
||||
Thread.currentThread().sleep(1000);
|
||||
} catch (InterruptedException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
// open a new connection
|
||||
JedisConnection connection2 = factory.getConnection();
|
||||
connection2.publish(expectedMessage, "channel1".getBytes());
|
||||
connection2.publish(expectedMessage, "channel2".getBytes());
|
||||
connection2.close();
|
||||
// unsubscribe connection
|
||||
connection.getSubscription().pUnsubscribe(expectedPattern);
|
||||
}
|
||||
});
|
||||
|
||||
th.start();
|
||||
connection.pSubscribe(listener, expectedPattern);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMulti() throws Exception {
|
||||
byte[] key = "key".getBytes();
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.data.keyvalue.redis.connection.jredis;
|
||||
|
||||
import org.jredis.JRedis;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.keyvalue.redis.SettingsUtils;
|
||||
import org.springframework.data.keyvalue.redis.connection.AbstractConnectionIntegrationTests;
|
||||
@@ -43,10 +44,47 @@ public class JRedisConnectionIntegrationTests extends AbstractConnectionIntegrat
|
||||
@Test
|
||||
public void testRaw() throws Exception {
|
||||
JRedis jr = (JRedis) factory.getConnection().getNativeConnection();
|
||||
|
||||
|
||||
System.out.println(jr.dbsize());
|
||||
System.out.println(jr.exists("foobar"));
|
||||
jr.set("foobar", "barfoo");
|
||||
System.out.println(jr.get("foobar"));
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("JRedis does not support pipelining")
|
||||
public void testNullCollections() {
|
||||
}
|
||||
|
||||
@Ignore
|
||||
public void testNullKey() throws Exception {
|
||||
}
|
||||
|
||||
@Ignore
|
||||
public void testNullValue() throws Exception {
|
||||
}
|
||||
|
||||
@Ignore
|
||||
public void testHashNullKey() throws Exception {
|
||||
}
|
||||
|
||||
@Ignore
|
||||
public void testHashNullValue() throws Exception {
|
||||
}
|
||||
|
||||
@Ignore
|
||||
public void testNullSerialization() throws Exception {
|
||||
}
|
||||
|
||||
@Ignore
|
||||
public void testPubSub() throws Exception {
|
||||
}
|
||||
|
||||
@Ignore
|
||||
public void testPubSubWithPatterns() {
|
||||
}
|
||||
|
||||
@Ignore
|
||||
public void testPubSubWithNamedChannels() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.connection.rjc;
|
||||
|
||||
import org.idevlab.rjc.Session;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.keyvalue.redis.SettingsUtils;
|
||||
import org.springframework.data.keyvalue.redis.connection.AbstractConnectionIntegrationTests;
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisConnectionFactory;
|
||||
|
||||
/**
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class RjcConnectionIntegrationTests extends AbstractConnectionIntegrationTests {
|
||||
|
||||
RjcConnectionFactory factory;
|
||||
|
||||
public RjcConnectionIntegrationTests() {
|
||||
factory = new RjcConnectionFactory();
|
||||
factory.setPort(SettingsUtils.getPort());
|
||||
factory.setHostName(SettingsUtils.getHost());
|
||||
|
||||
factory.setUsePool(false);
|
||||
factory.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RedisConnectionFactory getConnectionFactory() {
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRaw() throws Exception {
|
||||
Session jr = (Session) factory.getConnection().getNativeConnection();
|
||||
|
||||
System.out.println(jr.dbSize());
|
||||
System.out.println(jr.exists("foobar"));
|
||||
jr.set("foobar", "barfoo");
|
||||
System.out.println(jr.get("foobar"));
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ public class SessionTest {
|
||||
when(factory.getConnection()).thenReturn(conn);
|
||||
final StringRedisTemplate template = new StringRedisTemplate(factory);
|
||||
|
||||
template.execute(new SessionCallback() {
|
||||
template.execute(new SessionCallback<Object>() {
|
||||
@Override
|
||||
public Object execute(RedisOperations operations) {
|
||||
checkConnection(template, conn);
|
||||
@@ -48,7 +48,7 @@ public class SessionTest {
|
||||
});
|
||||
}
|
||||
|
||||
private void checkConnection(RedisTemplate template, final RedisConnection expectedConnection) {
|
||||
private void checkConnection(RedisTemplate<?, ?> template, final RedisConnection expectedConnection) {
|
||||
template.execute(new RedisCallback<Object>() {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.keyvalue.redis.core;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
import org.springframework.data.keyvalue.redis.ConnectionFactoryTracker;
|
||||
import org.springframework.data.keyvalue.redis.support.collections.CollectionTestParams;
|
||||
import org.springframework.data.keyvalue.redis.support.collections.ObjectFactory;
|
||||
|
||||
/**
|
||||
* @author Costin Leau
|
||||
*/
|
||||
@RunWith(Parameterized.class)
|
||||
public class TemplateTest {
|
||||
private ObjectFactory<Object> objFactory;
|
||||
private RedisTemplate template;
|
||||
|
||||
public TemplateTest(ObjectFactory<Object> objFactory, RedisTemplate template) {
|
||||
this.objFactory = objFactory;
|
||||
this.template = template;
|
||||
ConnectionFactoryTracker.add(template.getConnectionFactory());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void cleanUp() {
|
||||
ConnectionFactoryTracker.cleanUp();
|
||||
}
|
||||
|
||||
@Parameters
|
||||
public static Collection<Object[]> testParams() {
|
||||
return CollectionTestParams.testParams();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeys() throws Exception {
|
||||
assertTrue(template.keys("*") != null);
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import java.util.Collection;
|
||||
import org.springframework.data.keyvalue.redis.Person;
|
||||
import org.springframework.data.keyvalue.redis.SettingsUtils;
|
||||
import org.springframework.data.keyvalue.redis.connection.jedis.JedisConnectionFactory;
|
||||
import org.springframework.data.keyvalue.redis.connection.rjc.RjcConnectionFactory;
|
||||
import org.springframework.data.keyvalue.redis.core.RedisTemplate;
|
||||
import org.springframework.data.keyvalue.redis.core.StringRedisTemplate;
|
||||
import org.springframework.data.keyvalue.redis.support.collections.ObjectFactory;
|
||||
@@ -38,16 +39,30 @@ public class PubSubTestParams {
|
||||
ObjectFactory<Person> personFactory = new PersonObjectFactory();
|
||||
|
||||
JedisConnectionFactory jedisConnFactory = new JedisConnectionFactory();
|
||||
jedisConnFactory.setUsePool(false);
|
||||
jedisConnFactory.setUsePool(true);
|
||||
jedisConnFactory.setPort(SettingsUtils.getPort());
|
||||
jedisConnFactory.setHostName(SettingsUtils.getHost());
|
||||
jedisConnFactory.setDatabase(2);
|
||||
|
||||
jedisConnFactory.afterPropertiesSet();
|
||||
|
||||
RedisTemplate<String, String> stringTemplate = new StringRedisTemplate(jedisConnFactory);
|
||||
RedisTemplate<String, Person> personTemplate = new RedisTemplate<String, Person>(jedisConnFactory);
|
||||
|
||||
// create RJC
|
||||
|
||||
return Arrays.asList(new Object[][] { { stringFactory, stringTemplate }, { personFactory, personTemplate } });
|
||||
RjcConnectionFactory rjcConnFactory = new RjcConnectionFactory();
|
||||
rjcConnFactory.setUsePool(false);
|
||||
rjcConnFactory.setPort(SettingsUtils.getPort());
|
||||
rjcConnFactory.setHostName(SettingsUtils.getHost());
|
||||
rjcConnFactory.afterPropertiesSet();
|
||||
|
||||
RedisTemplate<String, String> stringTemplateRJC = new StringRedisTemplate(rjcConnFactory);
|
||||
RedisTemplate<String, Person> personTemplateRJC = new RedisTemplate<String, Person>(rjcConnFactory);
|
||||
|
||||
|
||||
return Arrays.asList(new Object[][] { { stringFactory, stringTemplate }, { personFactory, personTemplate },
|
||||
{ stringFactory, stringTemplateRJC }, { personFactory, personTemplateRJC }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +32,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.data.keyvalue.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.keyvalue.redis.ConnectionFactoryTracker;
|
||||
import org.springframework.data.keyvalue.redis.core.RedisTemplate;
|
||||
import org.springframework.data.keyvalue.redis.listener.adapter.MessageListenerAdapter;
|
||||
import org.springframework.data.keyvalue.redis.support.collections.ObjectFactory;
|
||||
@@ -51,13 +50,11 @@ public class PubSubTests<T> {
|
||||
protected RedisMessageListenerContainer container;
|
||||
protected ObjectFactory<T> factory;
|
||||
protected RedisTemplate template;
|
||||
private static Set<RedisConnectionFactory> connFactories = new LinkedHashSet<RedisConnectionFactory>();
|
||||
|
||||
private final BlockingDeque<String> bag = new LinkedBlockingDeque<String>(99);
|
||||
|
||||
private final Object handler = new Object() {
|
||||
void handleMessage(String message) {
|
||||
System.out.println("Received message " + message);
|
||||
bag.add(message);
|
||||
}
|
||||
};
|
||||
@@ -74,7 +71,7 @@ public class PubSubTests<T> {
|
||||
container.addMessageListener(adapter, Arrays.asList(new ChannelTopic(CHANNEL)));
|
||||
container.afterPropertiesSet();
|
||||
|
||||
Thread.sleep(500);
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -85,21 +82,12 @@ public class PubSubTests<T> {
|
||||
public PubSubTests(ObjectFactory<T> factory, RedisTemplate template) {
|
||||
this.factory = factory;
|
||||
this.template = template;
|
||||
connFactories.add(template.getConnectionFactory());
|
||||
ConnectionFactoryTracker.add(template.getConnectionFactory());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void cleanUp() {
|
||||
if (connFactories != null) {
|
||||
for (RedisConnectionFactory connectionFactory : connFactories) {
|
||||
try {
|
||||
((DisposableBean) connectionFactory).destroy();
|
||||
System.out.println("Succesfully cleaned up factory " + connectionFactory);
|
||||
} catch (Exception ex) {
|
||||
System.err.println("Cannot clean factory " + connectionFactory + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
ConnectionFactoryTracker.cleanUp();
|
||||
}
|
||||
|
||||
@Parameters
|
||||
@@ -127,6 +115,8 @@ public class PubSubTests<T> {
|
||||
set.add(bag.poll(1, TimeUnit.SECONDS));
|
||||
set.add(bag.poll(1, TimeUnit.SECONDS));
|
||||
|
||||
System.out.println(set);
|
||||
|
||||
assertTrue(set.contains(payload1));
|
||||
assertTrue(set.contains(payload2));
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user