+ renamed org.springframework.data.keyvalue.redis to o.s.d.redis

+ eliminated Riak package
+ eliminate Core package
This commit is contained in:
Costin Leau
2011-07-05 19:18:31 +03:00
parent f242a95620
commit 3217289401
293 changed files with 794 additions and 8579 deletions

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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>&lt;collection&gt;</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));
}
}

View File

@@ -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>&lt;listener-container&gt;</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;
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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 {
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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
}
}

View File

@@ -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);
}
}
}

View File

@@ -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()][]);
}
}

View File

@@ -0,0 +1,5 @@
/**
* Connection package for <a href="http://github.com/xetorthio/jedis">Jedis</a> library.
*/
package org.springframework.data.redis.connection.jedis;

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,5 @@
/**
* Connection package for <a href="http://github.com/alphazero/jredis">JRedis</a> library.
*/
package org.springframework.data.redis.connection.jredis;

View File

@@ -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;

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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));
}
}

View File

@@ -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));
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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));
}
}
}
}

View File

@@ -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 (&lt 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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,5 @@
/**
* Internal utility package for encoding/decoding Strings to byte[] (using Base64) library.
*/
package org.springframework.data.redis.connection.util;

View File

@@ -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);
}
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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();
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}

View File

@@ -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);
}
}
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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 + "]";
}
}

View File

@@ -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()][]);
}
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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