+ renamed org.springframework.data.keyvalue.redis to o.s.d.redis
+ eliminated Riak package + eliminate Core package
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.redis;
|
||||
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
|
||||
/**
|
||||
* Fatal exception thrown when the Redis connection fails completely.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
*/
|
||||
public class RedisConnectionFailureException extends DataAccessResourceFailureException {
|
||||
|
||||
public RedisConnectionFailureException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public RedisConnectionFailureException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.redis;
|
||||
|
||||
import org.springframework.dao.UncategorizedDataAccessException;
|
||||
|
||||
/**
|
||||
* Exception thrown when we can't classify a Redis exception into one of Spring generic data access exceptions.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class RedisSystemException extends UncategorizedDataAccessException {
|
||||
|
||||
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.redis.config;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
|
||||
import org.springframework.data.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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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.redis.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.data.redis.listener.ChannelTopic;
|
||||
import org.springframework.data.redis.listener.PatternTopic;
|
||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
||||
import org.springframework.data.redis.listener.Topic;
|
||||
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.w3c.dom.Attr;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NamedNodeMap;
|
||||
|
||||
/**
|
||||
* Parser for the Redis <code><listener-container></code> element.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class RedisListenerContainerParser extends AbstractSimpleBeanDefinitionParser {
|
||||
|
||||
@Override
|
||||
protected Class<RedisMessageListenerContainer> getBeanClass(Element element) {
|
||||
return RedisMessageListenerContainer.class;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
// parse attributes (but replace the value assignment with references)
|
||||
NamedNodeMap attributes = element.getAttributes();
|
||||
|
||||
for (int x = 0; x < attributes.getLength(); x++) {
|
||||
Attr attribute = (Attr) attributes.item(x);
|
||||
if (isEligibleAttribute(attribute, parserContext)) {
|
||||
String propertyName = extractPropertyName(attribute.getLocalName());
|
||||
Assert.state(StringUtils.hasText(propertyName),
|
||||
"Illegal property name returned from 'extractPropertyName(String)': cannot be null or empty.");
|
||||
builder.addPropertyReference(propertyName, attribute.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
String phase = element.getAttribute("phase");
|
||||
if (StringUtils.hasText(phase)) {
|
||||
builder.addPropertyValue("phase", phase);
|
||||
}
|
||||
|
||||
postProcess(builder, element);
|
||||
|
||||
// parse nested listeners
|
||||
List<Element> listDefs = DomUtils.getChildElementsByTagName(element, "listener");
|
||||
|
||||
if (!listDefs.isEmpty()) {
|
||||
ManagedMap<BeanDefinition, Collection<? extends BeanDefinition>> listeners = new ManagedMap<BeanDefinition, Collection<? extends BeanDefinition>>(
|
||||
listDefs.size());
|
||||
for (Element listElement : listDefs) {
|
||||
Object[] listenerDefinition = parseListener(listElement);
|
||||
listeners.put((BeanDefinition) listenerDefinition[0],
|
||||
(Collection<? extends BeanDefinition>) listenerDefinition[1]);
|
||||
}
|
||||
|
||||
builder.addPropertyValue("messageListeners", listeners);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEligibleAttribute(String attributeName) {
|
||||
return (!"phase".equals(attributeName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a listener definition. Returns the listener bean reference definition (as the array first entry) and its associated topics (also as bean definitions).
|
||||
*
|
||||
* @param element
|
||||
* @return
|
||||
*/
|
||||
private Object[] parseListener(Element element) {
|
||||
Object[] ret = new Object[2];
|
||||
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MessageListenerAdapter.class);
|
||||
builder.addConstructorArgReference(element.getAttribute("ref"));
|
||||
|
||||
String method = element.getAttribute("method");
|
||||
if (StringUtils.hasText(method)){
|
||||
builder.addPropertyValue("defaultListenerMethod", method);
|
||||
}
|
||||
|
||||
String serializer = element.getAttribute("serializer");
|
||||
if (StringUtils.hasText(serializer)){
|
||||
builder.addPropertyReference("serializer", serializer);
|
||||
}
|
||||
|
||||
// assemble topics
|
||||
Collection<Topic> topics = new ArrayList<Topic>();
|
||||
|
||||
// get topic
|
||||
String xTopics = element.getAttribute("topic");
|
||||
if (StringUtils.hasText(xTopics)) {
|
||||
String[] array = StringUtils.delimitedListToStringArray(xTopics, " ");
|
||||
|
||||
for (String string : array) {
|
||||
topics.add(string.contains("*") ? new PatternTopic(string) : new ChannelTopic(string));
|
||||
}
|
||||
}
|
||||
ret[0] = builder.getBeanDefinition();
|
||||
ret[1] = topics;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldGenerateId() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.redis.config;
|
||||
|
||||
import org.springframework.beans.factory.xml.NamespaceHandler;
|
||||
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
|
||||
|
||||
/**
|
||||
* {@link NamespaceHandler} for Spring Data Redis namespace.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class RedisNamespaceHandler extends NamespaceHandlerSupport {
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
registerBeanDefinitionParser("listener-container", new RedisListenerContainerParser());
|
||||
registerBeanDefinitionParser("collection", new RedisCollectionParser());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.redis.connection;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Enumeration of the Redis data types.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public enum DataType {
|
||||
|
||||
NONE("none"), STRING("string"), LIST("list"), SET("set"), ZSET("zset"), HASH("hash");
|
||||
|
||||
private static final Map<String, DataType> codeLookup = new ConcurrentHashMap<String, DataType>(6);
|
||||
|
||||
static {
|
||||
for (DataType type : EnumSet.allOf(DataType.class))
|
||||
codeLookup.put(type.code, type);
|
||||
|
||||
}
|
||||
|
||||
private final String code;
|
||||
|
||||
DataType(String name) {
|
||||
this.code = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the code associated with the current enum.
|
||||
*
|
||||
* @return code of this enum
|
||||
*/
|
||||
public String code() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method for converting an enum code to an actual enum.
|
||||
*
|
||||
* @param code enum code
|
||||
* @return actual enum corresponding to the given code
|
||||
*/
|
||||
public static DataType fromCode(String code) {
|
||||
DataType data = codeLookup.get(code);
|
||||
if (data == null)
|
||||
throw new IllegalArgumentException("unknown data type code");
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.redis.connection;
|
||||
|
||||
|
||||
/**
|
||||
* Default message implementation.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class DefaultMessage implements Message {
|
||||
|
||||
private final byte[] channel;
|
||||
private final byte[] body;
|
||||
private String toString;
|
||||
|
||||
public DefaultMessage(byte[] channel, byte[] body) {
|
||||
this.body = body;
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getChannel() {
|
||||
return (channel != null ? channel.clone() : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBody() {
|
||||
return (body != null ? body.clone() : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (toString == null){
|
||||
toString = new String(body);
|
||||
}
|
||||
return toString;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* 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 byPattern 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.redis.connection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Default implementation for {@link SortParameters}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class DefaultSortParameters implements SortParameters {
|
||||
|
||||
private byte[] byPattern;
|
||||
private Range limit;
|
||||
private final List<byte[]> getPattern = new ArrayList<byte[]>(4);
|
||||
private Order order;
|
||||
private Boolean alphabetic;
|
||||
|
||||
/**
|
||||
* Constructs a new <code>DefaultSortParameters</code> instance.
|
||||
*/
|
||||
public DefaultSortParameters() {
|
||||
this(null, null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>DefaultSortParameters</code> instance.
|
||||
*
|
||||
* @param limit
|
||||
* @param order
|
||||
* @param alphabetic
|
||||
*/
|
||||
public DefaultSortParameters(Range limit, Order order, Boolean alphabetic) {
|
||||
this(null, limit, null, order, alphabetic);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>DefaultSortParameters</code> instance.
|
||||
*
|
||||
* @param byPattern
|
||||
* @param limit
|
||||
* @param getPattern
|
||||
* @param order
|
||||
* @param alphabetic
|
||||
*/
|
||||
public DefaultSortParameters(byte[] byPattern, Range limit, byte[][] getPattern, Order order, Boolean alphabetic) {
|
||||
super();
|
||||
this.byPattern = byPattern;
|
||||
this.limit = limit;
|
||||
this.order = order;
|
||||
this.alphabetic = alphabetic;
|
||||
setGetPattern(getPattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getByPattern() {
|
||||
return byPattern;
|
||||
}
|
||||
|
||||
public void setByPattern(byte[] byPattern) {
|
||||
this.byPattern = byPattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range getLimit() {
|
||||
return limit;
|
||||
}
|
||||
|
||||
public void setLimit(Range limit) {
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[][] getGetPattern() {
|
||||
return getPattern.toArray(new byte[getPattern.size()][]);
|
||||
}
|
||||
|
||||
public void addGetPattern(byte[] gPattern) {
|
||||
getPattern.add(gPattern);
|
||||
}
|
||||
|
||||
public void setGetPattern(byte[][] gPattern) {
|
||||
getPattern.clear();
|
||||
|
||||
for (byte[] bs : gPattern) {
|
||||
getPattern.add(bs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Order getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(Order order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isAlphabetic() {
|
||||
return alphabetic;
|
||||
}
|
||||
|
||||
public void setAlphabetic(Boolean alphabetic) {
|
||||
this.alphabetic = alphabetic;
|
||||
}
|
||||
|
||||
//
|
||||
// builder like methods
|
||||
//
|
||||
|
||||
public DefaultSortParameters order(Order order) {
|
||||
setOrder(order);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DefaultSortParameters alpha() {
|
||||
setAlphabetic(true);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DefaultSortParameters asc() {
|
||||
setOrder(Order.ASC);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DefaultSortParameters desc() {
|
||||
setOrder(Order.DESC);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DefaultSortParameters numeric() {
|
||||
setAlphabetic(false);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DefaultSortParameters get(byte[] pattern) {
|
||||
addGetPattern(pattern);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DefaultSortParameters by(byte[] pattern) {
|
||||
setByPattern(pattern);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DefaultSortParameters limit(long start, long count) {
|
||||
setLimit(new Range(start, count));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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.redis.connection;
|
||||
|
||||
import org.springframework.data.redis.connection.RedisZSetCommands.Tuple;
|
||||
import org.springframework.data.redis.connection.StringRedisConnection.StringTuple;
|
||||
|
||||
/**
|
||||
* Default implementation for {@link StringTuple} interface.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class DefaultStringTuple extends DefaultTuple implements StringTuple {
|
||||
|
||||
private final String valueAsString;
|
||||
|
||||
/**
|
||||
* Constructs a new <code>DefaultStringTuple</code> instance.
|
||||
*
|
||||
* @param value
|
||||
* @param score
|
||||
*/
|
||||
public DefaultStringTuple(byte[] value, String valueAsString, Double score) {
|
||||
super(value, score);
|
||||
this.valueAsString = valueAsString;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>DefaultStringTuple</code> instance.
|
||||
*
|
||||
* @param tuple
|
||||
* @param valueAsString
|
||||
*/
|
||||
public DefaultStringTuple(Tuple tuple, String valueAsString) {
|
||||
super(tuple.getValue(), tuple.getScore());
|
||||
this.valueAsString = valueAsString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueAsString() {
|
||||
return valueAsString;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.redis.connection;
|
||||
|
||||
import org.springframework.data.redis.connection.RedisZSetCommands.Tuple;
|
||||
|
||||
/**
|
||||
* Default implementation for {@link Tuple} interface.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class DefaultTuple implements Tuple {
|
||||
|
||||
private final Double score;
|
||||
private final byte[] value;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new <code>DefaultTuple</code> instance.
|
||||
*
|
||||
* @param value
|
||||
* @param score
|
||||
*/
|
||||
public DefaultTuple(byte[] value, Double score) {
|
||||
this.score = score;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.redis.connection;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Class encapsulating a Redis message body and its properties.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface Message extends Serializable {
|
||||
|
||||
/**
|
||||
* Returns the body (or the payload) of the message.
|
||||
*
|
||||
* @return message body
|
||||
*/
|
||||
byte[] getBody();
|
||||
|
||||
/**
|
||||
* Returns the channel associated with the message.
|
||||
*
|
||||
* @return message channel.
|
||||
*/
|
||||
byte[] getChannel();
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.redis.connection;
|
||||
|
||||
/**
|
||||
* Listener of messages published in Redis.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface MessageListener {
|
||||
|
||||
/**
|
||||
* Callback for processing received objects through Redis.
|
||||
*
|
||||
* @param message message
|
||||
* @param pattern pattern matching the channel (if specified) - can be null
|
||||
*/
|
||||
void onMessage(Message message, byte[] pattern);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.redis.connection;
|
||||
|
||||
|
||||
/**
|
||||
* Interface for the commands supported by Redis.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface RedisCommands extends RedisKeyCommands, RedisStringCommands, RedisListCommands, RedisSetCommands,
|
||||
RedisZSetCommands, RedisHashCommands, RedisTxCommands, RedisPubSubCommands, RedisConnectionCommands,
|
||||
RedisServerCommands {
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.redis.connection;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
|
||||
/**
|
||||
* A connection to a Redis server. Acts as an common abstraction across various
|
||||
* Redis client libraries (or drivers). Additionally performs exception translation
|
||||
* between the underlying Redis client library and Spring DAO exceptions.
|
||||
*
|
||||
* The methods follow as much as possible the Redis names and conventions.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface RedisConnection extends RedisCommands {
|
||||
|
||||
/**
|
||||
* Closes (or quits) the connection.
|
||||
*
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
void close() throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Indicates whether the underlying connection is closed or not.
|
||||
*
|
||||
* @return true if the connection is closed, false otherwise.
|
||||
*/
|
||||
boolean isClosed();
|
||||
|
||||
/**
|
||||
* Returns the native connection (the underlying library/driver object).
|
||||
*
|
||||
* @return underlying, native object
|
||||
*/
|
||||
Object getNativeConnection();
|
||||
|
||||
/**
|
||||
* Indicates whether the connection is in "queue"(or "MULTI") mode or not.
|
||||
* When queueing, all commands are postponed until EXEC or DISCARD commands
|
||||
* are issued.
|
||||
* Since in queueing no results are returned, the connection will return NULL
|
||||
* on all operations that interact with the data.
|
||||
*
|
||||
* @return true if the connection is in queue/MULTI mode, false otherwise
|
||||
*/
|
||||
boolean isQueueing();
|
||||
|
||||
/**
|
||||
* Indicates whether the connection is currently pipelined or not.
|
||||
*
|
||||
* @return true if the connection is pipelined, false otherwise
|
||||
* @see #openPipeline()
|
||||
* @see #isQueueing()
|
||||
*/
|
||||
boolean isPipelined();
|
||||
|
||||
/**
|
||||
* Activates the pipeline mode for this connection. When pipelined, all commands return null
|
||||
* (the reply is read at the end through {@link #closePipeline()}.
|
||||
* Calling this method when the connection is already pipelined has no effect.
|
||||
*
|
||||
* Pipelining is used for issuing commands without requesting the response right away but rather
|
||||
* at the end of the batch. While somewhat similar to MULTI, pipelining does not
|
||||
* guarantee atomicity - it only tries to improve performance when issuing a lot of
|
||||
* commands (such as in batching scenarios).
|
||||
*
|
||||
* <p>Note:</p>Consider doing some performance testing before using this feature since
|
||||
* in many cases the performance benefits are minimal yet the impact on usage are not.
|
||||
*
|
||||
* @see #multi()
|
||||
*/
|
||||
void openPipeline();
|
||||
|
||||
/**
|
||||
* Executes the commands in the pipeline and returns their result.
|
||||
* If the connection is not pipelined, an empty collection is returned.
|
||||
*
|
||||
* @return the result of the executed commands.
|
||||
*/
|
||||
List<Object> closePipeline();
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.redis.connection;
|
||||
|
||||
|
||||
/**
|
||||
* Connection-specific commands supported by Redis.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface RedisConnectionCommands {
|
||||
|
||||
public abstract void select(int dbIndex);
|
||||
|
||||
public abstract byte[] echo(byte[] message);
|
||||
|
||||
public abstract String ping();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.redis.connection;
|
||||
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
|
||||
/**
|
||||
* Thread-safe factory of Redis connections.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface RedisConnectionFactory extends PersistenceExceptionTranslator {
|
||||
|
||||
/**
|
||||
* Provides a suitable connection for interacting with Redis.
|
||||
*
|
||||
* @return connection for interacting with Redis.
|
||||
*/
|
||||
RedisConnection getConnection();
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.redis.connection;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Hash-specific commands supported by Redis.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface RedisHashCommands {
|
||||
|
||||
Boolean hSet(byte[] key, byte[] field, byte[] value);
|
||||
|
||||
Boolean hSetNX(byte[] key, byte[] field, byte[] value);
|
||||
|
||||
byte[] hGet(byte[] key, byte[] field);
|
||||
|
||||
List<byte[]> hMGet(byte[] key, byte[]... fields);
|
||||
|
||||
void hMSet(byte[] key, Map<byte[], byte[]> hashes);
|
||||
|
||||
Long hIncrBy(byte[] key, byte[] field, long delta);
|
||||
|
||||
Boolean hExists(byte[] key, byte[] field);
|
||||
|
||||
Boolean hDel(byte[] key, byte[] field);
|
||||
|
||||
Long hLen(byte[] key);
|
||||
|
||||
Set<byte[]> hKeys(byte[] key);
|
||||
|
||||
List<byte[]> hVals(byte[] key);
|
||||
|
||||
Map<byte[], byte[]> hGetAll(byte[] key);
|
||||
}
|
||||
@@ -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.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.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);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.redis.connection;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* List-specific commands supported by Redis.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface RedisListCommands {
|
||||
|
||||
/**
|
||||
* List insertion position.
|
||||
*/
|
||||
public enum Position {
|
||||
BEFORE, AFTER
|
||||
}
|
||||
|
||||
Long rPush(byte[] key, byte[] value);
|
||||
|
||||
Long lPush(byte[] key, byte[] value);
|
||||
|
||||
Long rPushX(byte[] key, byte[] value);
|
||||
|
||||
Long lPushX(byte[] key, byte[] value);
|
||||
|
||||
Long lLen(byte[] key);
|
||||
|
||||
List<byte[]> lRange(byte[] key, long begin, long end);
|
||||
|
||||
void lTrim(byte[] key, long begin, long end);
|
||||
|
||||
byte[] lIndex(byte[] key, long index);
|
||||
|
||||
Long lInsert(byte[] key, Position where, byte[] pivot, byte[] value);
|
||||
|
||||
void lSet(byte[] key, long index, byte[] value);
|
||||
|
||||
Long lRem(byte[] key, long count, byte[] value);
|
||||
|
||||
byte[] lPop(byte[] key);
|
||||
|
||||
byte[] rPop(byte[] key);
|
||||
|
||||
List<byte[]> bLPop(int timeout, byte[]... keys);
|
||||
|
||||
List<byte[]> bRPop(int timeout, byte[]... keys);
|
||||
|
||||
byte[] rPopLPush(byte[] srcKey, byte[] dstKey);
|
||||
|
||||
byte[] bRPopLPush(int timeout, byte[] srcKey, byte[] dstKey);
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.redis.connection;
|
||||
|
||||
/**
|
||||
* PubSub-specific Redis commands.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface RedisPubSubCommands {
|
||||
|
||||
/**
|
||||
* Indicates whether the current connection is subscribed (to at least one channel)
|
||||
* or not.
|
||||
*
|
||||
* @return true if the connection is subscribed, false otherwise
|
||||
*/
|
||||
boolean isSubscribed();
|
||||
|
||||
/**
|
||||
* Returns the current subscription for this connection or null if the connection is
|
||||
* not subscribed.
|
||||
*
|
||||
* @return the current subscription, null if none is available
|
||||
*/
|
||||
Subscription getSubscription();
|
||||
|
||||
/**
|
||||
* Publishes the given message to the given channel.
|
||||
*
|
||||
* @param channel the channel to publish to
|
||||
* @param message message to publish
|
||||
* @return the number of clients that received the message
|
||||
*/
|
||||
Long publish(byte[] channel, byte[] message);
|
||||
|
||||
/**
|
||||
* Subscribes the connection to the given channels.
|
||||
* Once subscribed, a connection
|
||||
* enters listening mode and can only subscribe to other channels or unsubscribe.
|
||||
* No other commands are accepted until the connection is unsubscribed.
|
||||
* <p/>
|
||||
* Note that this operation is blocking and the current thread starts waiting
|
||||
* for new messages immediately.
|
||||
*
|
||||
* @param listener message listener
|
||||
* @param channels channel names
|
||||
*/
|
||||
void subscribe(MessageListener listener, byte[]... channels);
|
||||
|
||||
/**
|
||||
* Subscribes the connection to all channels matching the given patterns.
|
||||
* Once subscribed, a connection
|
||||
* enters listening mode and can only subscribe to other channels or unsubscribe.
|
||||
* No other commands are accepted until the connection is unsubscribed.
|
||||
* <p/>
|
||||
* Note that this operation is blocking and the current thread starts waiting
|
||||
* for new messages immediately.
|
||||
*
|
||||
* @param listener message listener
|
||||
* @param patterns channel name patterns
|
||||
*/
|
||||
void pSubscribe(MessageListener listener, byte[]... patterns);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.redis.connection;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Server-specific commands supported by Redis.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface RedisServerCommands {
|
||||
|
||||
void bgWriteAof();
|
||||
|
||||
void bgSave();
|
||||
|
||||
Long lastSave();
|
||||
|
||||
void save();
|
||||
|
||||
Long dbSize();
|
||||
|
||||
void flushDb();
|
||||
|
||||
void flushAll();
|
||||
|
||||
Properties info();
|
||||
|
||||
void shutdown();
|
||||
|
||||
List<String> getConfig(String pattern);
|
||||
|
||||
void setConfig(String param, String value);
|
||||
|
||||
void resetConfigStats();
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.redis.connection;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Set-specific commands supported by Redis.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface RedisSetCommands {
|
||||
|
||||
Boolean sAdd(byte[] key, byte[] value);
|
||||
|
||||
Boolean sRem(byte[] key, byte[] value);
|
||||
|
||||
byte[] sPop(byte[] key);
|
||||
|
||||
Boolean sMove(byte[] srcKey, byte[] destKey, byte[] value);
|
||||
|
||||
Long sCard(byte[] key);
|
||||
|
||||
Boolean sIsMember(byte[] key, byte[] value);
|
||||
|
||||
Set<byte[]> sInter(byte[]... keys);
|
||||
|
||||
void sInterStore(byte[] destKey, byte[]... keys);
|
||||
|
||||
Set<byte[]> sUnion(byte[]... keys);
|
||||
|
||||
void sUnionStore(byte[] destKey, byte[]... keys);
|
||||
|
||||
Set<byte[]> sDiff(byte[]... keys);
|
||||
|
||||
void sDiffStore(byte[] destKey, byte[]... keys);
|
||||
|
||||
Set<byte[]> sMembers(byte[] key);
|
||||
|
||||
byte[] sRandMember(byte[] key);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.redis.connection;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* String/Value-specific commands supported by Redis.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface RedisStringCommands {
|
||||
|
||||
byte[] get(byte[] key);
|
||||
|
||||
byte[] getSet(byte[] key, byte[] value);
|
||||
|
||||
List<byte[]> mGet(byte[]... keys);
|
||||
|
||||
void set(byte[] key, byte[] value);
|
||||
|
||||
Boolean setNX(byte[] key, byte[] value);
|
||||
|
||||
void setEx(byte[] key, long seconds, byte[] value);
|
||||
|
||||
void mSet(Map<byte[], byte[]> tuple);
|
||||
|
||||
void mSetNX(Map<byte[], byte[]> tuple);
|
||||
|
||||
Long incr(byte[] key);
|
||||
|
||||
Long incrBy(byte[] key, long value);
|
||||
|
||||
Long decr(byte[] key);
|
||||
|
||||
Long decrBy(byte[] key, long value);
|
||||
|
||||
Long append(byte[] key, byte[] value);
|
||||
|
||||
byte[] getRange(byte[] key, long begin, long end);
|
||||
|
||||
void setRange(byte[] key, byte[] value, long offset);
|
||||
|
||||
Boolean getBit(byte[] key, long offset);
|
||||
|
||||
void setBit(byte[] key, long offset, boolean value);
|
||||
|
||||
Long strLen(byte[] key);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.redis.connection;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
|
||||
/**
|
||||
* Exception thrown when issuing commands on a connection that is subscribed and waiting
|
||||
* for events.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @see org.springframework.data.redis.connection.RedisPubSubCommands
|
||||
*/
|
||||
public class RedisSubscribedConnectionException extends InvalidDataAccessApiUsageException {
|
||||
|
||||
/**
|
||||
* Constructs a new <code>RedisSubscribedConnectionException</code> instance.
|
||||
*
|
||||
* @param msg
|
||||
* @param cause
|
||||
*/
|
||||
public RedisSubscribedConnectionException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>RedisSubscribedConnectionException</code> instance.
|
||||
*
|
||||
* @param msg
|
||||
*/
|
||||
public RedisSubscribedConnectionException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.redis.connection;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Transaction/Batch specific commands supported by Redis.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface RedisTxCommands {
|
||||
|
||||
void multi();
|
||||
|
||||
List<Object> exec();
|
||||
|
||||
void discard();
|
||||
|
||||
void watch(byte[]... keys);
|
||||
|
||||
void unwatch();
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.redis.connection;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* ZSet(SortedSet)-specific commands supported by Redis.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface RedisZSetCommands {
|
||||
|
||||
/**
|
||||
* Sort aggregation operations.
|
||||
*/
|
||||
public enum Aggregate {
|
||||
SUM, MIN, MAX;
|
||||
}
|
||||
|
||||
/**
|
||||
* ZSet tuple.
|
||||
*/
|
||||
public interface Tuple {
|
||||
byte[] getValue();
|
||||
|
||||
Double getScore();
|
||||
}
|
||||
|
||||
Boolean zAdd(byte[] key, double score, byte[] value);
|
||||
|
||||
Boolean zRem(byte[] key, byte[] value);
|
||||
|
||||
Double zIncrBy(byte[] key, double increment, byte[] value);
|
||||
|
||||
Long zRank(byte[] key, byte[] value);
|
||||
|
||||
Long zRevRank(byte[] key, byte[] value);
|
||||
|
||||
Set<byte[]> zRange(byte[] key, long begin, long end);
|
||||
|
||||
Set<Tuple> zRangeWithScores(byte[] key, long begin, long end);
|
||||
|
||||
Set<byte[]> zRangeByScore(byte[] key, double min, double max);
|
||||
|
||||
Set<Tuple> zRangeByScoreWithScores(byte[] key, double min, double max);
|
||||
|
||||
Set<byte[]> zRangeByScore(byte[] key, double min, double max, long offset, long count);
|
||||
|
||||
Set<Tuple> zRangeByScoreWithScores(byte[] key, double min, double max, long offset, long count);
|
||||
|
||||
Set<byte[]> zRevRange(byte[] key, long begin, long end);
|
||||
|
||||
Set<Tuple> zRevRangeWithScores(byte[] key, long begin, long end);
|
||||
|
||||
Set<byte[]> zRevRangeByScore(byte[] key, double min, double max);
|
||||
|
||||
Set<Tuple> zRevRangeByScoreWithScores(byte[] key, double min, double max);
|
||||
|
||||
Set<byte[]> zRevRangeByScore(byte[] key, double min, double max, long offset, long count);
|
||||
|
||||
Set<Tuple> zRevRangeByScoreWithScores(byte[] key, double min, double max, long offset, long count);
|
||||
|
||||
Long zCount(byte[] key, double min, double max);
|
||||
|
||||
Long zCard(byte[] key);
|
||||
|
||||
Double zScore(byte[] key, byte[] value);
|
||||
|
||||
Long zRemRange(byte[] key, long begin, long end);
|
||||
|
||||
Long zRemRangeByScore(byte[] key, double min, double max);
|
||||
|
||||
Long zUnionStore(byte[] destKey, byte[]... sets);
|
||||
|
||||
Long zUnionStore(byte[] destKey, Aggregate aggregate, int[] weights, byte[]... sets);
|
||||
|
||||
Long zInterStore(byte[] destKey, byte[]... sets);
|
||||
|
||||
Long zInterStore(byte[] destKey, Aggregate aggregate, int[] weights, byte[]... sets);
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.redis.connection;
|
||||
|
||||
/**
|
||||
* Entity containing the parameters for the SORT operation.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface SortParameters {
|
||||
|
||||
/**
|
||||
* Sorting order.
|
||||
*/
|
||||
public enum Order {
|
||||
ASC, DESC
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility class wrapping the 'LIMIT' setting.
|
||||
*
|
||||
*/
|
||||
static class Range {
|
||||
private final long start;
|
||||
private final long count;
|
||||
|
||||
public Range(long start, long count) {
|
||||
this.start = start;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public long getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public long getCount() {
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sorting order. Can be null if nothing is specified.
|
||||
*
|
||||
* @return sorting order
|
||||
*/
|
||||
Order getOrder();
|
||||
|
||||
/**
|
||||
* Indicates if the sorting is numeric (default) or alphabetical (lexicographical).
|
||||
* Can be null if nothing is specified.
|
||||
*
|
||||
* @return the type of sorting
|
||||
*/
|
||||
Boolean isAlphabetic();
|
||||
|
||||
/**
|
||||
* Returns the pattern (if set) for sorting by external keys (<tt>BY</tt>).
|
||||
* Can be null if nothing is specified.
|
||||
*
|
||||
* @return <tt>BY</tt> pattern.
|
||||
*/
|
||||
byte[] getByPattern();
|
||||
|
||||
/**
|
||||
* Returns the pattern (if set) for retrieving external keys (<tt>GET</tt>).
|
||||
* Can be null if nothing is specified.
|
||||
*
|
||||
* @return <tt>GET</tt> pattern.
|
||||
*/
|
||||
byte[][] getGetPattern();
|
||||
|
||||
/**
|
||||
* Returns the sorting limit (range or pagination).
|
||||
* Can be null if nothing is specified.
|
||||
*
|
||||
* @return sorting limit/range
|
||||
*/
|
||||
Range getLimit();
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* 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.redis.connection;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.redis.core.RedisCallback;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
|
||||
/**
|
||||
* Convenience extension of {@link RedisConnection} that accepts and returns {@link String}s instead of
|
||||
* byte arrays. Uses a {@link RedisSerializer} underneath to perform the conversion.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @see RedisCallback
|
||||
* @see RedisSerializer
|
||||
* @see StringRedisTemplate
|
||||
*/
|
||||
public interface StringRedisConnection extends RedisConnection {
|
||||
|
||||
/**
|
||||
* String-friendly ZSet tuple.
|
||||
*/
|
||||
public interface StringTuple extends Tuple {
|
||||
String getValueAsString();
|
||||
}
|
||||
|
||||
Boolean exists(String key);
|
||||
|
||||
Long del(String... keys);
|
||||
|
||||
DataType type(String key);
|
||||
|
||||
Collection<String> keys(String pattern);
|
||||
|
||||
void rename(String oldName, String newName);
|
||||
|
||||
Boolean renameNX(String oldName, String newName);
|
||||
|
||||
Boolean expire(String key, long seconds);
|
||||
|
||||
Boolean expireAt(String key, long unixTime);
|
||||
|
||||
Boolean persist(String key);
|
||||
|
||||
Boolean move(String key, int dbIndex);
|
||||
|
||||
Long ttl(String key);
|
||||
|
||||
String echo(String message);
|
||||
|
||||
// sort commands
|
||||
List<String> sort(String key, SortParameters params);
|
||||
|
||||
Long sort(String key, SortParameters params, String storeKey);
|
||||
|
||||
String get(String key);
|
||||
|
||||
String getSet(String key, String value);
|
||||
|
||||
List<String> mGet(String... keys);
|
||||
|
||||
void set(String key, String value);
|
||||
|
||||
Boolean setNX(String key, String value);
|
||||
|
||||
void setEx(String key, long seconds, String value);
|
||||
|
||||
void mSetString(Map<String, String> tuple);
|
||||
|
||||
void mSetNXString(Map<String, String> tuple);
|
||||
|
||||
Long incr(String key);
|
||||
|
||||
Long incrBy(String key, long value);
|
||||
|
||||
Long decr(String key);
|
||||
|
||||
Long decrBy(String key, long value);
|
||||
|
||||
Long append(String key, String value);
|
||||
|
||||
String getRange(String key, long start, long end);
|
||||
|
||||
void setRange(String key, String value, long offset);
|
||||
|
||||
Boolean getBit(String key, long offset);
|
||||
|
||||
void setBit(String key, long offset, boolean value);
|
||||
|
||||
Long strLen(String key);
|
||||
|
||||
Long rPush(String key, String value);
|
||||
|
||||
Long lPush(String key, String value);
|
||||
|
||||
Long rPushX(String key, String value);
|
||||
|
||||
Long lPushX(String key, String value);
|
||||
|
||||
Long lLen(String key);
|
||||
|
||||
List<String> lRange(String key, long start, long end);
|
||||
|
||||
void lTrim(String key, long start, long end);
|
||||
|
||||
String lIndex(String key, long index);
|
||||
|
||||
Long lInsert(String key, Position where, String pivot, String value);
|
||||
|
||||
void lSet(String key, long index, String value);
|
||||
|
||||
Long lRem(String key, long count, String value);
|
||||
|
||||
String lPop(String key);
|
||||
|
||||
String rPop(String key);
|
||||
|
||||
List<String> bLPop(int timeout, String... keys);
|
||||
|
||||
List<String> bRPop(int timeout, String... keys);
|
||||
|
||||
String rPopLPush(String srcKey, String dstKey);
|
||||
|
||||
String bRPopLPush(int timeout, String srcKey, String dstKey);
|
||||
|
||||
Boolean sAdd(String key, String value);
|
||||
|
||||
Boolean sRem(String key, String value);
|
||||
|
||||
String sPop(String key);
|
||||
|
||||
Boolean sMove(String srcKey, String destKey, String value);
|
||||
|
||||
Long sCard(String key);
|
||||
|
||||
Boolean sIsMember(String key, String value);
|
||||
|
||||
Set<String> sInter(String... keys);
|
||||
|
||||
void sInterStore(String destKey, String... keys);
|
||||
|
||||
Set<String> sUnion(String... keys);
|
||||
|
||||
void sUnionStore(String destKey, String... keys);
|
||||
|
||||
Set<String> sDiff(String... keys);
|
||||
|
||||
void sDiffStore(String destKey, String... keys);
|
||||
|
||||
Set<String> sMembers(String key);
|
||||
|
||||
String sRandMember(String key);
|
||||
|
||||
Boolean zAdd(String key, double score, String value);
|
||||
|
||||
Boolean zRem(String key, String value);
|
||||
|
||||
Double zIncrBy(String key, double increment, String value);
|
||||
|
||||
Long zRank(String key, String value);
|
||||
|
||||
Long zRevRank(String key, String value);
|
||||
|
||||
Set<String> zRange(String key, long start, long end);
|
||||
|
||||
Set<StringTuple> zRangeWithScores(String key, long start, long end);
|
||||
|
||||
Set<String> zRevRange(String key, long start, long end);
|
||||
|
||||
Set<StringTuple> zRevRangeWithScores(String key, long start, long end);
|
||||
|
||||
Set<String> zRangeByScore(String key, double min, double max);
|
||||
|
||||
Set<StringTuple> zRangeByScoreWithScores(String key, double min, double max);
|
||||
|
||||
Set<String> zRangeByScore(String key, double min, double max, long offset, long count);
|
||||
|
||||
Set<StringTuple> zRangeByScoreWithScores(String key, double min, double max, long offset, long count);
|
||||
|
||||
Long zCount(String key, double min, double max);
|
||||
|
||||
Long zCard(String key);
|
||||
|
||||
Double zScore(String key, String value);
|
||||
|
||||
Long zRemRange(String key, long start, long end);
|
||||
|
||||
Long zRemRangeByScore(String key, double min, double max);
|
||||
|
||||
Long zUnionStore(String destKey, String... sets);
|
||||
|
||||
Long zUnionStore(String destKey, Aggregate aggregate, int[] weights, String... sets);
|
||||
|
||||
Long zInterStore(String destKey, String... sets);
|
||||
|
||||
Long zInterStore(String destKey, Aggregate aggregate, int[] weights, String... sets);
|
||||
|
||||
Boolean hSet(String key, String field, String value);
|
||||
|
||||
Boolean hSetNX(String key, String field, String value);
|
||||
|
||||
String hGet(String key, String field);
|
||||
|
||||
List<String> hMGet(String key, String... fields);
|
||||
|
||||
void hMSet(String key, Map<String, String> hashes);
|
||||
|
||||
Long hIncrBy(String key, String field, long delta);
|
||||
|
||||
Boolean hExists(String key, String field);
|
||||
|
||||
Boolean hDel(String key, String field);
|
||||
|
||||
Long hLen(String key);
|
||||
|
||||
Set<String> hKeys(String key);
|
||||
|
||||
List<String> hVals(String key);
|
||||
|
||||
Map<String, String> hGetAll(String key);
|
||||
|
||||
Long publish(String channel, String message);
|
||||
|
||||
void subscribe(MessageListener listener, String... channels);
|
||||
|
||||
void pSubscribe(MessageListener listener, String... patterns);
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.redis.connection;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public interface Subscription {
|
||||
|
||||
/**
|
||||
* Adds the given channels to the current subscription.
|
||||
*
|
||||
* @param channels channel names
|
||||
*/
|
||||
void subscribe(byte[]... channels) throws RedisInvalidSubscriptionException;
|
||||
|
||||
/**
|
||||
* Adds the given channel patterns to the current subscription.
|
||||
*
|
||||
* @param patterns channel patterns
|
||||
*/
|
||||
void pSubscribe(byte[]... patterns) throws RedisInvalidSubscriptionException;
|
||||
|
||||
/**
|
||||
* Cancels the current subscription for all channels given by name.
|
||||
*/
|
||||
void unsubscribe();
|
||||
|
||||
/**
|
||||
* Cancels the current subscription for all given channels.
|
||||
*
|
||||
* @param channels channel names
|
||||
*/
|
||||
void unsubscribe(byte[]... channels);
|
||||
|
||||
/**
|
||||
* Cancels the subscription for all channels matched by patterns.
|
||||
*/
|
||||
void pUnsubscribe();
|
||||
|
||||
/**
|
||||
* Cancels the subscription for all channels matching the given patterns.
|
||||
*
|
||||
* @param patterns
|
||||
*/
|
||||
void pUnsubscribe(byte[]... patterns);
|
||||
|
||||
/**
|
||||
* Returns the (named) channels for this subscription.
|
||||
*
|
||||
* @return collection of named channels
|
||||
*/
|
||||
Collection<byte[]> getChannels();
|
||||
|
||||
/**
|
||||
* Returns the channel patters for this subscription.
|
||||
*
|
||||
* @return collection of channel patterns
|
||||
*/
|
||||
Collection<byte[]> getPatterns();
|
||||
|
||||
/**
|
||||
* Returns the listener used for this subscription.
|
||||
*
|
||||
* @return the listener used for this subscription.
|
||||
*/
|
||||
MessageListener getListener();
|
||||
|
||||
/**
|
||||
* Indicates whether this subscription is still 'alive'
|
||||
* or not.
|
||||
*
|
||||
* @return true if the subscription still applies, false otherwise.
|
||||
*/
|
||||
boolean isAlive();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* 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.redis.connection.jedis;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.JedisPool;
|
||||
import redis.clients.jedis.JedisPoolConfig;
|
||||
import redis.clients.jedis.JedisShardInfo;
|
||||
import redis.clients.jedis.Protocol;
|
||||
|
||||
/**
|
||||
* Connection factory creating <a href="http://github.com/xetorthio/jedis">Jedis</a> based connections.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class JedisConnectionFactory implements InitializingBean, DisposableBean, RedisConnectionFactory {
|
||||
|
||||
private final static Log log = LogFactory.getLog(JedisConnectionFactory.class);
|
||||
|
||||
private JedisShardInfo shardInfo;
|
||||
private String hostName = "localhost";
|
||||
private int port = Protocol.DEFAULT_PORT;
|
||||
private int timeout = Protocol.DEFAULT_TIMEOUT;
|
||||
private String password;
|
||||
|
||||
private boolean usePool = true;
|
||||
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).
|
||||
*/
|
||||
public JedisConnectionFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>JedisConnectionFactory</code> instance.
|
||||
* Will override the other connection parameters passed to the factory.
|
||||
*
|
||||
* @param shardInfo shard information
|
||||
*/
|
||||
public JedisConnectionFactory(JedisShardInfo shardInfo) {
|
||||
this.shardInfo = shardInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>JedisConnectionFactory</code> instance using
|
||||
* the given pool configuration.
|
||||
*
|
||||
* @param poolConfig pool configuration
|
||||
*/
|
||||
public JedisConnectionFactory(JedisPoolConfig poolConfig) {
|
||||
this.poolConfig = poolConfig;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a Jedis instance to be used as a Redis connection.
|
||||
* The instance can be newly created or retrieved from a pool.
|
||||
*
|
||||
* @return Jedis instance ready for wrapping into a {@link RedisConnection}.
|
||||
*/
|
||||
protected Jedis fetchJedisConnector() {
|
||||
try {
|
||||
if (usePool && pool != null) {
|
||||
return pool.getResource();
|
||||
}
|
||||
Jedis jedis = new Jedis(getShardInfo());
|
||||
// force initialization (see Jedis issue #82)
|
||||
jedis.connect();
|
||||
return jedis;
|
||||
} catch (Exception ex) {
|
||||
throw new DataAccessResourceFailureException("Cannot get Jedis connection", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
if (StringUtils.hasLength(password)) {
|
||||
shardInfo.setPassword(password);
|
||||
}
|
||||
|
||||
if (timeout > 0) {
|
||||
shardInfo.setTimeout(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
if (usePool) {
|
||||
pool = new JedisPool(poolConfig, shardInfo.getHost(), shardInfo.getPort(), shardInfo.getTimeout(),
|
||||
shardInfo.getPassword());
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
if (usePool && pool != null) {
|
||||
try {
|
||||
pool.destroy();
|
||||
} catch (Exception ex) {
|
||||
log.warn("Cannot properly close Jedis pool", ex);
|
||||
}
|
||||
pool = null;
|
||||
}
|
||||
}
|
||||
|
||||
public JedisConnection getConnection() {
|
||||
Jedis jedis = fetchJedisConnector();
|
||||
return postProcessConnection((usePool ? new JedisConnection(jedis, pool, dbIndex) : new JedisConnection(jedis,
|
||||
null, dbIndex)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
|
||||
return JedisUtils.convertJedisAccessException(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 shardInfo.
|
||||
*
|
||||
* @return Returns the shardInfo
|
||||
*/
|
||||
public JedisShardInfo getShardInfo() {
|
||||
return shardInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the shard info for this factory.
|
||||
*
|
||||
* @param shardInfo The shardInfo to set.
|
||||
*/
|
||||
public void setShardInfo(JedisShardInfo shardInfo) {
|
||||
this.shardInfo = shardInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 poolConfig.
|
||||
*
|
||||
* @return Returns the poolConfig
|
||||
*/
|
||||
public JedisPoolConfig getPoolConfig() {
|
||||
return poolConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pool configuration for this factory.
|
||||
*
|
||||
* @param poolConfig The poolConfig to set.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.redis.connection.jedis;
|
||||
|
||||
import org.springframework.data.redis.connection.DefaultMessage;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import redis.clients.jedis.BinaryJedisPubSub;
|
||||
|
||||
/**
|
||||
* MessageListener adapter on top of Jedis.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class JedisMessageListener extends BinaryJedisPubSub {
|
||||
|
||||
private final MessageListener listener;
|
||||
|
||||
JedisMessageListener(MessageListener listener) {
|
||||
Assert.notNull(listener, "message listener is required");
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(byte[] channel, byte[] message) {
|
||||
listener.onMessage(new DefaultMessage(channel, message), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPMessage(byte[] pattern, byte[] channel, byte[] message) {
|
||||
listener.onMessage(new DefaultMessage(channel, message), pattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPSubscribe(byte[] pattern, int subscribedChannels) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPUnsubscribe(byte[] pattern, int subscribedChannels) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubscribe(byte[] channel, int subscribedChannels) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnsubscribe(byte[] channel, int subscribedChannels) {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.redis.connection.jedis;
|
||||
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.data.redis.connection.util.AbstractSubscription;
|
||||
|
||||
import redis.clients.jedis.BinaryJedisPubSub;
|
||||
|
||||
/**
|
||||
* Jedis specific subscription.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class JedisSubscription extends AbstractSubscription {
|
||||
|
||||
private final BinaryJedisPubSub jedisPubSub;
|
||||
|
||||
JedisSubscription(MessageListener listener, BinaryJedisPubSub jedisPubSub, byte[][] channels, byte[][] patterns) {
|
||||
super(listener, channels, patterns);
|
||||
this.jedisPubSub = jedisPubSub;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doClose() {
|
||||
jedisPubSub.unsubscribe();
|
||||
jedisPubSub.punsubscribe();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPsubscribe(byte[]... patterns) {
|
||||
jedisPubSub.psubscribe(patterns);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPUnsubscribe(boolean all, byte[]... patterns) {
|
||||
if (all) {
|
||||
jedisPubSub.punsubscribe();
|
||||
}
|
||||
else {
|
||||
jedisPubSub.punsubscribe(patterns);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSubscribe(byte[]... channels) {
|
||||
jedisPubSub.subscribe(channels);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doUnsubscribe(boolean all, byte[]... channels) {
|
||||
if (all) {
|
||||
jedisPubSub.unsubscribe();
|
||||
}
|
||||
else {
|
||||
jedisPubSub.unsubscribe(channels);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* 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.redis.connection.jedis;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
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 java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.redis.RedisConnectionFailureException;
|
||||
import org.springframework.data.redis.RedisSystemException;
|
||||
import org.springframework.data.redis.connection.DefaultTuple;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.data.redis.connection.SortParameters;
|
||||
import org.springframework.data.redis.connection.RedisListCommands.Position;
|
||||
import org.springframework.data.redis.connection.RedisZSetCommands.Tuple;
|
||||
import org.springframework.data.redis.connection.SortParameters.Order;
|
||||
import org.springframework.data.redis.connection.SortParameters.Range;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import redis.clients.jedis.BinaryJedisPubSub;
|
||||
import redis.clients.jedis.Protocol;
|
||||
import redis.clients.jedis.SortingParams;
|
||||
import redis.clients.jedis.BinaryClient.LIST_POSITION;
|
||||
import redis.clients.jedis.exceptions.JedisConnectionException;
|
||||
import redis.clients.jedis.exceptions.JedisDataException;
|
||||
import redis.clients.jedis.exceptions.JedisException;
|
||||
|
||||
/**
|
||||
* Helper class featuring methods for Jedis connection handling, providing support for exception translation.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
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[] { 1 };
|
||||
private static final byte[] ZERO = new byte[] { 0 };
|
||||
|
||||
/**
|
||||
* Converts the given, native Jedis exception to Spring's DAO hierarchy.
|
||||
*
|
||||
* @param ex Jedis exception
|
||||
* @return converted exception
|
||||
*/
|
||||
public static DataAccessException convertJedisAccessException(JedisException ex) {
|
||||
if (ex instanceof JedisDataException) {
|
||||
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof JedisConnectionException) {
|
||||
return new RedisConnectionFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
// fallback to invalid data exception
|
||||
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given, native, runtime Jedis exception to Spring's DAO hierarchy.
|
||||
*
|
||||
* @param ex Jedis runtime/unchecked exception
|
||||
* @return converted exception
|
||||
*/
|
||||
public static DataAccessException convertJedisAccessException(RuntimeException ex) {
|
||||
if (ex instanceof JedisException) {
|
||||
return convertJedisAccessException((JedisException) ex);
|
||||
}
|
||||
|
||||
return new RedisSystemException("Unknown exception", ex);
|
||||
}
|
||||
|
||||
static DataAccessException convertJedisAccessException(IOException ex) {
|
||||
if (ex instanceof UnknownHostException) {
|
||||
return new RedisConnectionFailureException("Unknown host " + ex.getMessage(), ex);
|
||||
}
|
||||
return new RedisConnectionFailureException("Could not connect to Redis server", ex);
|
||||
}
|
||||
|
||||
static DataAccessException convertJedisAccessException(TimeoutException ex) {
|
||||
throw new RedisConnectionFailureException("Jedis pool timed out. Could not get Redis Connection", ex);
|
||||
}
|
||||
|
||||
static boolean isStatusOk(String status) {
|
||||
return status != null && (OK_CODE.equals(status) || OK_MULTI_CODE.equals(status));
|
||||
}
|
||||
|
||||
static Boolean convertCodeReply(Number code) {
|
||||
return (code != null ? code.intValue() == 1 : null);
|
||||
}
|
||||
|
||||
static Set<Tuple> convertJedisTuple(Set<redis.clients.jedis.Tuple> tuples) {
|
||||
Set<Tuple> value = new LinkedHashSet<Tuple>(tuples.size());
|
||||
for (redis.clients.jedis.Tuple tuple : tuples) {
|
||||
value.add(new DefaultTuple(tuple.getBinaryElement(), tuple.getScore()));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static byte[][] convert(Map<byte[], byte[]> hgetAll) {
|
||||
byte[][] result = new byte[hgetAll.size() * 2][];
|
||||
|
||||
int index = 0;
|
||||
for (Map.Entry<byte[], byte[]> entry : hgetAll.entrySet()) {
|
||||
result[index++] = entry.getKey();
|
||||
result[index++] = entry.getValue();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static Map<String, String> convert(String[] fields, String[] values) {
|
||||
Map<String, String> result = new LinkedHashMap<String, String>(fields.length);
|
||||
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
result.put(fields[i], values[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static String[] arrange(String[] keys, String[] values) {
|
||||
String[] result = new String[keys.length * 2];
|
||||
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
int index = i << 1;
|
||||
result[index] = keys[i];
|
||||
result[index + 1] = values[i];
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static SortingParams convertSortParams(SortParameters params) {
|
||||
SortingParams jedisParams = null;
|
||||
|
||||
if (params != null) {
|
||||
jedisParams = new SortingParams();
|
||||
|
||||
byte[] byPattern = params.getByPattern();
|
||||
if (byPattern != null) {
|
||||
jedisParams.by(params.getByPattern());
|
||||
}
|
||||
|
||||
byte[][] getPattern = params.getGetPattern();
|
||||
if (getPattern != null) {
|
||||
jedisParams.get(getPattern);
|
||||
}
|
||||
|
||||
Range limit = params.getLimit();
|
||||
if (limit != null) {
|
||||
jedisParams.limit((int) limit.getStart(), (int) limit.getCount());
|
||||
}
|
||||
Order order = params.getOrder();
|
||||
if (order != null && order.equals(Order.DESC)) {
|
||||
jedisParams.desc();
|
||||
}
|
||||
Boolean isAlpha = params.isAlphabetic();
|
||||
if (isAlpha != null && isAlpha) {
|
||||
jedisParams.alpha();
|
||||
}
|
||||
}
|
||||
|
||||
return jedisParams;
|
||||
}
|
||||
|
||||
static byte[] asBit(boolean value) {
|
||||
return (value ? ONE : ZERO);
|
||||
}
|
||||
|
||||
static LIST_POSITION convertPosition(Position where) {
|
||||
Assert.notNull("list positions are mandatory");
|
||||
return (Position.AFTER.equals(where) ? LIST_POSITION.AFTER : LIST_POSITION.BEFORE);
|
||||
}
|
||||
|
||||
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 BinaryJedisPubSub adaptPubSub(MessageListener listener) {
|
||||
return new JedisMessageListener(listener);
|
||||
}
|
||||
|
||||
static String[] convert(byte[]... raw) {
|
||||
String[] result = new String[raw.length];
|
||||
|
||||
for (int i = 0; i < raw.length; i++) {
|
||||
result[i] = new String(raw[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static byte[][] bXPopArgs(int timeout, byte[]... keys) {
|
||||
final List<byte[]> args = new ArrayList<byte[]>();
|
||||
for (final byte[] arg : keys) {
|
||||
args.add(arg);
|
||||
}
|
||||
args.add(Protocol.toByteArray(timeout));
|
||||
return args.toArray(new byte[args.size()][]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Connection package for <a href="http://github.com/xetorthio/jedis">Jedis</a> library.
|
||||
*/
|
||||
package org.springframework.data.redis.connection.jedis;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* 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.redis.connection.jredis;
|
||||
|
||||
import org.jredis.ClientRuntimeException;
|
||||
import org.jredis.connector.Connection;
|
||||
import org.jredis.connector.ConnectionSpec;
|
||||
import org.jredis.connector.Connection.Socket.Property;
|
||||
import org.jredis.ri.alphazero.JRedisClient;
|
||||
import org.jredis.ri.alphazero.JRedisService;
|
||||
import org.jredis.ri.alphazero.connection.DefaultConnectionSpec;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Connection factory using creating <a href="http://github.com/alphazero/jredis">JRedis</a> based connections.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class JredisConnectionFactory implements InitializingBean, DisposableBean, RedisConnectionFactory {
|
||||
|
||||
private ConnectionSpec connectionSpec;
|
||||
|
||||
private String hostName = "localhost";
|
||||
private int port = DEFAULT_REDIS_PORT;
|
||||
private String password = null;
|
||||
private int timeout;
|
||||
|
||||
private boolean usePool = true;
|
||||
private int dbIndex = DEFAULT_REDIS_DB;
|
||||
|
||||
private JRedisService pool = null;
|
||||
// taken from JRedis code
|
||||
private int poolSize = 5;
|
||||
|
||||
private static final int DEFAULT_REDIS_PORT = 6379;
|
||||
private static final int DEFAULT_REDIS_DB = 0;
|
||||
private static final byte[] DEFAULT_REDIS_PASSWORD = null;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new <code>JredisConnectionFactory</code> instance.
|
||||
*/
|
||||
public JredisConnectionFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>JredisConnectionFactory</code> instance.
|
||||
* Will override the other connection parameters passed to the factory.
|
||||
*
|
||||
* @param connectionSpec already configured connection.
|
||||
*/
|
||||
public JredisConnectionFactory(ConnectionSpec connectionSpec) {
|
||||
this.connectionSpec = connectionSpec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
if (connectionSpec == null) {
|
||||
Assert.hasText(hostName);
|
||||
connectionSpec = DefaultConnectionSpec.newSpec(hostName, port, dbIndex, DEFAULT_REDIS_PASSWORD);
|
||||
connectionSpec.setConnectionFlag(Connection.Flag.RELIABLE, false);
|
||||
|
||||
if (StringUtils.hasLength(password)) {
|
||||
connectionSpec.setCredentials(password);
|
||||
}
|
||||
|
||||
if (timeout > 0) {
|
||||
connectionSpec.setSocketProperty(Property.SO_TIMEOUT, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
if (usePool) {
|
||||
int size = getPoolSize();
|
||||
pool = new JRedisService(connectionSpec, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
if (usePool && pool != null) {
|
||||
pool.quit();
|
||||
pool = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public RedisConnection getConnection() {
|
||||
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) {
|
||||
return JredisUtils.convertJredisAccessException((ClientRuntimeException) ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the Redis host name of this factory.
|
||||
*
|
||||
* @return Returns the hostName
|
||||
*/
|
||||
public String getHostName() {
|
||||
return hostName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Redis host name for this factory.
|
||||
*
|
||||
* @param hostName The hostName to set.
|
||||
*/
|
||||
public void setHostName(String hostName) {
|
||||
this.hostName = hostName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the Redis port.
|
||||
*
|
||||
* @return Returns the port
|
||||
*/
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Redis port.
|
||||
*
|
||||
* @param port The port to set.
|
||||
*/
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 pool size of this factory.
|
||||
*
|
||||
* @return Returns the poolSize
|
||||
*/
|
||||
public int getPoolSize() {
|
||||
return poolSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the connection pool size of the underlying factory.
|
||||
*
|
||||
* @param poolSize The poolSize to set.
|
||||
*/
|
||||
public void setPoolSize(int poolSize) {
|
||||
Assert.isTrue(poolSize > 0, "pool size needs to be bigger then zero");
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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.redis.connection.jredis;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jredis.ClientRuntimeException;
|
||||
import org.jredis.RedisException;
|
||||
import org.jredis.RedisType;
|
||||
import org.jredis.Sort;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
import org.springframework.data.redis.connection.DataType;
|
||||
import org.springframework.data.redis.connection.SortParameters;
|
||||
import org.springframework.data.redis.connection.SortParameters.Order;
|
||||
import org.springframework.data.redis.connection.SortParameters.Range;
|
||||
import org.springframework.data.redis.connection.util.DecodeUtils;
|
||||
|
||||
/**
|
||||
* Helper class featuring methods for JRedis connection handling, providing support for exception translation.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public abstract class JredisUtils {
|
||||
|
||||
/**
|
||||
* Converts the given, native JRedis exception to Spring's DAO hierarchy.
|
||||
*
|
||||
* @param ex JRedis exception
|
||||
* @return converted exception
|
||||
*/
|
||||
public static DataAccessException convertJredisAccessException(RedisException ex) {
|
||||
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given, native JRedis exception to Spring's DAO hierarchy.
|
||||
*
|
||||
* @param ex JRedis exception
|
||||
* @return converted exception
|
||||
*/
|
||||
public static DataAccessException convertJredisAccessException(ClientRuntimeException ex) {
|
||||
return new InvalidDataAccessResourceUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
static DataType convertDataType(RedisType type) {
|
||||
switch (type) {
|
||||
case NONE:
|
||||
return DataType.NONE;
|
||||
case string:
|
||||
return DataType.STRING;
|
||||
case list:
|
||||
return DataType.LIST;
|
||||
case set:
|
||||
return DataType.SET;
|
||||
//case zset:
|
||||
// return DataType.ZSET;
|
||||
case hash:
|
||||
return DataType.HASH;
|
||||
}
|
||||
|
||||
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 Map<byte[], byte[]> encodeMap(Map<String, byte[]> map) {
|
||||
return DecodeUtils.encodeMap(map);
|
||||
}
|
||||
|
||||
static Map<String, byte[]> decodeMap(Map<byte[], byte[]> tuple) {
|
||||
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) {
|
||||
byte[] byPattern = params.getByPattern();
|
||||
if (byPattern != null) {
|
||||
jredisSort.BY(decode(byPattern));
|
||||
}
|
||||
byte[][] getPattern = params.getGetPattern();
|
||||
|
||||
if (getPattern != null && getPattern.length > 0) {
|
||||
for (byte[] bs : getPattern) {
|
||||
jredisSort.GET(decode(bs));
|
||||
}
|
||||
}
|
||||
Range limit = params.getLimit();
|
||||
if (limit != null) {
|
||||
jredisSort.LIMIT(limit.getStart(), limit.getCount());
|
||||
}
|
||||
Order order = params.getOrder();
|
||||
if (order != null && order.equals(Order.DESC)) {
|
||||
jredisSort.DESC();
|
||||
}
|
||||
Boolean isAlpha = params.isAlphabetic();
|
||||
if (isAlpha != null && isAlpha) {
|
||||
jredisSort.ALPHA();
|
||||
}
|
||||
}
|
||||
|
||||
if (storeKey != null) {
|
||||
jredisSort.STORE(decode(storeKey));
|
||||
}
|
||||
|
||||
|
||||
return jredisSort;
|
||||
}
|
||||
|
||||
static Properties info(Map<String, String> map) {
|
||||
Properties info = new Properties();
|
||||
info.putAll(map);
|
||||
return info;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Connection package for <a href="http://github.com/alphazero/jredis">JRedis</a> library.
|
||||
*/
|
||||
package org.springframework.data.redis.connection.jredis;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Connection package providing low-level abstractions for interacting with
|
||||
* the various Redis 'drivers'/libraries.
|
||||
*
|
||||
* <p/>Performs exception translation between the underlying library exceptions to Spring's DAO hierarchy.
|
||||
*/
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
@@ -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.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.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.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.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.redis.connection.rjc;
|
||||
|
||||
import org.idevlab.rjc.message.MessageListener;
|
||||
import org.idevlab.rjc.message.PMessageListener;
|
||||
import org.springframework.data.redis.connection.DefaultMessage;
|
||||
|
||||
/**
|
||||
* Message listener adapter for RJC library.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class RjcMessageListener implements MessageListener, PMessageListener {
|
||||
|
||||
private final org.springframework.data.redis.connection.MessageListener listener;
|
||||
|
||||
RjcMessageListener(org.springframework.data.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.redis.connection.rjc;
|
||||
|
||||
import org.idevlab.rjc.message.RedisNodeSubscriber;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.data.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.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.redis.RedisSystemException;
|
||||
import org.springframework.data.redis.connection.DataType;
|
||||
import org.springframework.data.redis.connection.DefaultTuple;
|
||||
import org.springframework.data.redis.connection.SortParameters;
|
||||
import org.springframework.data.redis.connection.RedisListCommands.Position;
|
||||
import org.springframework.data.redis.connection.RedisZSetCommands.Aggregate;
|
||||
import org.springframework.data.redis.connection.RedisZSetCommands.Tuple;
|
||||
import org.springframework.data.redis.connection.SortParameters.Order;
|
||||
import org.springframework.data.redis.connection.SortParameters.Range;
|
||||
import org.springframework.data.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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.redis.connection.rjc;
|
||||
|
||||
import org.idevlab.rjc.ds.DataSource;
|
||||
import org.idevlab.rjc.ds.RedisConnection;
|
||||
|
||||
/**
|
||||
* Basic data source that always returns the same connection.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class SingleDataSource implements DataSource {
|
||||
|
||||
private final RedisConnection connection;
|
||||
|
||||
SingleDataSource(RedisConnection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
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.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.redis.connection.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.data.redis.connection.RedisInvalidSubscriptionException;
|
||||
import org.springframework.data.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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,570 @@
|
||||
package org.springframework.data.redis.connection.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* A very fast and memory efficient class to encode and decode to and from BASE64 in full accordance
|
||||
* with RFC 2045.<br><br>
|
||||
* On Windows XP sp1 with 1.4.2_04 and later ;), this encoder and decoder is about 10 times faster
|
||||
* on small arrays (10 - 1000 bytes) and 2-3 times as fast on larger arrays (10000 - 1000000 bytes)
|
||||
* compared to <code>sun.misc.Encoder()/Decoder()</code>.<br><br>
|
||||
*
|
||||
* On byte arrays the encoder is about 20% faster than Jakarta Commons Base64 Codec for encode and
|
||||
* about 50% faster for decoding large arrays. This implementation is about twice as fast on very small
|
||||
* arrays (< 30 bytes). If source/destination is a <code>String</code> this
|
||||
* version is about three times as fast due to the fact that the Commons Codec result has to be recoded
|
||||
* to a <code>String</code> from <code>byte[]</code>, which is very expensive.<br><br>
|
||||
*
|
||||
* This encode/decode algorithm doesn't create any temporary arrays as many other codecs do, it only
|
||||
* allocates the resulting array. This produces less garbage and it is possible to handle arrays twice
|
||||
* as large as algorithms that create a temporary array. (E.g. Jakarta Commons Codec). It is unknown
|
||||
* whether Sun's <code>sun.misc.Encoder()/Decoder()</code> produce temporary arrays but since performance
|
||||
* is quite low it probably does.<br><br>
|
||||
*
|
||||
* The encoder produces the same output as the Sun one except that the Sun's encoder appends
|
||||
* a trailing line separator if the last character isn't a pad. Unclear why but it only adds to the
|
||||
* length and is probably a side effect. Both are in conformance with RFC 2045 though.<br>
|
||||
* Commons codec seem to always att a trailing line separator.<br><br>
|
||||
*
|
||||
* <b>Note!</b>
|
||||
* The encode/decode method pairs (types) come in three versions with the <b>exact</b> same algorithm and
|
||||
* thus a lot of code redundancy. This is to not create any temporary arrays for transcoding to/from different
|
||||
* format types. The methods not used can simply be commented out.<br><br>
|
||||
*
|
||||
* There is also a "fast" version of all decode methods that works the same way as the normal ones, but
|
||||
* har a few demands on the decoded input. Normally though, these fast verions should be used if the source if
|
||||
* the input is known and it hasn't bee tampered with.<br><br>
|
||||
*
|
||||
* If you find the code useful or you find a bug, please send me a note at base64 @ miginfocom . com.
|
||||
*
|
||||
* Licence (BSD):
|
||||
* ==============
|
||||
*
|
||||
* Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (base64 @ miginfocom . com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright notice, this list
|
||||
* of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||
* list of conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
* Neither the name of the MiG InfoCom AB nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* @version 2.2
|
||||
* @author Mikael Grev
|
||||
* Date: 2004-aug-02
|
||||
* Time: 11:31:11
|
||||
*/
|
||||
|
||||
class Base64 {
|
||||
private static final char[] CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
|
||||
private static final int[] IA = new int[256];
|
||||
static {
|
||||
Arrays.fill(IA, -1);
|
||||
for (int i = 0, iS = CA.length; i < iS; i++)
|
||||
IA[CA[i]] = i;
|
||||
IA['='] = 0;
|
||||
}
|
||||
|
||||
// ****************************************************************************************
|
||||
// * char[] version
|
||||
// ****************************************************************************************
|
||||
|
||||
/** Encodes a raw byte array into a BASE64 <code>char[]</code> representation i accordance with RFC 2045.
|
||||
* @param sArr The bytes to convert. If <code>null</code> or length 0 an empty array will be returned.
|
||||
* @param lineSep Optional "\r\n" after 76 characters, unless end of file.<br>
|
||||
* No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a
|
||||
* little faster.
|
||||
* @return A BASE64 encoded array. Never <code>null</code>.
|
||||
*/
|
||||
public final static char[] encodeToChar(byte[] sArr, boolean lineSep) {
|
||||
// Check special case
|
||||
int sLen = sArr != null ? sArr.length : 0;
|
||||
if (sLen == 0)
|
||||
return new char[0];
|
||||
|
||||
int eLen = (sLen / 3) * 3; // Length of even 24-bits.
|
||||
int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count
|
||||
int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array
|
||||
char[] dArr = new char[dLen];
|
||||
|
||||
// Encode even 24-bits
|
||||
for (int s = 0, d = 0, cc = 0; s < eLen;) {
|
||||
// Copy next three bytes into lower 24 bits of int, paying attension to sign.
|
||||
int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff);
|
||||
|
||||
// Encode the int into four chars
|
||||
dArr[d++] = CA[(i >>> 18) & 0x3f];
|
||||
dArr[d++] = CA[(i >>> 12) & 0x3f];
|
||||
dArr[d++] = CA[(i >>> 6) & 0x3f];
|
||||
dArr[d++] = CA[i & 0x3f];
|
||||
|
||||
// Add optional line separator
|
||||
if (lineSep && ++cc == 19 && d < dLen - 2) {
|
||||
dArr[d++] = '\r';
|
||||
dArr[d++] = '\n';
|
||||
cc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Pad and encode last bits if source isn't even 24 bits.
|
||||
int left = sLen - eLen; // 0 - 2.
|
||||
if (left > 0) {
|
||||
// Prepare the int
|
||||
int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0);
|
||||
|
||||
// Set last four chars
|
||||
dArr[dLen - 4] = CA[i >> 12];
|
||||
dArr[dLen - 3] = CA[(i >>> 6) & 0x3f];
|
||||
dArr[dLen - 2] = left == 2 ? CA[i & 0x3f] : '=';
|
||||
dArr[dLen - 1] = '=';
|
||||
}
|
||||
return dArr;
|
||||
}
|
||||
|
||||
/** Decodes a BASE64 encoded char array. All illegal characters will be ignored and can handle both arrays with
|
||||
* and without line separators.
|
||||
* @param sArr The source array. <code>null</code> or length 0 will return an empty array.
|
||||
* @return The decoded array of bytes. May be of length 0. Will be <code>null</code> if the legal characters
|
||||
* (including '=') isn't divideable by 4. (I.e. definitely corrupted).
|
||||
*/
|
||||
public final static byte[] decode(char[] sArr) {
|
||||
// Check special case
|
||||
int sLen = sArr != null ? sArr.length : 0;
|
||||
if (sLen == 0)
|
||||
return new byte[0];
|
||||
|
||||
// Count illegal characters (including '\r', '\n') to know what size the returned array will be,
|
||||
// so we don't have to reallocate & copy it later.
|
||||
int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...)
|
||||
for (int i = 0; i < sLen; i++)
|
||||
// If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out.
|
||||
if (IA[sArr[i]] < 0)
|
||||
sepCnt++;
|
||||
|
||||
// Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045.
|
||||
if ((sLen - sepCnt) % 4 != 0)
|
||||
return null;
|
||||
|
||||
int pad = 0;
|
||||
for (int i = sLen; i > 1 && IA[sArr[--i]] <= 0;)
|
||||
if (sArr[i] == '=')
|
||||
pad++;
|
||||
|
||||
int len = ((sLen - sepCnt) * 6 >> 3) - pad;
|
||||
|
||||
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
|
||||
|
||||
for (int s = 0, d = 0; d < len;) {
|
||||
// Assemble three bytes into an int from four "valid" characters.
|
||||
int i = 0;
|
||||
for (int j = 0; j < 4; j++) { // j only increased if a valid char was found.
|
||||
int c = IA[sArr[s++]];
|
||||
if (c >= 0)
|
||||
i |= c << (18 - j * 6);
|
||||
else
|
||||
j--;
|
||||
}
|
||||
// Add the bytes
|
||||
dArr[d++] = (byte) (i >> 16);
|
||||
if (d < len) {
|
||||
dArr[d++] = (byte) (i >> 8);
|
||||
if (d < len)
|
||||
dArr[d++] = (byte) i;
|
||||
}
|
||||
}
|
||||
return dArr;
|
||||
}
|
||||
|
||||
/** Decodes a BASE64 encoded char array that is known to be resonably well formatted. The method is about twice as
|
||||
* fast as {@link #decode(char[])}. The preconditions are:<br>
|
||||
* + The array must have a line length of 76 chars OR no line separators at all (one line).<br>
|
||||
* + Line separator must be "\r\n", as specified in RFC 2045
|
||||
* + The array must not contain illegal characters within the encoded string<br>
|
||||
* + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>
|
||||
* @param sArr The source array. Length 0 will return an empty array. <code>null</code> will throw an exception.
|
||||
* @return The decoded array of bytes. May be of length 0.
|
||||
*/
|
||||
public final static byte[] decodeFast(char[] sArr) {
|
||||
// Check special case
|
||||
int sLen = sArr.length;
|
||||
if (sLen == 0)
|
||||
return new byte[0];
|
||||
|
||||
int sIx = 0, eIx = sLen - 1; // Start and end index after trimming.
|
||||
|
||||
// Trim illegal chars from start
|
||||
while (sIx < eIx && IA[sArr[sIx]] < 0)
|
||||
sIx++;
|
||||
|
||||
// Trim illegal chars from end
|
||||
while (eIx > 0 && IA[sArr[eIx]] < 0)
|
||||
eIx--;
|
||||
|
||||
// get the padding count (=) (0, 1 or 2)
|
||||
int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end.
|
||||
int cCnt = eIx - sIx + 1; // Content count including possible separators
|
||||
int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0;
|
||||
|
||||
int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
|
||||
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
|
||||
|
||||
// Decode all but the last 0 - 2 bytes.
|
||||
int d = 0;
|
||||
for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) {
|
||||
// Assemble three bytes into an int from four "valid" characters.
|
||||
int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]];
|
||||
|
||||
// Add the bytes
|
||||
dArr[d++] = (byte) (i >> 16);
|
||||
dArr[d++] = (byte) (i >> 8);
|
||||
dArr[d++] = (byte) i;
|
||||
|
||||
// If line separator, jump over it.
|
||||
if (sepCnt > 0 && ++cc == 19) {
|
||||
sIx += 2;
|
||||
cc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (d < len) {
|
||||
// Decode last 1-3 bytes (incl '=') into 1-3 bytes
|
||||
int i = 0;
|
||||
for (int j = 0; sIx <= eIx - pad; j++)
|
||||
i |= IA[sArr[sIx++]] << (18 - j * 6);
|
||||
|
||||
for (int r = 16; d < len; r -= 8)
|
||||
dArr[d++] = (byte) (i >> r);
|
||||
}
|
||||
|
||||
return dArr;
|
||||
}
|
||||
|
||||
// ****************************************************************************************
|
||||
// * byte[] version
|
||||
// ****************************************************************************************
|
||||
|
||||
/** Encodes a raw byte array into a BASE64 <code>byte[]</code> representation i accordance with RFC 2045.
|
||||
* @param sArr The bytes to convert. If <code>null</code> or length 0 an empty array will be returned.
|
||||
* @param lineSep Optional "\r\n" after 76 characters, unless end of file.<br>
|
||||
* No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a
|
||||
* little faster.
|
||||
* @return A BASE64 encoded array. Never <code>null</code>.
|
||||
*/
|
||||
public final static byte[] encodeToByte(byte[] sArr, boolean lineSep) {
|
||||
// Check special case
|
||||
int sLen = sArr != null ? sArr.length : 0;
|
||||
if (sLen == 0)
|
||||
return new byte[0];
|
||||
|
||||
int eLen = (sLen / 3) * 3; // Length of even 24-bits.
|
||||
int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count
|
||||
int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array
|
||||
byte[] dArr = new byte[dLen];
|
||||
|
||||
// Encode even 24-bits
|
||||
for (int s = 0, d = 0, cc = 0; s < eLen;) {
|
||||
// Copy next three bytes into lower 24 bits of int, paying attension to sign.
|
||||
int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff);
|
||||
|
||||
// Encode the int into four chars
|
||||
dArr[d++] = (byte) CA[(i >>> 18) & 0x3f];
|
||||
dArr[d++] = (byte) CA[(i >>> 12) & 0x3f];
|
||||
dArr[d++] = (byte) CA[(i >>> 6) & 0x3f];
|
||||
dArr[d++] = (byte) CA[i & 0x3f];
|
||||
|
||||
// Add optional line separator
|
||||
if (lineSep && ++cc == 19 && d < dLen - 2) {
|
||||
dArr[d++] = '\r';
|
||||
dArr[d++] = '\n';
|
||||
cc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Pad and encode last bits if source isn't an even 24 bits.
|
||||
int left = sLen - eLen; // 0 - 2.
|
||||
if (left > 0) {
|
||||
// Prepare the int
|
||||
int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0);
|
||||
|
||||
// Set last four chars
|
||||
dArr[dLen - 4] = (byte) CA[i >> 12];
|
||||
dArr[dLen - 3] = (byte) CA[(i >>> 6) & 0x3f];
|
||||
dArr[dLen - 2] = left == 2 ? (byte) CA[i & 0x3f] : (byte) '=';
|
||||
dArr[dLen - 1] = '=';
|
||||
}
|
||||
return dArr;
|
||||
}
|
||||
|
||||
/** Decodes a BASE64 encoded byte array. All illegal characters will be ignored and can handle both arrays with
|
||||
* and without line separators.
|
||||
* @param sArr The source array. Length 0 will return an empty array. <code>null</code> will throw an exception.
|
||||
* @return The decoded array of bytes. May be of length 0. Will be <code>null</code> if the legal characters
|
||||
* (including '=') isn't divideable by 4. (I.e. definitely corrupted).
|
||||
*/
|
||||
public final static byte[] decode(byte[] sArr) {
|
||||
// Check special case
|
||||
int sLen = sArr.length;
|
||||
|
||||
// Count illegal characters (including '\r', '\n') to know what size the returned array will be,
|
||||
// so we don't have to reallocate & copy it later.
|
||||
int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...)
|
||||
for (int i = 0; i < sLen; i++)
|
||||
// If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out.
|
||||
if (IA[sArr[i] & 0xff] < 0)
|
||||
sepCnt++;
|
||||
|
||||
// Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045.
|
||||
if ((sLen - sepCnt) % 4 != 0)
|
||||
return null;
|
||||
|
||||
int pad = 0;
|
||||
for (int i = sLen; i > 1 && IA[sArr[--i] & 0xff] <= 0;)
|
||||
if (sArr[i] == '=')
|
||||
pad++;
|
||||
|
||||
int len = ((sLen - sepCnt) * 6 >> 3) - pad;
|
||||
|
||||
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
|
||||
|
||||
for (int s = 0, d = 0; d < len;) {
|
||||
// Assemble three bytes into an int from four "valid" characters.
|
||||
int i = 0;
|
||||
for (int j = 0; j < 4; j++) { // j only increased if a valid char was found.
|
||||
int c = IA[sArr[s++] & 0xff];
|
||||
if (c >= 0)
|
||||
i |= c << (18 - j * 6);
|
||||
else
|
||||
j--;
|
||||
}
|
||||
|
||||
// Add the bytes
|
||||
dArr[d++] = (byte) (i >> 16);
|
||||
if (d < len) {
|
||||
dArr[d++] = (byte) (i >> 8);
|
||||
if (d < len)
|
||||
dArr[d++] = (byte) i;
|
||||
}
|
||||
}
|
||||
|
||||
return dArr;
|
||||
}
|
||||
|
||||
|
||||
/** Decodes a BASE64 encoded byte array that is known to be resonably well formatted. The method is about twice as
|
||||
* fast as {@link #decode(byte[])}. The preconditions are:<br>
|
||||
* + The array must have a line length of 76 chars OR no line separators at all (one line).<br>
|
||||
* + Line separator must be "\r\n", as specified in RFC 2045
|
||||
* + The array must not contain illegal characters within the encoded string<br>
|
||||
* + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>
|
||||
* @param sArr The source array. Length 0 will return an empty array. <code>null</code> will throw an exception.
|
||||
* @return The decoded array of bytes. May be of length 0.
|
||||
*/
|
||||
public final static byte[] decodeFast(byte[] sArr) {
|
||||
// Check special case
|
||||
int sLen = sArr.length;
|
||||
if (sLen == 0)
|
||||
return new byte[0];
|
||||
|
||||
int sIx = 0, eIx = sLen - 1; // Start and end index after trimming.
|
||||
|
||||
// Trim illegal chars from start
|
||||
while (sIx < eIx && IA[sArr[sIx] & 0xff] < 0)
|
||||
sIx++;
|
||||
|
||||
// Trim illegal chars from end
|
||||
while (eIx > 0 && IA[sArr[eIx] & 0xff] < 0)
|
||||
eIx--;
|
||||
|
||||
// get the padding count (=) (0, 1 or 2)
|
||||
int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end.
|
||||
int cCnt = eIx - sIx + 1; // Content count including possible separators
|
||||
int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0;
|
||||
|
||||
int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
|
||||
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
|
||||
|
||||
// Decode all but the last 0 - 2 bytes.
|
||||
int d = 0;
|
||||
for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) {
|
||||
// Assemble three bytes into an int from four "valid" characters.
|
||||
int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]];
|
||||
|
||||
// Add the bytes
|
||||
dArr[d++] = (byte) (i >> 16);
|
||||
dArr[d++] = (byte) (i >> 8);
|
||||
dArr[d++] = (byte) i;
|
||||
|
||||
// If line separator, jump over it.
|
||||
if (sepCnt > 0 && ++cc == 19) {
|
||||
sIx += 2;
|
||||
cc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (d < len) {
|
||||
// Decode last 1-3 bytes (incl '=') into 1-3 bytes
|
||||
int i = 0;
|
||||
for (int j = 0; sIx <= eIx - pad; j++)
|
||||
i |= IA[sArr[sIx++]] << (18 - j * 6);
|
||||
|
||||
for (int r = 16; d < len; r -= 8)
|
||||
dArr[d++] = (byte) (i >> r);
|
||||
}
|
||||
|
||||
return dArr;
|
||||
}
|
||||
|
||||
// ****************************************************************************************
|
||||
// * String version
|
||||
// ****************************************************************************************
|
||||
|
||||
/** Encodes a raw byte array into a BASE64 <code>String</code> representation i accordance with RFC 2045.
|
||||
* @param sArr The bytes to convert. If <code>null</code> or length 0 an empty array will be returned.
|
||||
* @param lineSep Optional "\r\n" after 76 characters, unless end of file.<br>
|
||||
* No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a
|
||||
* little faster.
|
||||
* @return A BASE64 encoded array. Never <code>null</code>.
|
||||
*/
|
||||
public final static String encodeToString(byte[] sArr, boolean lineSep) {
|
||||
// Reuse char[] since we can't create a String incrementally anyway and StringBuffer/Builder would be slower.
|
||||
return new String(encodeToChar(sArr, lineSep));
|
||||
}
|
||||
|
||||
/** Decodes a BASE64 encoded <code>String</code>. All illegal characters will be ignored and can handle both strings with
|
||||
* and without line separators.<br>
|
||||
* <b>Note!</b> It can be up to about 2x the speed to call <code>decode(str.toCharArray())</code> instead. That
|
||||
* will create a temporary array though. This version will use <code>str.charAt(i)</code> to iterate the string.
|
||||
* @param str The source string. <code>null</code> or length 0 will return an empty array.
|
||||
* @return The decoded array of bytes. May be of length 0. Will be <code>null</code> if the legal characters
|
||||
* (including '=') isn't divideable by 4. (I.e. definitely corrupted).
|
||||
*/
|
||||
public final static byte[] decode(String str) {
|
||||
// Check special case
|
||||
int sLen = str != null ? str.length() : 0;
|
||||
if (sLen == 0)
|
||||
return new byte[0];
|
||||
|
||||
// Count illegal characters (including '\r', '\n') to know what size the returned array will be,
|
||||
// so we don't have to reallocate & copy it later.
|
||||
int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...)
|
||||
for (int i = 0; i < sLen; i++)
|
||||
// If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out.
|
||||
if (IA[str.charAt(i)] < 0)
|
||||
sepCnt++;
|
||||
|
||||
// Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045.
|
||||
if ((sLen - sepCnt) % 4 != 0)
|
||||
return null;
|
||||
|
||||
// Count '=' at end
|
||||
int pad = 0;
|
||||
for (int i = sLen; i > 1 && IA[str.charAt(--i)] <= 0;)
|
||||
if (str.charAt(i) == '=')
|
||||
pad++;
|
||||
|
||||
int len = ((sLen - sepCnt) * 6 >> 3) - pad;
|
||||
|
||||
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
|
||||
|
||||
for (int s = 0, d = 0; d < len;) {
|
||||
// Assemble three bytes into an int from four "valid" characters.
|
||||
int i = 0;
|
||||
for (int j = 0; j < 4; j++) { // j only increased if a valid char was found.
|
||||
int c = IA[str.charAt(s++)];
|
||||
if (c >= 0)
|
||||
i |= c << (18 - j * 6);
|
||||
else
|
||||
j--;
|
||||
}
|
||||
// Add the bytes
|
||||
dArr[d++] = (byte) (i >> 16);
|
||||
if (d < len) {
|
||||
dArr[d++] = (byte) (i >> 8);
|
||||
if (d < len)
|
||||
dArr[d++] = (byte) i;
|
||||
}
|
||||
}
|
||||
return dArr;
|
||||
}
|
||||
|
||||
/** Decodes a BASE64 encoded string that is known to be resonably well formatted. The method is about twice as
|
||||
* fast as {@link #decode(String)}. The preconditions are:<br>
|
||||
* + The array must have a line length of 76 chars OR no line separators at all (one line).<br>
|
||||
* + Line separator must be "\r\n", as specified in RFC 2045
|
||||
* + The array must not contain illegal characters within the encoded string<br>
|
||||
* + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>
|
||||
* @param s The source string. Length 0 will return an empty array. <code>null</code> will throw an exception.
|
||||
* @return The decoded array of bytes. May be of length 0.
|
||||
*/
|
||||
public final static byte[] decodeFast(String s) {
|
||||
// Check special case
|
||||
int sLen = s.length();
|
||||
if (sLen == 0)
|
||||
return new byte[0];
|
||||
|
||||
int sIx = 0, eIx = sLen - 1; // Start and end index after trimming.
|
||||
|
||||
// Trim illegal chars from start
|
||||
while (sIx < eIx && IA[s.charAt(sIx) & 0xff] < 0)
|
||||
sIx++;
|
||||
|
||||
// Trim illegal chars from end
|
||||
while (eIx > 0 && IA[s.charAt(eIx) & 0xff] < 0)
|
||||
eIx--;
|
||||
|
||||
// get the padding count (=) (0, 1 or 2)
|
||||
int pad = s.charAt(eIx) == '=' ? (s.charAt(eIx - 1) == '=' ? 2 : 1) : 0; // Count '=' at end.
|
||||
int cCnt = eIx - sIx + 1; // Content count including possible separators
|
||||
int sepCnt = sLen > 76 ? (s.charAt(76) == '\r' ? cCnt / 78 : 0) << 1 : 0;
|
||||
|
||||
int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
|
||||
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
|
||||
|
||||
// Decode all but the last 0 - 2 bytes.
|
||||
int d = 0;
|
||||
for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) {
|
||||
// Assemble three bytes into an int from four "valid" characters.
|
||||
int i = IA[s.charAt(sIx++)] << 18 | IA[s.charAt(sIx++)] << 12 | IA[s.charAt(sIx++)] << 6
|
||||
| IA[s.charAt(sIx++)];
|
||||
|
||||
// Add the bytes
|
||||
dArr[d++] = (byte) (i >> 16);
|
||||
dArr[d++] = (byte) (i >> 8);
|
||||
dArr[d++] = (byte) i;
|
||||
|
||||
// If line separator, jump over it.
|
||||
if (sepCnt > 0 && ++cc == 19) {
|
||||
sIx += 2;
|
||||
cc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (d < len) {
|
||||
// Decode last 1-3 bytes (incl '=') into 1-3 bytes
|
||||
int i = 0;
|
||||
for (int j = 0; sIx <= eIx - pad; j++)
|
||||
i |= IA[s.charAt(sIx++)] << (18 - j * 6);
|
||||
|
||||
for (int r = 16; d < len; r -= 8)
|
||||
dArr[d++] = (byte) (i >> r);
|
||||
}
|
||||
|
||||
return dArr;
|
||||
}
|
||||
}
|
||||
@@ -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.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.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.redis.connection.util;
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisZSetCommands.Tuple;
|
||||
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.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")
|
||||
Set<TypedTuple<V>> deserializeTupleValues(Set<Tuple> rawValues) {
|
||||
Set<TypedTuple<V>> set = new LinkedHashSet<TypedTuple<V>>(rawValues.size());
|
||||
for (Tuple rawValue : rawValues) {
|
||||
set.add(new DefaultTypedTuple(valueSerializer.deserialize(rawValue.getValue()), rawValue.getScore()));
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Hash operations bound to a certain key.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface BoundHashOperations<H, HK, HV> extends BoundKeyOperations<H> {
|
||||
|
||||
RedisOperations<H, ?> getOperations();
|
||||
|
||||
boolean hasKey(Object key);
|
||||
|
||||
Long increment(HK key, long delta);
|
||||
|
||||
HV get(Object key);
|
||||
|
||||
void put(HK key, HV value);
|
||||
|
||||
Boolean putIfAbsent(HK key, HV value);
|
||||
|
||||
Collection<HV> multiGet(Collection<HK> keys);
|
||||
|
||||
void putAll(Map<? extends HK, ? extends HV> m);
|
||||
|
||||
Set<HK> keys();
|
||||
|
||||
Collection<HV> values();
|
||||
|
||||
Long size();
|
||||
|
||||
void delete(Object key);
|
||||
|
||||
Map<HK, HV> entries();
|
||||
}
|
||||
@@ -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.redis.core;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.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);
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* List operations bound to a certain key.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface BoundListOperations<K, V> extends BoundKeyOperations<K> {
|
||||
|
||||
RedisOperations<K, V> getOperations();
|
||||
|
||||
List<V> range(long start, long end);
|
||||
|
||||
void trim(long start, long end);
|
||||
|
||||
Long size();
|
||||
|
||||
Long leftPush(V value);
|
||||
|
||||
Long leftPushIfPresent(V value);
|
||||
|
||||
Long leftPush(V pivot, V value);
|
||||
|
||||
Long rightPush(V value);
|
||||
|
||||
Long rightPushIfPresent(V value);
|
||||
|
||||
Long rightPush(V pivot, V value);
|
||||
|
||||
V leftPop();
|
||||
|
||||
V leftPop(long timeout, TimeUnit unit);
|
||||
|
||||
V rightPop();
|
||||
|
||||
V rightPop(long timeout, TimeUnit unit);
|
||||
|
||||
Long remove(long i, Object value);
|
||||
|
||||
V index(long index);
|
||||
|
||||
void set(long index, V value);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Set operations bound to a certain key.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface BoundSetOperations<K, V> extends BoundKeyOperations<K> {
|
||||
|
||||
RedisOperations<K, V> getOperations();
|
||||
|
||||
Set<V> diff(K key);
|
||||
|
||||
Set<V> diff(Collection<K> keys);
|
||||
|
||||
void diffAndStore(K key, K destKey);
|
||||
|
||||
void diffAndStore(Collection<K> keys, K destKey);
|
||||
|
||||
Set<V> intersect(K key);
|
||||
|
||||
Set<V> intersect(Collection<K> keys);
|
||||
|
||||
void intersectAndStore(K key, K destKey);
|
||||
|
||||
void intersectAndStore(Collection<K> keys, K destKey);
|
||||
|
||||
Set<V> union(K key);
|
||||
|
||||
Set<V> union(Collection<K> keys);
|
||||
|
||||
void unionAndStore(K key, K destKey);
|
||||
|
||||
void unionAndStore(Collection<K> keys, K destKey);
|
||||
|
||||
Boolean add(V value);
|
||||
|
||||
Boolean isMember(Object o);
|
||||
|
||||
Set<V> members();
|
||||
|
||||
Boolean move(K destKey, V value);
|
||||
|
||||
V randomMember();
|
||||
|
||||
Boolean remove(Object o);
|
||||
|
||||
V pop();
|
||||
|
||||
Long size();
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Value (or String in Redis terminology) operations bound to a certain key.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
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);
|
||||
|
||||
Long size();
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
|
||||
|
||||
|
||||
/**
|
||||
* ZSet (or SortedSet) operations bound to a certain key.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface BoundZSetOperations<K, V> extends BoundKeyOperations<K> {
|
||||
|
||||
RedisOperations<K, V> getOperations();
|
||||
|
||||
void intersectAndStore(K otherKey, K destKey);
|
||||
|
||||
void intersectAndStore(Collection<K> otherKeys, K destKey);
|
||||
|
||||
Set<V> range(long start, long end);
|
||||
|
||||
Set<V> rangeByScore(double min, double max);
|
||||
|
||||
Set<V> reverseRange(long start, long end);
|
||||
|
||||
Set<V> reverseRangeByScore(double min, double max);
|
||||
|
||||
Set<TypedTuple<V>> rangeWithScores(long start, long end);
|
||||
|
||||
Set<TypedTuple<V>> rangeByScoreWithScores(double min, double max);
|
||||
|
||||
Set<TypedTuple<V>> reverseRangeWithScores(long start, long end);
|
||||
|
||||
Set<TypedTuple<V>> reverseRangeByScoreWithScores(double min, double max);
|
||||
|
||||
void removeRange(long start, long end);
|
||||
|
||||
void removeRangeByScore(double min, double max);
|
||||
|
||||
void unionAndStore(K otherKey, K destKey);
|
||||
|
||||
void unionAndStore(Collection<K> otherKeys, K destKey);
|
||||
|
||||
Boolean add(V value, double score);
|
||||
|
||||
Double incrementScore(V value, double delta);
|
||||
|
||||
Long rank(Object o);
|
||||
|
||||
Long reverseRank(Object o);
|
||||
|
||||
Boolean remove(Object o);
|
||||
|
||||
Long count(double min, double max);
|
||||
|
||||
Long size();
|
||||
|
||||
Double score(Object o);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Mapper translating Redis bulk value responses (typically returned by a sort query) to actual objects. Implementations of this interface do not have to worry
|
||||
* about exception or connection handling.
|
||||
* <p/>
|
||||
* Typically used by {@link RedisTemplate} <tt>sort</tt> methods.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface BulkMapper<T, V> {
|
||||
|
||||
T mapBulk(List<V> tuple);
|
||||
}
|
||||
@@ -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.redis.core;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.data.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.redis.connection.DataType;
|
||||
|
||||
/**
|
||||
* Default implementation for {@link HashOperations}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class DefaultBoundHashOperations<H, HK, HV> extends DefaultBoundKeyOperations<H> implements BoundHashOperations<H, HK, HV> {
|
||||
|
||||
private final HashOperations<H, HK, HV> ops;
|
||||
|
||||
/**
|
||||
* Constructs a new <code>DefaultBoundHashOperations</code> instance.
|
||||
*
|
||||
* @param key
|
||||
* @param template
|
||||
*/
|
||||
public DefaultBoundHashOperations(H key, RedisOperations<H, ?> operations) {
|
||||
super(key, operations);
|
||||
this.ops = operations.opsForHash();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Object key) {
|
||||
ops.delete(getKey(), key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HV get(Object key) {
|
||||
return ops.get(getKey(), key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<HV> multiGet(Collection<HK> hashKeys) {
|
||||
return ops.multiGet(getKey(), hashKeys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisOperations<H, ?> getOperations() {
|
||||
return ops.getOperations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasKey(Object key) {
|
||||
return ops.hasKey(getKey(), key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long increment(HK key, long delta) {
|
||||
return ops.increment(getKey(), key, delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<HK> keys() {
|
||||
return ops.keys(getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long size() {
|
||||
return ops.size(getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends HK, ? extends HV> m) {
|
||||
ops.putAll(getKey(), m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(HK key, HV value) {
|
||||
ops.put(getKey(), key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean putIfAbsent(HK key, HV value) {
|
||||
return ops.putIfAbsent(getKey(), key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<HV> values() {
|
||||
return ops.values(getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<HK, HV> entries() {
|
||||
return ops.entries(getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getType() {
|
||||
return DataType.HASH;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.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) {
|
||||
if (ops.hasKey(key)) {
|
||||
ops.rename(key, newKey);
|
||||
}
|
||||
key = newKey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.redis.connection.DataType;
|
||||
|
||||
|
||||
/**
|
||||
* Default implementation for {@link BoundListOperations}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class DefaultBoundListOperations<K, V> extends DefaultBoundKeyOperations<K> implements BoundListOperations<K, V> {
|
||||
|
||||
private final ListOperations<K, V> ops;
|
||||
|
||||
/**
|
||||
* Constructs a new <code>DefaultBoundListOperations</code> instance.
|
||||
*
|
||||
* @param key
|
||||
* @param operations
|
||||
*/
|
||||
public DefaultBoundListOperations(K key, RedisOperations<K, V> operations) {
|
||||
super(key, operations);
|
||||
this.ops = operations.opsForList();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public RedisOperations<K, V> getOperations() {
|
||||
return ops.getOperations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V index(long index) {
|
||||
return ops.index(getKey(), index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V leftPop() {
|
||||
return ops.leftPop(getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public V leftPop(long timeout, TimeUnit unit) {
|
||||
return ops.leftPop(getKey(), timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long leftPush(V value) {
|
||||
return ops.leftPush(getKey(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long leftPushIfPresent(V value) {
|
||||
return ops.leftPushIfPresent(getKey(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long leftPush(V pivot, V value) {
|
||||
return ops.leftPush(getKey(), pivot, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long size() {
|
||||
return ops.size(getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<V> range(long start, long end) {
|
||||
return ops.range(getKey(), start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long remove(long i, Object value) {
|
||||
return ops.remove(getKey(), i, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V rightPop() {
|
||||
return ops.rightPop(getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public V rightPop(long timeout, TimeUnit unit) {
|
||||
return ops.rightPop(getKey(), timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long rightPushIfPresent(V value) {
|
||||
return ops.rightPushIfPresent(getKey(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long rightPush(V value) {
|
||||
return ops.rightPush(getKey(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long rightPush(V pivot, V value) {
|
||||
return ops.rightPush(getKey(), pivot, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trim(long start, long end) {
|
||||
ops.trim(getKey(), start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(long index, V value) {
|
||||
ops.set(getKey(), index, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getType() {
|
||||
return DataType.LIST;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.redis.connection.DataType;
|
||||
|
||||
/**
|
||||
* Default implementation for {@link BoundSetOperations}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class DefaultBoundSetOperations<K, V> extends DefaultBoundKeyOperations<K> implements BoundSetOperations<K, V> {
|
||||
|
||||
private final SetOperations<K, V> ops;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new <code>DefaultBoundSetOperations</code> instance.
|
||||
*
|
||||
* @param key
|
||||
* @param operations
|
||||
*/
|
||||
DefaultBoundSetOperations(K key, RedisOperations<K, V> operations) {
|
||||
super(key, operations);
|
||||
this.ops = operations.opsForSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean add(V value) {
|
||||
return ops.add(getKey(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> diff(K key) {
|
||||
return ops.difference(getKey(), key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> diff(Collection<K> keys) {
|
||||
return ops.difference(getKey(), keys);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void diffAndStore(K key, K destKey) {
|
||||
ops.differenceAndStore(getKey(), key, destKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void diffAndStore(Collection<K> keys, K destKey) {
|
||||
ops.differenceAndStore(getKey(), keys, destKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisOperations<K, V> getOperations() {
|
||||
return ops.getOperations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> intersect(K key) {
|
||||
return ops.intersect(getKey(), key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> intersect(Collection<K> keys) {
|
||||
return ops.intersect(getKey(), keys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void intersectAndStore(K key, K destKey) {
|
||||
ops.intersectAndStore(getKey(), key, destKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void intersectAndStore(Collection<K> keys, K destKey) {
|
||||
ops.intersectAndStore(getKey(), keys, destKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isMember(Object o) {
|
||||
return ops.isMember(getKey(), o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> members() {
|
||||
return ops.members(getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean move(K destKey, V value) {
|
||||
return ops.move(getKey(), value, destKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V randomMember() {
|
||||
return ops.randomMember(getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean remove(Object o) {
|
||||
return ops.remove(getKey(), o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V pop() {
|
||||
return ops.pop(getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long size() {
|
||||
return ops.size(getKey());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Set<V> union(K key) {
|
||||
return ops.union(getKey(), key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> union(Collection<K> keys) {
|
||||
return ops.union(getKey(), keys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unionAndStore(K key, K destKey) {
|
||||
ops.unionAndStore(getKey(), key, destKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unionAndStore(Collection<K> keys, K destKey) {
|
||||
ops.unionAndStore(getKey(), keys, destKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getType() {
|
||||
return DataType.SET;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.redis.connection.DataType;
|
||||
|
||||
/**
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class DefaultBoundValueOperations<K, V> extends DefaultBoundKeyOperations<K> implements BoundValueOperations<K, V> {
|
||||
|
||||
private final ValueOperations<K, V> ops;
|
||||
|
||||
/**
|
||||
* Constructs a new <code>DefaultBoundValueOperations</code> instance.
|
||||
*
|
||||
* @param key
|
||||
* @param operations
|
||||
*/
|
||||
public DefaultBoundValueOperations(K key, RedisOperations<K, V> operations) {
|
||||
super(key, operations);
|
||||
this.ops = operations.opsForValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get() {
|
||||
return ops.get(getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getAndSet(V value) {
|
||||
return ops.getAndSet(getKey(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long increment(long delta) {
|
||||
return ops.increment(getKey(), delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer append(String value) {
|
||||
return ops.append(getKey(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(long start, long end) {
|
||||
return ops.get(getKey(), start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(V value, long timeout, TimeUnit unit) {
|
||||
ops.set(getKey(), value, timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(V value) {
|
||||
ops.set(getKey(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean setIfAbsent(V value) {
|
||||
return ops.setIfAbsent(getKey(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(V value, long offset) {
|
||||
ops.set(getKey(), value, offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long size() {
|
||||
return ops.size(getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisOperations<K, V> getOperations() {
|
||||
return ops.getOperations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getType() {
|
||||
return DataType.STRING;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.redis.connection.DataType;
|
||||
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
|
||||
|
||||
/**
|
||||
* Default implementation for {@link BoundZSetOperations}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class DefaultBoundZSetOperations<K, V> extends DefaultBoundKeyOperations<K> implements BoundZSetOperations<K, V> {
|
||||
|
||||
private final ZSetOperations<K, V> ops;
|
||||
|
||||
/**
|
||||
* Constructs a new <code>DefaultBoundZSetOperations</code> instance.
|
||||
*
|
||||
* @param key
|
||||
* @param oeprations
|
||||
*/
|
||||
public DefaultBoundZSetOperations(K key, RedisOperations<K, V> operations) {
|
||||
super(key, operations);
|
||||
this.ops = operations.opsForZSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean add(V value, double score) {
|
||||
return ops.add(getKey(), value, score);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double incrementScore(V value, double delta) {
|
||||
return ops.incrementScore(getKey(), value, delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisOperations<K, V> getOperations() {
|
||||
return ops.getOperations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void intersectAndStore(K destKey, K otherKey) {
|
||||
ops.intersectAndStore(getKey(), otherKey, destKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void intersectAndStore(Collection<K> otherKeys, K destKey) {
|
||||
ops.intersectAndStore(getKey(), otherKeys, destKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> range(long start, long end) {
|
||||
return ops.range(getKey(), start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> rangeByScore(double min, double max) {
|
||||
return ops.rangeByScore(getKey(), min, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<TypedTuple<V>> rangeByScoreWithScores(double min, double max) {
|
||||
return ops.rangeByScoreWithScores(getKey(), min, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<TypedTuple<V>> rangeWithScores(long start, long end) {
|
||||
return ops.rangeWithScores(getKey(), start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> reverseRangeByScore(double min, double max) {
|
||||
return ops.reverseRangeByScore(getKey(), min, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<TypedTuple<V>> reverseRangeByScoreWithScores(double min, double max) {
|
||||
return ops.reverseRangeByScoreWithScores(getKey(), min, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<TypedTuple<V>> reverseRangeWithScores(long start, long end) {
|
||||
return ops.reverseRangeWithScores(getKey(), start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long rank(Object o) {
|
||||
return ops.rank(getKey(), o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long reverseRank(Object o) {
|
||||
return ops.reverseRank(getKey(), o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double score(Object o) {
|
||||
return ops.score(getKey(), o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean remove(Object o) {
|
||||
return ops.remove(getKey(), o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRange(long start, long end) {
|
||||
ops.removeRange(getKey(), start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRangeByScore(double min, double max) {
|
||||
ops.removeRangeByScore(getKey(), min, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> reverseRange(long start, long end) {
|
||||
return ops.reverseRange(getKey(), start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long count(double min, double max) {
|
||||
return ops.count(getKey(), min, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long size() {
|
||||
return ops.size(getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unionAndStore(K otherKey, K destKey) {
|
||||
ops.unionAndStore(getKey(), otherKey, destKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
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.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.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,249 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisListCommands.Position;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
List<byte[]> lPop = connection.bLPop(tm, rawKey);
|
||||
return (CollectionUtils.isEmpty(lPop) ? null : lPop.get(1));
|
||||
}
|
||||
}, 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) {
|
||||
List<byte[]> bRPop = connection.bRPop(tm, rawKey);
|
||||
return (CollectionUtils.isEmpty(bRPop) ? null : bRPop.get(1));
|
||||
}
|
||||
}, 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.redis.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.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,50 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
|
||||
|
||||
/**
|
||||
* Default implementation of TypedTuple.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class DefaultTypedTuple<V> implements TypedTuple<V> {
|
||||
|
||||
private final Double score;
|
||||
private final V value;
|
||||
|
||||
/**
|
||||
* Constructs a new <code>DefaultTypedTuple</code> instance.
|
||||
*
|
||||
* @param value
|
||||
* @param score
|
||||
*/
|
||||
public DefaultTypedTuple(V value, Double score) {
|
||||
this.score = score;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -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.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.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,313 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisZSetCommands.Tuple;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@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 Set<TypedTuple<V>> rangeWithScores(K key, final long start, final long end) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
|
||||
Set<Tuple> rawValues = execute(new RedisCallback<Set<Tuple>>() {
|
||||
@Override
|
||||
public Set<Tuple> doInRedis(RedisConnection connection) {
|
||||
return connection.zRangeWithScores(rawKey, start, end);
|
||||
}
|
||||
}, true);
|
||||
|
||||
return deserializeTupleValues(rawValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<TypedTuple<V>> reverseRangeWithScores(K key, final long start, final long end) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
|
||||
Set<Tuple> rawValues = execute(new RedisCallback<Set<Tuple>>() {
|
||||
@Override
|
||||
public Set<Tuple> doInRedis(RedisConnection connection) {
|
||||
return connection.zRevRangeWithScores(rawKey, start, end);
|
||||
}
|
||||
}, true);
|
||||
|
||||
return deserializeTupleValues(rawValues);
|
||||
}
|
||||
|
||||
@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 Set<V> reverseRangeByScore(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.zRevRangeByScore(rawKey, min, max);
|
||||
}
|
||||
}, true);
|
||||
|
||||
return deserializeValues(rawValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<TypedTuple<V>> rangeByScoreWithScores(K key, final double min, final double max) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
|
||||
Set<Tuple> rawValues = execute(new RedisCallback<Set<Tuple>>() {
|
||||
@Override
|
||||
public Set<Tuple> doInRedis(RedisConnection connection) {
|
||||
return connection.zRangeByScoreWithScores(rawKey, min, max);
|
||||
}
|
||||
}, true);
|
||||
|
||||
return deserializeTupleValues(rawValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<TypedTuple<V>> reverseRangeByScoreWithScores(K key, final double min, final double max) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
|
||||
Set<Tuple> rawValues = execute(new RedisCallback<Set<Tuple>>() {
|
||||
@Override
|
||||
public Set<Tuple> doInRedis(RedisConnection connection) {
|
||||
return connection.zRevRangeByScoreWithScores(rawKey, min, max);
|
||||
|
||||
}
|
||||
}, true);
|
||||
|
||||
return deserializeTupleValues(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);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Redis map specific operations working on a hash.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface HashOperations<H, HK, HV> {
|
||||
|
||||
void delete(H key, Object hashKey);
|
||||
|
||||
Boolean hasKey(H key, Object hashKey);
|
||||
|
||||
HV get(H key, Object hashKey);
|
||||
|
||||
Collection<HV> multiGet(H key, Collection<HK> hashKeys);
|
||||
|
||||
Long increment(H key, HK hashKey, long delta);
|
||||
|
||||
Set<HK> keys(H key);
|
||||
|
||||
Long size(H key);
|
||||
|
||||
void putAll(H key, Map<? extends HK, ? extends HV> m);
|
||||
|
||||
void put(H key, HK hashKey, HV value);
|
||||
|
||||
Boolean putIfAbsent(H key, HK hashKey, HV value);
|
||||
|
||||
Collection<HV> values(H key);
|
||||
|
||||
Map<HK, HV> entries(H key);
|
||||
|
||||
RedisOperations<H, ?> getOperations();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
|
||||
/**
|
||||
* PropertyEditor allowing for easy injection of {@link HashOperations} from
|
||||
* {@link RedisOperations}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class HashOperationsEditor extends PropertyEditorSupport {
|
||||
|
||||
@Override
|
||||
public void setValue(Object value) {
|
||||
if (value instanceof RedisOperations) {
|
||||
super.setValue(((RedisOperations) value).opsForHash());
|
||||
}
|
||||
else {
|
||||
throw new java.lang.IllegalArgumentException("Editor supports only conversion of type "
|
||||
+ RedisOperations.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Redis list specific operations.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface ListOperations<K, V> {
|
||||
|
||||
List<V> range(K key, long start, long end);
|
||||
|
||||
void trim(K key, long start, long end);
|
||||
|
||||
Long size(K key);
|
||||
|
||||
Long leftPush(K key, V value);
|
||||
|
||||
Long leftPushIfPresent(K key, V value);
|
||||
|
||||
Long leftPush(K key, V pivot, V value);
|
||||
|
||||
Long rightPush(K key, V value);
|
||||
|
||||
Long rightPushIfPresent(K key, V value);
|
||||
|
||||
Long rightPush(K key, V pivot, V value);
|
||||
|
||||
void set(K key, long index, V value);
|
||||
|
||||
Long remove(K key, long i, Object value);
|
||||
|
||||
V index(K key, long index);
|
||||
|
||||
V leftPop(K key);
|
||||
|
||||
V leftPop(K key, long timeout, TimeUnit unit);
|
||||
|
||||
V rightPop(K key);
|
||||
|
||||
V rightPop(K key, long timeout, TimeUnit unit);
|
||||
|
||||
V rightPopAndLeftPush(K sourceKey, K destinationKey);
|
||||
|
||||
V rightPopAndLeftPush(K sourceKey, K destinationKey, long timeout, TimeUnit unit);
|
||||
|
||||
RedisOperations<K, V> getOperations();
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
|
||||
/**
|
||||
* PropertyEditor allowing for easy injection of {@link ListOperations} from
|
||||
* {@link RedisOperations}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class ListOperationsEditor extends PropertyEditorSupport {
|
||||
@Override
|
||||
public void setValue(Object value) {
|
||||
if (value instanceof RedisOperations) {
|
||||
super.setValue(((RedisOperations) value).opsForList());
|
||||
}
|
||||
else {
|
||||
throw new java.lang.IllegalArgumentException("Editor supports only conversion of type "
|
||||
+ RedisOperations.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base class for {@link RedisTemplate} defining common properties.
|
||||
* Not intended to be used directly.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class RedisAccessor implements InitializingBean {
|
||||
|
||||
/** Logger available to subclasses */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private RedisConnectionFactory connectionFactory;
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(getConnectionFactory(), "RedisConnectionFactory is required");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the connectionFactory.
|
||||
*
|
||||
* @return Returns the connectionFactory
|
||||
*/
|
||||
public RedisConnectionFactory getConnectionFactory() {
|
||||
return connectionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the connection factory.
|
||||
*
|
||||
* @param connectionFactory The connectionFactory to set.
|
||||
*/
|
||||
public void setConnectionFactory(RedisConnectionFactory connectionFactory) {
|
||||
this.connectionFactory = connectionFactory;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
|
||||
/**
|
||||
* Callback interface for Redis 'low level' code.
|
||||
* To be used with {@link RedisTemplate} execution methods, often as anonymous classes within a method implementation.
|
||||
* Usually, used for chaining several operations together ({@code get/set/trim etc...}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface RedisCallback<T> {
|
||||
|
||||
/**
|
||||
* Gets called by {@link RedisTemplate} with an active Redis connection. Does not need to care about activating or
|
||||
* closing the connection or handling exceptions.
|
||||
*
|
||||
* @param connection active Redis connection
|
||||
* @return a result object or {@code null} if none
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
T doInRedis(RedisConnection connection) throws DataAccessException;
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.transaction.support.ResourceHolder;
|
||||
import org.springframework.transaction.support.ResourceHolderSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Helper class featuring {@link RedisConnection} handling, allowing for reuse of instances within 'transactions'/scopes.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public abstract class RedisConnectionUtils {
|
||||
|
||||
private static final Log log = LogFactory.getLog(RedisConnectionUtils.class);
|
||||
|
||||
/**
|
||||
* Binds a new Redis connection (from the given factory) to the current thread, if none is already bound.
|
||||
*
|
||||
* @param factory connection factory
|
||||
* @return a new Redis connection
|
||||
*/
|
||||
public static RedisConnection bindConnection(RedisConnectionFactory factory) {
|
||||
return doGetConnection(factory, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a Redis connection from the given factory. Is aware of and will return any existing corresponding connections bound to the current thread,
|
||||
* for example when using a transaction manager. Will always create a new connection otherwise.
|
||||
*
|
||||
* @param factory connection factory for creating the connection
|
||||
* @return an active Redis connection
|
||||
*/
|
||||
public static RedisConnection getConnection(RedisConnectionFactory factory) {
|
||||
return doGetConnection(factory, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a Redis connection. Is aware of and will return any existing corresponding connections bound to the current thread,
|
||||
* for example when using a transaction manager. Will create a new Connection otherwise, if {@code allowCreate} is <tt>true</tt>.
|
||||
*
|
||||
* @param factory connection factory for creating the connection
|
||||
* @param allowCreate whether a new (unbound) connection should be created when no connection can be found for the current thread
|
||||
* @param bind binds the connection to the thread, in case one was created
|
||||
* @return an active Redis connection
|
||||
*/
|
||||
public static RedisConnection doGetConnection(RedisConnectionFactory factory, boolean allowCreate, boolean bind) {
|
||||
Assert.notNull(factory, "No RedisConnectionFactory specified");
|
||||
|
||||
RedisConnectionHolder connHolder = (RedisConnectionHolder) TransactionSynchronizationManager.getResource(factory);
|
||||
//TODO: investigate tx synchronization
|
||||
|
||||
if (connHolder != null)
|
||||
return connHolder.getConnection();
|
||||
|
||||
if (!allowCreate) {
|
||||
throw new IllegalArgumentException("No connection found and allowCreate = false");
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled())
|
||||
log.debug("Opening RedisConnection");
|
||||
|
||||
RedisConnection conn = factory.getConnection();
|
||||
|
||||
boolean synchronizationActive = TransactionSynchronizationManager.isSynchronizationActive();
|
||||
|
||||
if (bind || synchronizationActive) {
|
||||
connHolder = new RedisConnectionHolder(conn);
|
||||
if (synchronizationActive) {
|
||||
TransactionSynchronizationManager.registerSynchronization(new RedisConnectionSynchronization(
|
||||
connHolder, factory, true));
|
||||
}
|
||||
TransactionSynchronizationManager.bindResource(factory, connHolder);
|
||||
return connHolder.getConnection();
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the given connection, created via the given factory if not managed externally (i.e. not bound to the thread).
|
||||
*
|
||||
* @param conn the Redis connection to close
|
||||
* @param factory the Redis factory that the connection was created with
|
||||
*/
|
||||
public static void releaseConnection(RedisConnection conn, RedisConnectionFactory factory) {
|
||||
if (conn == null) {
|
||||
return;
|
||||
}
|
||||
// Only release non-transactional/non-bound connections.
|
||||
if (!isConnectionTransactional(conn, factory)) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Closing Redis Connection");
|
||||
}
|
||||
conn.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbinds and closes the connection (if any) associated with the given factory.
|
||||
*
|
||||
* @param factory Redis factory
|
||||
*/
|
||||
public static void unbindConnection(RedisConnectionFactory factory) {
|
||||
RedisConnectionHolder connHolder = (RedisConnectionHolder) TransactionSynchronizationManager.unbindResourceIfPossible(factory);
|
||||
if (connHolder != null) {
|
||||
RedisConnection connection = connHolder.getConnection();
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the given Redis connection is transactional, that is, bound to the current thread by Spring's transaction facilities.
|
||||
*
|
||||
* @param conn Redis connection to check
|
||||
* @param connFactory Redis connection factory that the connection was created with
|
||||
* @return whether the connection is transactional or not
|
||||
*/
|
||||
public static boolean isConnectionTransactional(RedisConnection conn, RedisConnectionFactory connFactory) {
|
||||
if (connFactory == null) {
|
||||
return false;
|
||||
}
|
||||
RedisConnectionHolder connHolder = (RedisConnectionHolder) TransactionSynchronizationManager.getResource(connFactory);
|
||||
return (connHolder != null && conn == connHolder.getConnection());
|
||||
}
|
||||
|
||||
private static class RedisConnectionSynchronization extends
|
||||
ResourceHolderSynchronization<RedisConnectionHolder, RedisConnectionFactory> {
|
||||
|
||||
private final boolean newRedisConnection;
|
||||
|
||||
public RedisConnectionSynchronization(RedisConnectionHolder connHolder, RedisConnectionFactory connFactory,
|
||||
boolean newRedisConnection) {
|
||||
super(connHolder, connFactory);
|
||||
this.newRedisConnection = newRedisConnection;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldUnbindAtCompletion() {
|
||||
return this.newRedisConnection;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void releaseResource(RedisConnectionHolder resourceHolder, RedisConnectionFactory resourceKey) {
|
||||
releaseConnection(resourceHolder.getConnection(), resourceKey);
|
||||
}
|
||||
}
|
||||
|
||||
private static class RedisConnectionHolder implements ResourceHolder {
|
||||
|
||||
private boolean isVoid = false;
|
||||
private final RedisConnection conn;
|
||||
|
||||
public RedisConnectionHolder(RedisConnection conn) {
|
||||
this.conn = conn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVoid() {
|
||||
return isVoid;
|
||||
}
|
||||
|
||||
public RedisConnection getConnection() {
|
||||
return conn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbound() {
|
||||
this.isVoid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.redis.connection.DataType;
|
||||
import org.springframework.data.redis.core.query.SortQuery;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
|
||||
|
||||
/**
|
||||
* Interface that specified a basic set of Redis operations, implemented by {@link RedisTemplate}.
|
||||
* Not often used but a useful option for extensibility and testability (as it can be easily mocked or stubbed).
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface RedisOperations<K, V> {
|
||||
|
||||
/**
|
||||
* Executes the given action within a Redis connection.
|
||||
*
|
||||
* Application exceptions thrown by the action object get propagated to the caller (can only be unchecked) whenever possible.
|
||||
* Redis exceptions are transformed into appropriate DAO ones.
|
||||
* Allows for returning a result object, that is a domain object or a collection of domain objects.
|
||||
* Performs automatic serialization/deserialization for the given objects to and from binary data suitable for the Redis storage.
|
||||
*
|
||||
* Note: Callback code is not supposed to handle transactions itself! Use an appropriate transaction manager.
|
||||
* Generally, callback code must not touch any Connection lifecycle methods, like close, to let the template do its work.
|
||||
*
|
||||
* @param <T> return type
|
||||
* @param action callback object that specifies the Redis action
|
||||
* @return a result object returned by the action or <tt>null</tt>
|
||||
*/
|
||||
<T> T execute(RedisCallback<T> action);
|
||||
|
||||
|
||||
/**
|
||||
* Executes a Redis session.
|
||||
*
|
||||
* Allows multiple operations to be executed in the same session enabling 'transactional' capabilities through {@link #multi()}
|
||||
* and {@link #watch(Collection)} operations.
|
||||
*
|
||||
* @param <T> return type
|
||||
* @param session session callback
|
||||
* @return result object returned by the action or <tt>null</tt>
|
||||
*/
|
||||
<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);
|
||||
|
||||
void delete(Collection<K> key);
|
||||
|
||||
DataType type(K key);
|
||||
|
||||
Set<K> keys(K pattern);
|
||||
|
||||
K randomKey();
|
||||
|
||||
void rename(K oldKey, K newKey);
|
||||
|
||||
Boolean renameIfAbsent(K oldKey, K newKey);
|
||||
|
||||
Boolean expire(K key, long timeout, TimeUnit unit);
|
||||
|
||||
Boolean expireAt(K key, Date date);
|
||||
|
||||
Boolean persist(K key);
|
||||
|
||||
Boolean move(K key, int dbIndex);
|
||||
|
||||
Long getExpire(K key);
|
||||
|
||||
void watch(K keys);
|
||||
|
||||
void watch(Collection<K> keys);
|
||||
|
||||
void unwatch();
|
||||
|
||||
/**'
|
||||
*
|
||||
*/
|
||||
void multi();
|
||||
|
||||
void discard();
|
||||
|
||||
List<Object> exec();
|
||||
|
||||
// pubsub functionality on the template
|
||||
void convertAndSend(String destination, Object message);
|
||||
|
||||
|
||||
// operation types
|
||||
/**
|
||||
* Returns the operations performed on simple values (or Strings in Redis terminology).
|
||||
*
|
||||
* @return value operations
|
||||
*/
|
||||
ValueOperations<K, V> opsForValue();
|
||||
|
||||
/**
|
||||
* Returns the operations performed on simple values (or Strings in Redis terminology)
|
||||
* bound to the given key.
|
||||
*
|
||||
* @param key Redis key
|
||||
* @return value operations bound to the given key
|
||||
*/
|
||||
BoundValueOperations<K, V> boundValueOps(K key);
|
||||
|
||||
/**
|
||||
* Returns the operations performed on list values.
|
||||
*
|
||||
* @return list operations
|
||||
*/
|
||||
ListOperations<K, V> opsForList();
|
||||
|
||||
/**
|
||||
* Returns the operations performed on list values bound to the given key.
|
||||
*
|
||||
* @param key Redis key
|
||||
* @return list operations bound to the given key
|
||||
*/
|
||||
BoundListOperations<K, V> boundListOps(K key);
|
||||
|
||||
/**
|
||||
* Returns the operations performed on set values.
|
||||
*
|
||||
* @return set operations
|
||||
*/
|
||||
SetOperations<K, V> opsForSet();
|
||||
|
||||
/**
|
||||
* Returns the operations performed on set values bound to the given key.
|
||||
*
|
||||
* @param key Redis key
|
||||
* @return set operations bound to the given key
|
||||
*/
|
||||
BoundSetOperations<K, V> boundSetOps(K key);
|
||||
|
||||
/**
|
||||
* Returns the operations performed on zset values (also known as sorted sets).
|
||||
*
|
||||
* @return zset operations
|
||||
*/
|
||||
ZSetOperations<K, V> opsForZSet();
|
||||
|
||||
/**
|
||||
* Returns the operations performed on zset values (also known as sorted sets)
|
||||
* bound to the given key.
|
||||
*
|
||||
* @param key Redis key
|
||||
* @return zset operations bound to the given key.
|
||||
*/
|
||||
BoundZSetOperations<K, V> boundZSetOps(K key);
|
||||
|
||||
/**
|
||||
* Returns the operations performed on hash values.
|
||||
*
|
||||
* @param <HK> hash key (or field) type
|
||||
* @param <HV> hash value type
|
||||
* @return hash operations
|
||||
*/
|
||||
<HK, HV> HashOperations<K, HK, HV> opsForHash();
|
||||
|
||||
/**
|
||||
* Returns the operations performed on hash values bound to the given key.
|
||||
*
|
||||
* @param <HK> hash key (or field) type
|
||||
* @param <HV> hash value type
|
||||
* @param key Redis key
|
||||
* @return hash operations bound to the given key.
|
||||
*/
|
||||
<HK, HV> BoundHashOperations<K, HK, HV> boundHashOps(K key);
|
||||
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -0,0 +1,794 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.redis.connection.DataType;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.SortParameters;
|
||||
import org.springframework.data.redis.core.query.QueryUtils;
|
||||
import org.springframework.data.redis.core.query.SortQuery;
|
||||
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.redis.serializer.SerializationUtils;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Helper class that simplifies Redis data access code.
|
||||
* <p/>
|
||||
* Performs automatic serialization/deserialization between the given objects and the underlying binary data in the Redis store.
|
||||
* By default, it uses Java serialization for its objects (through {@link JdkSerializationRedisSerializer}). For String intensive
|
||||
* operations consider the dedicated {@link StringRedisTemplate}.
|
||||
* <p/>
|
||||
* The central method is execute, supporting Redis access code implementing the {@link RedisCallback} interface.
|
||||
* It provides {@link RedisConnection} handling such that neither the {@link RedisCallback} implementation nor
|
||||
* the calling code needs to explicitly care about retrieving/closing Redis connections, or handling Connection
|
||||
* lifecycle exceptions. For typical single step actions, there are various convenience methods.
|
||||
* <p/>
|
||||
* Once configured, this class is thread-safe.
|
||||
*
|
||||
* <p/>Note that while the template is generified, it is up to the serializers/deserializers to properly convert the given Objects
|
||||
* to and from binary data.
|
||||
* <p/>
|
||||
* <b>This is the central class in Redis support</b>.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @param <K> the Redis key type against which the template works (usually a String)
|
||||
* @param <V> the Redis value type against which the template works
|
||||
* @see StringRedisTemplate
|
||||
*/
|
||||
public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V> {
|
||||
|
||||
private boolean exposeConnection = false;
|
||||
private RedisSerializer<?> defaultSerializer = new JdkSerializationRedisSerializer();
|
||||
|
||||
private RedisSerializer keySerializer = null;
|
||||
private RedisSerializer valueSerializer = null;
|
||||
private RedisSerializer hashKeySerializer = null;
|
||||
private RedisSerializer hashValueSerializer = null;
|
||||
private RedisSerializer<String> stringSerializer = new StringRedisSerializer();
|
||||
|
||||
// cache singleton objects (where possible)
|
||||
private ValueOperations<K, V> valueOps;
|
||||
private ListOperations<K, V> listOps;
|
||||
private SetOperations<K, V> setOps;
|
||||
private ZSetOperations<K, V> zSetOps;
|
||||
|
||||
/**
|
||||
* Constructs a new <code>RedisTemplate</code> instance.
|
||||
*
|
||||
*/
|
||||
public RedisTemplate() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
super.afterPropertiesSet();
|
||||
boolean defaultUsed = false;
|
||||
|
||||
if (keySerializer == null) {
|
||||
keySerializer = defaultSerializer;
|
||||
defaultUsed = true;
|
||||
}
|
||||
if (valueSerializer == null) {
|
||||
valueSerializer = defaultSerializer;
|
||||
defaultUsed = true;
|
||||
}
|
||||
|
||||
if (hashKeySerializer == null) {
|
||||
hashKeySerializer = defaultSerializer;
|
||||
defaultUsed = true;
|
||||
}
|
||||
|
||||
if (hashValueSerializer == null) {
|
||||
hashValueSerializer = defaultSerializer;
|
||||
defaultUsed = true;
|
||||
}
|
||||
|
||||
if (defaultUsed) {
|
||||
Assert.notNull(defaultSerializer, "default serializer null and not all serializers initialized");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T execute(RedisCallback<T> action) {
|
||||
return execute(action, isExposeConnection());
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given action object within a connection, which can be exposed or not.
|
||||
*
|
||||
* @param <T> return type
|
||||
* @param action callback object that specifies the Redis action
|
||||
* @param exposeConnection whether to enforce exposure of the native Redis Connection to callback code
|
||||
* @return object returned by the action
|
||||
*/
|
||||
public <T> T execute(RedisCallback<T> action, boolean exposeConnection) {
|
||||
return execute(action, exposeConnection, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given action object within a connection that can be exposed or not. Additionally, the connection
|
||||
* can be pipelined. Note the results of the pipeline are discarded (making it suitable for write-only scenarios).
|
||||
* Use {@link #executePipelined(RedisCallback)} as an alternative.
|
||||
*
|
||||
* @param <T> return type
|
||||
* @param action callback object to execute
|
||||
* @param exposeConnection whether to enforce exposure of the native Redis Connection to callback code
|
||||
* @param pipeline whether to pipeline or not the connection for the execution
|
||||
* @return object returned by the action
|
||||
*/
|
||||
public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {
|
||||
Assert.notNull(action, "Callback object must not be null");
|
||||
|
||||
RedisConnectionFactory factory = getConnectionFactory();
|
||||
RedisConnection conn = RedisConnectionUtils.getConnection(factory);
|
||||
|
||||
boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);
|
||||
preProcessConnection(conn, existingConnection);
|
||||
|
||||
boolean pipelineStatus = conn.isPipelined();
|
||||
if (pipeline && !pipelineStatus) {
|
||||
conn.openPipeline();
|
||||
}
|
||||
|
||||
try {
|
||||
RedisConnection connToExpose = (exposeConnection ? conn : createRedisConnectionProxy(conn));
|
||||
T result = action.doInRedis(connToExpose);
|
||||
// TODO: any other connection processing?
|
||||
return postProcessResult(result, conn, existingConnection);
|
||||
} finally {
|
||||
try {
|
||||
if (pipeline && !pipelineStatus) {
|
||||
conn.closePipeline();
|
||||
}
|
||||
} finally {
|
||||
RedisConnectionUtils.releaseConnection(conn, factory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T execute(SessionCallback<T> session) {
|
||||
RedisConnectionFactory factory = getConnectionFactory();
|
||||
// bind connection
|
||||
RedisConnectionUtils.bindConnection(factory);
|
||||
try {
|
||||
return session.execute(this);
|
||||
} finally {
|
||||
RedisConnectionUtils.unbindConnection(factory);
|
||||
}
|
||||
}
|
||||
|
||||
// @SuppressWarnings("unchecked")
|
||||
// public List<V> executePipelined(final RedisCallback<?> action) {
|
||||
// return executePipelined(action, valueSerializer);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Executes the given action object on a pipelined connection, returning the results using a dedicated serializer.
|
||||
// * Note that the callback <b>cannot</b> return a non-null value as it gets overwritten by the pipeline.
|
||||
// *
|
||||
// * @param action callback object to execute
|
||||
// * @param resultSerializer
|
||||
// * @return list of objects returned by the pipeline
|
||||
// */
|
||||
// public <T> List<T> executePipelined(final RedisCallback<?> action, final RedisSerializer<T> resultSerializer) {
|
||||
// return execute(new RedisCallback<List<T>>() {
|
||||
// public List<T> doInRedis(RedisConnection connection) throws DataAccessException {
|
||||
// connection.openPipeline();
|
||||
// boolean pipelinedClosed = false;
|
||||
// try {
|
||||
// Object result = action.doInRedis(connection);
|
||||
// if (result != null) {
|
||||
// throw new InvalidDataAccessApiUsageException(
|
||||
// "Callback cannot returned a non-null value as it gets overwritten by the pipeline");
|
||||
// }
|
||||
// List<Object> closePipeline = connection.closePipeline();
|
||||
// pipelinedClosed = true;
|
||||
// //return SerializationUtils.deserialize(pipeline, resultSerializer);
|
||||
//
|
||||
// } finally {
|
||||
// if (!pipelinedClosed) {
|
||||
// connection.closePipeline();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
protected RedisConnection createRedisConnectionProxy(RedisConnection pm) {
|
||||
Class<?>[] ifcs = ClassUtils.getAllInterfacesForClass(pm.getClass(), getClass().getClassLoader());
|
||||
return (RedisConnection) Proxy.newProxyInstance(pm.getClass().getClassLoader(), ifcs,
|
||||
new CloseSuppressingInvocationHandler(pm));
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the connection (before any settings are executed on it). Default implementation returns the connection as is.
|
||||
*
|
||||
* @param connection redis connection
|
||||
*/
|
||||
protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
|
||||
return connection;
|
||||
}
|
||||
|
||||
protected <T> T postProcessResult(T result, RedisConnection conn, boolean existingConnection) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether to expose the native Redis connection to RedisCallback code, or rather a connection proxy (the default).
|
||||
*
|
||||
* @return whether to expose the native Redis connection or not
|
||||
*/
|
||||
public boolean isExposeConnection() {
|
||||
return exposeConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to expose the Redis connection to {@link RedisCallback} code.
|
||||
*
|
||||
* Default is "false": a proxy will be returned, suppressing <tt>quit</tt> and <tt>disconnect</tt> calls.
|
||||
*
|
||||
* @param exposeConnection
|
||||
*/
|
||||
public void setExposeConnection(boolean exposeConnection) {
|
||||
this.exposeConnection = exposeConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default serializer used by this template.
|
||||
*
|
||||
* @return template default serializer
|
||||
*/
|
||||
public RedisSerializer<?> getDefaultSerializer() {
|
||||
return defaultSerializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default serializer to use for this template. All serializers (expect the {@link #setStringSerializer(RedisSerializer)}) are
|
||||
* initialized to this value unless explicitly set. Defaults to {@link JdkSerializationRedisSerializer}.
|
||||
*
|
||||
* @param serializer default serializer to use
|
||||
*/
|
||||
public void setDefaultSerializer(RedisSerializer<?> serializer) {
|
||||
this.defaultSerializer = serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the key serializer to be used by this template. Defaults to {@link #getDefaultSerializer()}.
|
||||
*
|
||||
* @param serializer the key serializer to be used by this template.
|
||||
*/
|
||||
public void setKeySerializer(RedisSerializer<?> serializer) {
|
||||
this.keySerializer = serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key serializer used by this template.
|
||||
*
|
||||
* @return the key serializer used by this template.
|
||||
*/
|
||||
public RedisSerializer<?> getKeySerializer() {
|
||||
return keySerializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value serializer to be used by this template. Defaults to {@link #getDefaultSerializer()}.
|
||||
*
|
||||
* @param serializer the value serializer to be used by this template.
|
||||
*/
|
||||
public void setValueSerializer(RedisSerializer<?> serializer) {
|
||||
this.valueSerializer = serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value serializer used by this template.
|
||||
*
|
||||
* @return the value serializer used by this template.
|
||||
*/
|
||||
public RedisSerializer<?> getValueSerializer() {
|
||||
return valueSerializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hashKeySerializer.
|
||||
*
|
||||
* @return Returns the hashKeySerializer
|
||||
*/
|
||||
public RedisSerializer<?> getHashKeySerializer() {
|
||||
return hashKeySerializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the hash key (or field) serializer to be used by this template. Defaults to {@link #getDefaultSerializer()}.
|
||||
*
|
||||
* @param hashKeySerializer The hashKeySerializer to set.
|
||||
*/
|
||||
public void setHashKeySerializer(RedisSerializer<?> hashKeySerializer) {
|
||||
this.hashKeySerializer = hashKeySerializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hashValueSerializer.
|
||||
*
|
||||
* @return Returns the hashValueSerializer
|
||||
*/
|
||||
public RedisSerializer<?> getHashValueSerializer() {
|
||||
return hashValueSerializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the hash value serializer to be used by this template. Defaults to {@link #getDefaultSerializer()}.
|
||||
*
|
||||
* @param hashValueSerializer The hashValueSerializer to set.
|
||||
*/
|
||||
public void setHashValueSerializer(RedisSerializer<?> hashValueSerializer) {
|
||||
this.hashValueSerializer = hashValueSerializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stringSerializer.
|
||||
*
|
||||
* @return Returns the stringSerializer
|
||||
*/
|
||||
public RedisSerializer<String> getStringSerializer() {
|
||||
return stringSerializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the string value serializer to be used by this template (when the arguments or return types
|
||||
* are always strings). Defaults to {@link StringRedisSerializer}.
|
||||
*
|
||||
* @see ValueOperations#get(Object, long, long)
|
||||
* @param stringSerializer The stringValueSerializer to set.
|
||||
*/
|
||||
public void setStringSerializer(RedisSerializer<String> stringSerializer) {
|
||||
this.stringSerializer = stringSerializer;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private byte[] rawKey(Object key) {
|
||||
Assert.notNull(key, "non null key required");
|
||||
return keySerializer.serialize(key);
|
||||
}
|
||||
|
||||
private byte[] rawString(String key) {
|
||||
return stringSerializer.serialize(key);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private byte[] rawValue(Object value) {
|
||||
return valueSerializer.serialize(value);
|
||||
}
|
||||
|
||||
private byte[][] rawKeys(Collection<K> keys) {
|
||||
final byte[][] rawKeys = new byte[keys.size()][];
|
||||
|
||||
int i = 0;
|
||||
for (K key : keys) {
|
||||
rawKeys[i++] = rawKey(key);
|
||||
}
|
||||
|
||||
return rawKeys;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private K deserializeKey(byte[] value) {
|
||||
return (K) keySerializer.deserialize(value);
|
||||
}
|
||||
|
||||
//
|
||||
// RedisOperations
|
||||
//
|
||||
@Override
|
||||
public List<Object> exec() {
|
||||
return execute(new RedisCallback<List<Object>>() {
|
||||
|
||||
@Override
|
||||
public List<Object> doInRedis(RedisConnection connection) throws DataAccessException {
|
||||
return connection.exec();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(K key) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
|
||||
execute(new RedisCallback<Object>() {
|
||||
@Override
|
||||
public Object doInRedis(RedisConnection connection) {
|
||||
connection.del(rawKey);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Collection<K> keys) {
|
||||
final byte[][] rawKeys = rawKeys(keys);
|
||||
|
||||
execute(new RedisCallback<Object>() {
|
||||
@Override
|
||||
public Object doInRedis(RedisConnection connection) {
|
||||
connection.del(rawKeys);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean hasKey(K key) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
|
||||
return execute(new RedisCallback<Boolean>() {
|
||||
@Override
|
||||
public Boolean doInRedis(RedisConnection connection) {
|
||||
return connection.exists(rawKey);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean expire(K key, long timeout, TimeUnit unit) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final int rawTimeout = (int) unit.toSeconds(timeout);
|
||||
|
||||
return execute(new RedisCallback<Boolean>() {
|
||||
@Override
|
||||
public Boolean doInRedis(RedisConnection connection) {
|
||||
return connection.expire(rawKey, rawTimeout);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean expireAt(K key, Date date) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
final long rawTimeout = date.getTime();
|
||||
|
||||
return execute(new RedisCallback<Boolean>() {
|
||||
@Override
|
||||
public Boolean doInRedis(RedisConnection connection) {
|
||||
return connection.expireAt(rawKey, rawTimeout);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertAndSend(String channel, Object message) {
|
||||
Assert.hasText(channel, "a non-empty channel is required");
|
||||
|
||||
final byte[] rawChannel = rawString(channel);
|
||||
final byte[] rawMessage = rawValue(message);
|
||||
|
||||
execute(new RedisCallback<Object>() {
|
||||
@Override
|
||||
public Object doInRedis(RedisConnection connection) {
|
||||
connection.publish(rawChannel, rawMessage);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Value operations
|
||||
//
|
||||
|
||||
@Override
|
||||
public Long getExpire(K key) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
|
||||
return execute(new RedisCallback<Long>() {
|
||||
@Override
|
||||
public Long doInRedis(RedisConnection connection) {
|
||||
return Long.valueOf(connection.ttl(rawKey));
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Set<K> keys(K pattern) {
|
||||
final byte[] rawKey = rawKey(pattern);
|
||||
|
||||
Set<byte[]> rawKeys = execute(new RedisCallback<Set<byte[]>>() {
|
||||
@Override
|
||||
public Set<byte[]> doInRedis(RedisConnection connection) {
|
||||
return connection.keys(rawKey);
|
||||
}
|
||||
}, true);
|
||||
|
||||
return SerializationUtils.deserialize(rawKeys, keySerializer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean persist(K key) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
|
||||
return execute(new RedisCallback<Boolean>() {
|
||||
@Override
|
||||
public Boolean doInRedis(RedisConnection connection) {
|
||||
return connection.persist(rawKey);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean move(K key, final int dbIndex) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
|
||||
return execute(new RedisCallback<Boolean>() {
|
||||
@Override
|
||||
public Boolean doInRedis(RedisConnection connection) {
|
||||
return connection.move(rawKey, dbIndex);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public K randomKey() {
|
||||
byte[] rawKey = execute(new RedisCallback<byte[]>() {
|
||||
@Override
|
||||
public byte[] doInRedis(RedisConnection connection) {
|
||||
return connection.randomKey();
|
||||
}
|
||||
}, true);
|
||||
|
||||
return deserializeKey(rawKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rename(K oldKey, K newKey) {
|
||||
final byte[] rawOldKey = rawKey(oldKey);
|
||||
final byte[] rawNewKey = rawKey(newKey);
|
||||
|
||||
execute(new RedisCallback<Object>() {
|
||||
@Override
|
||||
public Object doInRedis(RedisConnection connection) {
|
||||
connection.rename(rawOldKey, rawNewKey);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean renameIfAbsent(K oldKey, K newKey) {
|
||||
final byte[] rawOldKey = rawKey(oldKey);
|
||||
final byte[] rawNewKey = rawKey(newKey);
|
||||
|
||||
return execute(new RedisCallback<Boolean>() {
|
||||
@Override
|
||||
public Boolean doInRedis(RedisConnection connection) {
|
||||
return connection.renameNX(rawOldKey, rawNewKey);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType type(K key) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
|
||||
return execute(new RedisCallback<DataType>() {
|
||||
@Override
|
||||
public DataType doInRedis(RedisConnection connection) {
|
||||
return connection.type(rawKey);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void multi() {
|
||||
execute(new RedisCallback<Object>() {
|
||||
@Override
|
||||
public Object doInRedis(RedisConnection connection) throws DataAccessException {
|
||||
connection.multi();
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void discard() {
|
||||
execute(new RedisCallback<Object>() {
|
||||
|
||||
@Override
|
||||
public Object doInRedis(RedisConnection connection) throws DataAccessException {
|
||||
connection.discard();
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void watch(K key) {
|
||||
final byte[] rawKey = rawKey(key);
|
||||
|
||||
execute(new RedisCallback<Object>() {
|
||||
@Override
|
||||
public Object doInRedis(RedisConnection connection) {
|
||||
connection.watch(rawKey);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void watch(Collection<K> keys) {
|
||||
final byte[][] rawKeys = rawKeys(keys);
|
||||
|
||||
execute(new RedisCallback<Object>() {
|
||||
@Override
|
||||
public Object doInRedis(RedisConnection connection) {
|
||||
connection.watch(rawKeys);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unwatch() {
|
||||
execute(new RedisCallback<Object>() {
|
||||
@Override
|
||||
public Object doInRedis(RedisConnection connection) throws DataAccessException {
|
||||
connection.unwatch();
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
// Sort operations
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public List<V> sort(SortQuery<K> query) {
|
||||
return sort(query, valueSerializer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> sort(SortQuery<K> query, RedisSerializer<T> resultSerializer) {
|
||||
final byte[] rawKey = rawKey(query.getKey());
|
||||
final SortParameters params = QueryUtils.convertQuery(query, stringSerializer);
|
||||
|
||||
List<byte[]> vals = execute(new RedisCallback<List<byte[]>>() {
|
||||
@Override
|
||||
public List<byte[]> doInRedis(RedisConnection connection) throws DataAccessException {
|
||||
return connection.sort(rawKey, params);
|
||||
}
|
||||
}, true);
|
||||
|
||||
return SerializationUtils.deserialize(vals, resultSerializer);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> List<T> sort(SortQuery<K> query, BulkMapper<T, V> bulkMapper) {
|
||||
return sort(query, bulkMapper, valueSerializer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, S> List<T> sort(SortQuery<K> query, BulkMapper<T, S> bulkMapper, RedisSerializer<S> resultSerializer) {
|
||||
List<S> values = sort(query, resultSerializer);
|
||||
|
||||
int bulkSize = query.getGetPattern().size();
|
||||
List<T> result = new ArrayList<T>(values.size() / bulkSize + 1);
|
||||
|
||||
List<S> bulk = new ArrayList<S>(bulkSize);
|
||||
for (S s : values) {
|
||||
|
||||
bulk.add(s);
|
||||
if (bulk.size() == bulkSize) {
|
||||
result.add(bulkMapper.mapBulk(Collections.unmodifiableList(bulk)));
|
||||
// create a new list (we could reuse the old one but the client might hang on to it for some reason)
|
||||
bulk = new ArrayList<S>(bulkSize);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long sort(SortQuery<K> query, K storeKey) {
|
||||
final byte[] rawStoreKey = rawKey(storeKey);
|
||||
final byte[] rawKey = rawKey(query.getKey());
|
||||
final SortParameters params = QueryUtils.convertQuery(query, stringSerializer);
|
||||
|
||||
return execute(new RedisCallback<Long>() {
|
||||
@Override
|
||||
public Long doInRedis(RedisConnection connection) throws DataAccessException {
|
||||
return connection.sort(rawKey, params, rawStoreKey);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoundValueOperations<K, V> boundValueOps(K key) {
|
||||
return new DefaultBoundValueOperations<K, V>(key, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueOperations<K, V> opsForValue() {
|
||||
if (valueOps == null) {
|
||||
valueOps = new DefaultValueOperations<K, V>(this);
|
||||
}
|
||||
return valueOps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListOperations<K, V> opsForList() {
|
||||
if (listOps == null) {
|
||||
listOps = new DefaultListOperations<K, V>(this);
|
||||
}
|
||||
return listOps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoundListOperations<K, V> boundListOps(K key) {
|
||||
return new DefaultBoundListOperations<K, V>(key, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoundSetOperations<K, V> boundSetOps(K key) {
|
||||
return new DefaultBoundSetOperations<K, V>(key, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SetOperations<K, V> opsForSet() {
|
||||
if (setOps == null) {
|
||||
setOps = new DefaultSetOperations<K, V>(this);
|
||||
}
|
||||
return setOps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoundZSetOperations<K, V> boundZSetOps(K key) {
|
||||
return new DefaultBoundZSetOperations<K, V>(key, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ZSetOperations<K, V> opsForZSet() {
|
||||
if (zSetOps == null) {
|
||||
zSetOps = new DefaultZSetOperations<K, V>(this);
|
||||
}
|
||||
return zSetOps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <HK, HV> BoundHashOperations<K, HK, HV> boundHashOps(K key) {
|
||||
return new DefaultBoundHashOperations<K, HK, HV>(key, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <HK, HV> HashOperations<K, HK, HV> opsForHash() {
|
||||
return new DefaultHashOperations<K, HK, HV>(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.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.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface SessionCallback<T> {
|
||||
|
||||
/**
|
||||
* Executes all the given operations inside the same session.
|
||||
*
|
||||
* @param operations Redis operations
|
||||
* @return return value
|
||||
*/
|
||||
<K, V> T execute(RedisOperations<K, V> operations) throws DataAccessException;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Redis set specific operations.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface SetOperations<K, V> {
|
||||
|
||||
Set<V> difference(K key, K otherKey);
|
||||
|
||||
Set<V> difference(K key, Collection<K> otherKeys);
|
||||
|
||||
void differenceAndStore(K key, K otherKey, K destKey);
|
||||
|
||||
void differenceAndStore(K key, Collection<K> otherKeys, K destKey);
|
||||
|
||||
Set<V> intersect(K key, K otherKey);
|
||||
|
||||
Set<V> intersect(K key, Collection<K> otherKeys);
|
||||
|
||||
void intersectAndStore(K key, K otherKey, K destKey);
|
||||
|
||||
void intersectAndStore(K key, Collection<K> otherKeys, K destKey);
|
||||
|
||||
Set<V> union(K key, K otherKey);
|
||||
|
||||
Set<V> union(K key, Collection<K> otherKeys);
|
||||
|
||||
void unionAndStore(K key, K otherKey, K destKey);
|
||||
|
||||
void unionAndStore(K key, Collection<K> otherKeys, K destKey);
|
||||
|
||||
Boolean add(K key, V value);
|
||||
|
||||
Boolean isMember(K key, Object o);
|
||||
|
||||
Set<V> members(K key);
|
||||
|
||||
Boolean move(K key, V value, K destKey);
|
||||
|
||||
V randomMember(K key);
|
||||
|
||||
Boolean remove(K key, Object o);
|
||||
|
||||
V pop(K key);
|
||||
|
||||
Long size(K key);
|
||||
|
||||
RedisOperations<K, V> getOperations();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
|
||||
/**
|
||||
* PropertyEditor allowing for easy injection of {@link SetOperations} from
|
||||
* {@link RedisOperations}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class SetOperationsEditor extends PropertyEditorSupport {
|
||||
|
||||
@Override
|
||||
public void setValue(Object value) {
|
||||
if (value instanceof RedisOperations) {
|
||||
super.setValue(((RedisOperations) value).opsForSet());
|
||||
}
|
||||
else {
|
||||
throw new java.lang.IllegalArgumentException("Editor supports only conversion of type "
|
||||
+ RedisOperations.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import org.springframework.data.redis.connection.DefaultStringRedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.StringRedisConnection;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
/**
|
||||
* String-focused extension of RedisTemplate. Since most operations against Redis are String based,
|
||||
* this class provides a dedicated class that minimizes configuration of its more generic
|
||||
* {@link RedisTemplate template} especially in terms of serializers.
|
||||
*
|
||||
* <p/> Note that this template exposes the {@link RedisConnection} used by the {@link RedisCallback}
|
||||
* as a {@link StringRedisConnection}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class StringRedisTemplate extends RedisTemplate<String, String> {
|
||||
|
||||
/**
|
||||
* Constructs a new <code>StringRedisTemplate</code> instance.
|
||||
* {@link #setConnectionFactory(RedisConnectionFactory)} and {@link #afterPropertiesSet()} still need to be called.
|
||||
*
|
||||
*/
|
||||
public StringRedisTemplate() {
|
||||
RedisSerializer<String> stringSerializer = new StringRedisSerializer();
|
||||
setKeySerializer(stringSerializer);
|
||||
setValueSerializer(stringSerializer);
|
||||
setHashKeySerializer(stringSerializer);
|
||||
setHashValueSerializer(stringSerializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>StringRedisTemplate</code> instance ready to be used.
|
||||
*
|
||||
* @param connectionFactory connection factory for creating new connections
|
||||
*/
|
||||
public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
this();
|
||||
setConnectionFactory(connectionFactory);
|
||||
afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
|
||||
return new DefaultStringRedisConnection(connection);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Redis operations for simple (or in Redis terminology 'string') values.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface ValueOperations<K, V> {
|
||||
|
||||
void set(K key, V value);
|
||||
|
||||
void set(K key, V value, long timeout, TimeUnit unit);
|
||||
|
||||
Boolean setIfAbsent(K key, V value);
|
||||
|
||||
void multiSet(Map<? extends K, ? extends V> m);
|
||||
|
||||
void multiSetIfAbsent(Map<? extends K, ? extends V> m);
|
||||
|
||||
V get(Object key);
|
||||
|
||||
V getAndSet(K key, V value);
|
||||
|
||||
List<V> multiGet(Collection<K> keys);
|
||||
|
||||
Long increment(K key, long delta);
|
||||
|
||||
Integer append(K key, String value);
|
||||
|
||||
String get(K key, long start, long end);
|
||||
|
||||
void set(K key, V value, long offset);
|
||||
|
||||
Long size(K key);
|
||||
|
||||
RedisOperations<K, V> getOperations();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
|
||||
/**
|
||||
* PropertyEditor allowing for easy injection of {@link ValueOperations} from
|
||||
* {@link RedisOperations}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class ValueOperationsEditor extends PropertyEditorSupport {
|
||||
|
||||
@Override
|
||||
public void setValue(Object value) {
|
||||
if (value instanceof RedisOperations) {
|
||||
super.setValue(((RedisOperations) value).opsForValue());
|
||||
}
|
||||
else {
|
||||
throw new java.lang.IllegalArgumentException("Editor supports only conversion of type "
|
||||
+ RedisOperations.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Redis ZSet/sorted set specific operations.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface ZSetOperations<K, V> {
|
||||
|
||||
/**
|
||||
* Typed ZSet tuple.
|
||||
*/
|
||||
public interface TypedTuple<V> {
|
||||
V getValue();
|
||||
|
||||
Double getScore();
|
||||
}
|
||||
|
||||
void intersectAndStore(K key, K otherKey, K destKey);
|
||||
|
||||
void intersectAndStore(K key, Collection<K> otherKeys, K destKey);
|
||||
|
||||
void unionAndStore(K key, K otherKey, K destKey);
|
||||
|
||||
void unionAndStore(K key, Collection<K> otherKeys, K destKey);
|
||||
|
||||
Set<V> range(K key, long start, long end);
|
||||
|
||||
Set<V> reverseRange(K key, long start, long end);
|
||||
|
||||
Set<TypedTuple<V>> rangeWithScores(K key, long start, long end);
|
||||
|
||||
Set<TypedTuple<V>> reverseRangeWithScores(K key, long start, long end);
|
||||
|
||||
Set<V> rangeByScore(K key, double min, double max);
|
||||
|
||||
Set<V> reverseRangeByScore(K key, double min, double max);
|
||||
|
||||
Set<TypedTuple<V>> rangeByScoreWithScores(K key, double min, double max);
|
||||
|
||||
Set<TypedTuple<V>> reverseRangeByScoreWithScores(K key, double min, double max);
|
||||
|
||||
Boolean add(K key, V value, double score);
|
||||
|
||||
Double incrementScore(K key, V value, double delta);
|
||||
|
||||
Long rank(K key, Object o);
|
||||
|
||||
Long reverseRank(K key, Object o);
|
||||
|
||||
Double score(K key, Object o);
|
||||
|
||||
Boolean remove(K key, Object o);
|
||||
|
||||
void removeRange(K key, long start, long end);
|
||||
|
||||
void removeRangeByScore(K key, double min, double max);
|
||||
|
||||
Long count(K key, double min, double max);
|
||||
|
||||
Long size(K key);
|
||||
|
||||
RedisOperations<K, V> getOperations();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.redis.core;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
|
||||
/**
|
||||
* PropertyEditor allowing for easy injection of {@link ZSetOperations} from
|
||||
* {@link RedisOperations}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class ZSetOperationsEditor extends PropertyEditorSupport {
|
||||
|
||||
@Override
|
||||
public void setValue(Object value) {
|
||||
if (value instanceof RedisOperations) {
|
||||
super.setValue(((RedisOperations) value).opsForZSet());
|
||||
}
|
||||
else {
|
||||
throw new java.lang.IllegalArgumentException("Editor supports only conversion of type "
|
||||
+ RedisOperations.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Core package for integrating <a href="http://code.google.com/p/redis/">Redis</a> with Spring concepts.
|
||||
*
|
||||
* <p/>Provides template support and callback for low-level access.
|
||||
*/
|
||||
package org.springframework.data.redis.core;
|
||||
|
||||
@@ -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.redis.core.query;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.redis.connection.SortParameters.Order;
|
||||
import org.springframework.data.redis.connection.SortParameters.Range;
|
||||
|
||||
/**
|
||||
* Default implementation for {@link SortCriterion}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class DefaultSortCriterion<K> implements SortCriterion<K> {
|
||||
|
||||
private final K key;
|
||||
private String by;
|
||||
private final List<String> getKeys = new ArrayList<String>(4);
|
||||
|
||||
private Range limit;
|
||||
private Order order;
|
||||
private Boolean alpha;
|
||||
|
||||
DefaultSortCriterion(K key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortCriterion<K> alphabetical(boolean alpha) {
|
||||
this.alpha = Boolean.valueOf(alpha);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortQuery<K> build() {
|
||||
return new DefaultSortQuery<K>(key, by, limit, order, alpha, getKeys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortCriterion<K> limit(long offset, long count) {
|
||||
this.limit = new Range(offset, count);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortCriterion<K> limit(Range range) {
|
||||
this.limit = range;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortCriterion<K> order(Order order) {
|
||||
this.order = order;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortCriterion<K> get(String getPattern) {
|
||||
this.getKeys.add(getPattern);
|
||||
return this;
|
||||
}
|
||||
|
||||
SortCriterion<K> addBy(String keyPattern) {
|
||||
this.by = keyPattern;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.redis.core.query;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.redis.connection.SortParameters.Order;
|
||||
import org.springframework.data.redis.connection.SortParameters.Range;
|
||||
|
||||
/**
|
||||
* Default SortQuery implementation.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class DefaultSortQuery<K> implements SortQuery<K> {
|
||||
|
||||
private final K key;
|
||||
private final Boolean alpha;
|
||||
private final Order order;
|
||||
private final Range limit;
|
||||
private final String by;
|
||||
private final List<String> gets;
|
||||
|
||||
DefaultSortQuery(K key, String by, Range limit, Order order, Boolean alpha, List<String> gets) {
|
||||
this.key = key;
|
||||
this.by = by;
|
||||
this.limit = limit;
|
||||
this.order = order;
|
||||
this.alpha = alpha;
|
||||
this.gets = gets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBy() {
|
||||
return by;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range getLimit() {
|
||||
return limit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Order getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isAlphabetic() {
|
||||
return alpha;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getGetPattern() {
|
||||
return gets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DefaultSortQuery [alpha=" + alpha + ", by=" + by + ", gets=" + gets + ", key=" + key + ", limit="
|
||||
+ limit + ", order=" + order + "]";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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.redis.core.query;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.redis.connection.DefaultSortParameters;
|
||||
import org.springframework.data.redis.connection.SortParameters;
|
||||
import org.springframework.data.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,39 @@
|
||||
/*
|
||||
* 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.redis.core.query;
|
||||
|
||||
import org.springframework.data.redis.connection.SortParameters.Order;
|
||||
import org.springframework.data.redis.connection.SortParameters.Range;
|
||||
|
||||
/**
|
||||
* Internal interface part of the Sort DSL. Exposes generic operations.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface SortCriterion<K> {
|
||||
|
||||
SortCriterion<K> limit(long offset, long count);
|
||||
|
||||
SortCriterion<K> limit(Range range);
|
||||
|
||||
SortCriterion<K> order(Order order);
|
||||
|
||||
SortCriterion<K> alphabetical(boolean alpha);
|
||||
|
||||
SortCriterion<K> get(String pattern);
|
||||
|
||||
SortQuery<K> build();
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.redis.core.query;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.SortParameters;
|
||||
import org.springframework.data.redis.connection.SortParameters.Order;
|
||||
import org.springframework.data.redis.connection.SortParameters.Range;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
|
||||
/**
|
||||
* High-level abstraction over a Redis SORT (generified equivalent of {@link SortParameters}). To be used with {@link RedisTemplate}
|
||||
* (just as {@link SortParameters} is used by {@link RedisConnection}).
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface SortQuery<K> {
|
||||
|
||||
/**
|
||||
* Returns the sorting order. Can be null if nothing is specified.
|
||||
*
|
||||
* @return sorting order
|
||||
*/
|
||||
Order getOrder();
|
||||
|
||||
/**
|
||||
* Indicates if the sorting is numeric (default) or alphabetical (lexicographical).
|
||||
* Can be null if nothing is specified.
|
||||
*
|
||||
* @return the type of sorting
|
||||
*/
|
||||
Boolean isAlphabetic();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the sorting limit (range or pagination).
|
||||
* Can be null if nothing is specified.
|
||||
*
|
||||
* @return sorting limit/range
|
||||
*/
|
||||
Range getLimit();
|
||||
|
||||
/**
|
||||
* Return the target key for sorting.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
K getKey();
|
||||
|
||||
/**
|
||||
* Returns the pattern of the external key used for sorting.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getBy();
|
||||
|
||||
/**
|
||||
* Returns the external key(s) whose values are returned by the sort.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
List<String> getGetPattern();
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.redis.core.query;
|
||||
|
||||
|
||||
/**
|
||||
* Simple builder class for constructing {@link SortQuery}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class SortQueryBuilder<K> extends DefaultSortCriterion<K> {
|
||||
|
||||
private static final String NO_SORT_KEY = "~";
|
||||
|
||||
private SortQueryBuilder(K key) {
|
||||
super(key);
|
||||
}
|
||||
|
||||
public static <K> SortQueryBuilder<K> sort(K key) {
|
||||
return new SortQueryBuilder<K>(key);
|
||||
}
|
||||
|
||||
public SortCriterion<K> by(String keyPattern) {
|
||||
return addBy(keyPattern);
|
||||
}
|
||||
|
||||
public SortCriterion<K> noSort() {
|
||||
return by(NO_SORT_KEY);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user