diff --git a/spring-datastore-redis/pom.xml b/spring-datastore-redis/pom.xml index 679e15f44..eb476bdd2 100644 --- a/spring-datastore-redis/pom.xml +++ b/spring-datastore-redis/pom.xml @@ -88,7 +88,7 @@ redis.clients jedis - 1.0.0-RC3 + 1.1.1 compile @@ -98,6 +98,13 @@ a.0-SNAPSHOT compile + + + org.springframework.commons + spring-commons-serializer + 1.0.0.BUILD-SNAPSHOT + compile + diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/CannotGetRedisConnectionException.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/CannotGetRedisConnectionException.java new file mode 100644 index 000000000..6775acff3 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/CannotGetRedisConnectionException.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002-2010 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.datastore.redis; + +import org.springframework.dao.DataAccessResourceFailureException; + +/** + * Fatal exception thrown when we can't connect to Redis. + * @author Mark Pollack + * + */ +public class CannotGetRedisConnectionException extends + DataAccessResourceFailureException { + + public CannotGetRedisConnectionException(String msg) { + super(msg); + } + + public CannotGetRedisConnectionException(String msg, Throwable cause) { + super(msg, cause); + } + +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/AbstractRedisClient.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/AbstractRedisClient.java new file mode 100644 index 000000000..458c2e0a8 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/AbstractRedisClient.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core; + +import java.io.UnsupportedEncodingException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.dao.InvalidDataAccessApiUsageException; + +/** + * Common base class for RedisClient implementations + * @author Mark Pollack + * + */ +public abstract class AbstractRedisClient implements RedisClient { + + protected final Log logger = LogFactory.getLog(this.getClass()); + + public static final String DEFAULT_CHARSET = "UTF-8"; + + private volatile String defaultCharset = DEFAULT_CHARSET; + + /** + * Specify the default charset to use when converting to or from text-based + * Message body content. If not specified, the charset will be "UTF-8". + */ + public void setDefaultCharset(String defaultCharset) { + this.defaultCharset = (defaultCharset != null) ? defaultCharset : DEFAULT_CHARSET; + } + + public String getDefaultCharset() { + return defaultCharset; + } + + protected byte[] stringToByte(String string) throws InvalidDataAccessApiUsageException { + try { + return string.getBytes(this.defaultCharset); + } catch (UnsupportedEncodingException e) { + throw new InvalidDataAccessApiUsageException(defaultCharset + + " encoding not supported.", e); + } + } + + protected String byteToString(byte[] value) throws InvalidDataAccessApiUsageException { + try { + return new String(value, defaultCharset); + } catch (UnsupportedEncodingException e) { + throw new InvalidDataAccessApiUsageException(defaultCharset + + " encoding not supported.", e); + } + } +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/AbstractRedisClientFactory.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/AbstractRedisClientFactory.java new file mode 100644 index 000000000..a23109bef --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/AbstractRedisClientFactory.java @@ -0,0 +1,102 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.datastore.redis.support.RedisPersistenceExceptionTranslator; + +/** + * Common base class for RedisClientFactories + * @author Mark Pollack + * + */ +public abstract class AbstractRedisClientFactory implements RedisClientFactory { + + protected final Log logger = LogFactory.getLog(getClass()); + + private String hostName; + + private int port; + + private String password; + + public int getPort() { + return port; + } + + protected void setPort(int port) { + this.port = port; + } + + + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getHostName() { + return hostName; + } + + protected void setHostName(String hostName) { + this.hostName = hostName; + } + + public RedisClient createClient() { + return doGetClient(); + } + + public abstract RedisClient doGetClient(); + + public abstract RedisPersistenceExceptionTranslator getExceptionTranslator(); + + + protected String getDefaultHostName() { + String temp; + try { + InetAddress localMachine = InetAddress.getLocalHost(); + temp = localMachine.getHostName(); + logger.debug("Using hostname [" + temp + "] for hostname."); + } + catch (UnknownHostException e) { + logger.warn("Could not get host name, using 'localhost' as default value", e); + temp = "localhost"; + } + return temp; + } + + /* + public void closeClient() { + if (logger.isDebugEnabled()) { + logger.debug("Closing Redis Client: " + this.client); + } + try { + client.close(); + } + catch (Throwable ex) { + logger.debug("Could not close Redis Client", ex); + } + }*/ + +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/DefaultServerOperations.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/DefaultServerOperations.java new file mode 100644 index 000000000..a2cea5aa4 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/DefaultServerOperations.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core; + +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class DefaultServerOperations implements ServerOperations { + + protected final Log logger = LogFactory.getLog(getClass()); + + private RedisOperations redisOperations; + + public DefaultServerOperations(RedisOperations redisOperations) { + this.redisOperations = redisOperations; + } + + public Map getServerInfo() { + return redisOperations.execute(new RedisCallback>() { + public Map doInRedis(RedisClient redisClient) + throws Exception { + return redisClient.info(); + } + }); + } + +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/KeyValueOperations.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/KeyValueOperations.java new file mode 100644 index 000000000..8e6c8485f --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/KeyValueOperations.java @@ -0,0 +1,126 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core; + +import java.util.List; +import java.util.Map; + +/** + * Key value operations with 'friendly' names instead of using command names for methods. + * Additional helper methods for working with keys and values + * + * @author Mark Pollack + * + */ +public interface KeyValueOperations { + + // Set and Set with expiry operations + + void set(String key, String value); + + void set(String key, String value, long expiryInMillis); + + void setAsBytes(String key, byte[] value); + + void setAsBytes(String key, byte[] value, long expiryInMillis); + + void convertAndSet(String key, Object value); + + void convertAndSet(String key, Object value, long expiryInMillis); + + // Get operations + + String get(String key); + + byte[] getAsBytes(String key); + + T getAndConvert(String key, Class requiredType); + + // Get and Set operations + + String getAndSet(String key, String value); + + byte[] getAndSetBytes(String key, byte[] value); + + T getAndSetObject(String key, T value, Class requiredType); + + // Multi-get operations + + List getValues(List keys); + + List getAndConvertValues(List keys, Class requiredType); + + + // Set if non-existent operations + + void setIfKeyNonExistent(String key, String value); + + void setIfKeyNonExistent(String key, byte[] value); + + void convertAndSetIfKeyNonExistent(String key, Object value); + + // Multiple key-value set + + void setMultiple(Map keysAndValues); + + void setMultipleAsBytes(Map keysAndValues); + + void convertAndSetMultiple(Map keysAndValues); + + // Multiple key-value set if non-existent + + void setMultipleIfKeysNonExistent(Map keysAndValues); + + void setMultipleAsBytesIfKeysNonExistent(Map keysAndValues); + + void convertAndSetMultipleIfKeysNonExistent(Map keysAndValues); + + + + // Append + + int append(String key, String value); + + + + // Increment + + int increment(String key); + + int incrementBy(String key, int value); + + // Decrement + + int decrement(String key); + + int decrementBy(String key, int value); + + + // Substring + + String getSubString(String key, int fromIndex, int toIndex); + + boolean containsKey(String key); + + boolean deleteKeys(String... keys); + + + + + + + +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/ListOperations.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/ListOperations.java new file mode 100644 index 000000000..481bb84b0 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/ListOperations.java @@ -0,0 +1,29 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core; + +/** + * List operations with 'friendly' names instead of using Redis command names for methods. + * + * May also include List specific helper methods from redis recipies. + * @author Mark Pollack + * + */ +public interface ListOperations { + + + //ListRecipies getListRecipies(); +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisAccessor.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisAccessor.java new file mode 100644 index 000000000..a24228fea --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisAccessor.java @@ -0,0 +1,68 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.util.Assert; + +/** + * Base class for {@link RedisTemplate} and + * other Redis-accessing DAO helpers, defining common properties such as + * RedisClientFactory. + * + * @author Mark Pollack + * + */ +public class RedisAccessor implements InitializingBean { + + /** Logger available to subclasses */ + protected final Log logger = LogFactory.getLog(getClass()); + + private volatile RedisClientFactory redisClientFactory; + + /** + * Set the RedisClientFactory to use for obtaining Redis {@link RedisClient clients}. + */ + public void setRedisClientFactory(RedisClientFactory redisClientFactory) { + this.redisClientFactory = redisClientFactory; + } + + + /** + * Return the RedisClientFactory that this accessor uses for obtaining + * Redis {@link RedisClient Clients}. + */ + public RedisClientFactory getRedisClientFactory() { + return this.redisClientFactory; + } + + /** + * Create a Redis Client + * @return the new Redis Client + * @throws TODO + */ + protected RedisClient createClient() { + return this.redisClientFactory.createClient(); + } + + + public void afterPropertiesSet() { + Assert.notNull(getRedisClientFactory(), "RedisClientfactory is required"); + } + +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisCallback.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisCallback.java new file mode 100644 index 000000000..9e2de19a4 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisCallback.java @@ -0,0 +1,31 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core; + +/** + * Basic callback for use in RedisTemplate + * @author Mark Pollack + * + * @param TODO + */ +public interface RedisCallback { + + /** + * Execute any number of operations against the supplied RedisClient + * {@link RedicClient}, possibly returning a result. + */ + T doInRedis(RedisClient redisClient) throws Exception; +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisClient.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisClient.java new file mode 100644 index 000000000..42373d77d --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisClient.java @@ -0,0 +1,220 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * An interface that is a one to one mapping to Redis commands to method names + * that is portable across various Redis driver libraries. + * + * @author Mark Pollack + * + */ +public interface RedisClient { + + // Connection Management + + void disconnect() throws IOException; + + + // Database control commands + + String save(); + + String bgsave(); + + String bgrewriteaof(); + + Integer lastsave(); + + String shutdown(); + + Map info(); + + //bulk reply callback - monitor + + String slaveof(String host, int port); + + String slaveofNoOne(); + + String select(int index); + + String flushDb(); + + String flushAll(); + + Integer move(String key, int dbIndex); + + String auth(String password); + + Integer dbSize(); + + + // Note: JRedis and the SMA client do not return the response code for set, would probably have to catch exception. + + // Commands operating on string values "StringOperations" or "Operations" + + + /** + * Set the string value as value of the key. The string can't be longer than 1073741824 bytes (1 GB). + *

Time complexity: O(1)

+ *

Corresponds to Redis command "SET key value"

+ * @see setCommand + * @param key key whose associated value is to be returned + * @param value value to be associated with the specified key + */ + void set(String key, String value); + + void set(String key, byte[] value); + + String get(String key); + + byte[] getAsBytes(String key); + + String getSet(String key, String value); + + List mget(String... keys); + + //TODO mgetAsBytes? Best to have byte[] overloads somewhere else? + + /** + * SETNX works exactly like SET with the only difference that if the key already exists no operation is performed. + * SETNX actually means "SET if Not eXists". + *

Time complexity: O(1)

+ *

Corresponds to command "SETNX key value"

+ * @see SetnxCommand + * @param key key whose associated value is to be set + * @param value value to be associated with the specified key + * @return 1 if the key was set, 0 if the key was not set + */ + Integer setnx(String key, String value); + + /** + * The command is exactly equivalent to the following group of commands: + *

SET key value + * EXPIRE key time + *

+ *

Time complexity: O(1)

+ * @see SetexCommand + * @param key key whose associated value is to be set + * @param seconds timeout on the specified key. After the timeout the key will automatically be deleted by the server + * @param value timeout in seconds + * @return Status reply code, OK is success + */ + String setex(String key, int seconds, String value); + + /** + * Set the the respective keys to the respective values. + *

Time complexity: O(1) to set every key

+ *

Corresponds to the command "MSET key1 value1 key2 value2 ... keyN valueN"

+ * @see MsetCommand + * @param keysvalues key value sequence + * @return OK as MSET can't fail. + */ + //TODO Consider Map here or in template? Map ? + String mset(String... keysvalues); + + Integer msetnx(String... keysvalues); + + Integer incrBy(String key, int increment); + + Integer incr(String key); + + Integer decr(String key); + + Integer decrBy(String key, int decrement); + + //TODO incrementByOne,decrementByOne in template + + /** + * If the key already exists and is a string, this command appends the provided value at the + * end of the string. If the key does not exist it is created and set as an empty string, + * so APPEND will be very similar to SET in this special case. + * @see AppendCommand + * @param key key whose associated value is to be appended + * @param value value to be appended to end of current value associated with the specified key + * @return the total length of the string after the append operation. + */ + Integer append(String key, String value); + + String substr(String key, int start, int end); + + + // Commands operating on all value types "KeySpaceOperations" + + Integer exists(String key); + + Integer del(String... keys); + + String type(String key); + + List keys(String pattern); + + String randomKey(); + + String rename(String oldkey, String newkey); + + Integer renamenx(String oldkey, String newkey); + + Integer expire(String key, int seconds); + + Integer expireAt(String key, long unixTime); + + Integer ttl(String key); + + Integer persist(String key); + + + + + // Probably not possible to abstract at this level across different providers.... + // T sendCommand(String commandName, ReplyTypeMapper mapper, String... commandArgs); + + // Commands operating on Sets + + Integer sadd(String key, String member); + + Set smembers(String key); + + Integer srem(String key, String member); + + String spop(String key); + + Integer smove(String srckey, String dstkey, String member); + + Integer scard(String key); + + Integer sismember(String key, String member); + + Set sinter(String... keys); + + Integer sinterstore(String dstkey, String... keys); + + Set sunion(String... keys); + + Integer sunionstore(String dstkey, String... keys); + + Set sdiff(String... keys); + + Integer sdiffstore(String dstkey, String... keys); + + String srandmember(String key); + +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisClientFactory.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisClientFactory.java new file mode 100644 index 000000000..3ac6358a0 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisClientFactory.java @@ -0,0 +1,34 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core; + + +import org.springframework.datastore.redis.support.RedisPersistenceExceptionTranslator; + +/** + * An interface based ConnectionFactory for creating {@link RedisClient}s. + * + * @author Mark Pollack + * + */ +public interface RedisClientFactory { + + RedisClient createClient(); + + void setPassword(String password); + + RedisPersistenceExceptionTranslator getExceptionTranslator(); +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisOperations.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisOperations.java new file mode 100644 index 000000000..b677521dd --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisOperations.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core; + +import java.util.List; + +import org.springframework.dao.DataAccessException; + +/** + * Interface specifying a set of Redis operations. + * Implemented by {@link RedisTemplate}. + * + * @author Mark Pollack + * + */ +public interface RedisOperations extends KeyValueOperations { + + T execute(RedisCallback action) throws DataAccessException; + + ServerOperations getServerOperations(); + + ListOperations getListOperations(); + + SetOperations getSetOperations(); + + + +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisTemplate.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisTemplate.java new file mode 100644 index 000000000..5594cb431 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisTemplate.java @@ -0,0 +1,293 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core; + +import java.util.List; +import java.util.Map; + +import org.springframework.dao.DataAccessException; +import org.springframework.dao.DataRetrievalFailureException; +import org.springframework.datastore.redis.support.RedisUtils; +import org.springframework.datastore.redis.support.converter.DefaultRedisConverter; +import org.springframework.datastore.redis.support.converter.RedisConverter; +import org.springframework.util.Assert; + +/** + * This is the central class in the Redis core package. + * It simplifies the use of Redis and helps to avoid common errors. + * + * @author Mark Pollack + * + */ +public class RedisTemplate extends RedisAccessor implements RedisOperations { + + // TODO perform validation to see if value size > 1GB + // TODO perform validation to see if key contains space, newline or whitespace. + // TODO warning on key size being large > 1024 bytes? + + private RedisConverter redisConverter = new DefaultRedisConverter(); + + private ServerOperations serverOperations; + + public RedisTemplate() { + initDefaults(); + } + public RedisTemplate(RedisClientFactory redisClientFactory) { + this(); + this.setRedisClientFactory(redisClientFactory); + afterPropertiesSet(); + } + + public void setRedisConverter(RedisConverter redisConverter) { + this.redisConverter = redisConverter; + } + + protected void initDefaults() { + serverOperations = new DefaultServerOperations(this); + } + + + + public T execute(RedisCallback action) { + Assert.notNull(action, "Callback object must not be null"); + + RedisClient clientToClose = null; + try { + RedisClient clientToUse = null; //ConnectionFactoryUtils.doGetTransacxtionChannel(getConnectionFactory, this.transactionResourceFactory); + if (clientToUse == null) { + clientToClose = createClient(); + clientToUse = clientToClose; + } + if (logger.isDebugEnabled()) { + logger.debug("Executing callback on Redis Client: " + clientToUse); + } + return action.doInRedis(clientToUse); + } + catch (Exception e) { + throw convertRedisAccessException(e); + } finally { + RedisUtils.closeClient(clientToClose); + } + } + + protected DataAccessException convertRedisAccessException(Exception ex) { + //TODO + return null; + } + + public ServerOperations getServerOperations() { + return serverOperations; + } + + public ListOperations getListOperations() { + // TODO Auto-generated method stub + throw new RuntimeException("unimplemented"); + } + + // Key Value Operations + + public String get(final String key) { + return execute(new RedisCallback() { + public String doInRedis(RedisClient redisClient) throws Exception { + return redisClient.get(key); + } + }); + } + + public byte[] getAsBytes(final String key) { + return execute(new RedisCallback() { + public byte[] doInRedis(RedisClient redisClient) throws Exception { + return redisClient.getAsBytes(key); + } + }); + } + + public T getAndConvert(String key, Class requiredType) { + //TODO deserializer exceptions need to be under DAO exception hierarchy. + Object object = redisConverter.deserialize(getAsBytes(key)); + if (requiredType != null && object != null && !requiredType.isAssignableFrom(object.getClass())) { + throw new DataRetrievalFailureException("Can not assign from " + requiredType + " to " + object.getClass()); + } + return (T) object; + } + + public String getAndSet(String key, String value) { + // TODO Auto-generated method stub + throw new RuntimeException("unimplemented"); + } + + public byte[] getAndSetBytes(String key, byte[] value) { + // TODO Auto-generated method stub + throw new RuntimeException("unimplemented"); + } + + public T getAndSetObject(String key, T value, Class requiredType) { + // TODO Auto-generated method stub + throw new RuntimeException("unimplemented"); + } + + public void set(final String key, final String value) { + execute(new RedisCallback() { + public Void doInRedis(RedisClient redisClient) throws Exception { + redisClient.set(key, value); + return null; + } + }); + } + + public void set(String key, String value, long expiryInMillis) { + // TODO Auto-generated method stub + + } + + public void setAsBytes(final String key, final byte[] value) { + execute(new RedisCallback() { + public Void doInRedis(RedisClient redisClient) throws Exception { + redisClient.set(key, value); + return null; + } + }); + + } + + public void setAsBytes(String key, byte[] value, long expiryInMillis) { + // TODO Auto-generated method stub + throw new RuntimeException("unimplemented"); + } + + public void setIfKeyNonExistent(String key, String value) { + // TODO Auto-generated method stub + throw new RuntimeException("unimplemented"); + } + + public void setIfKeyNonExistent(String key, byte[] value) { + // TODO Auto-generated method stub + throw new RuntimeException("unimplemented"); + } + + public void setMultiple(Map keysAndValues) { + // TODO Auto-generated method stub + throw new RuntimeException("unimplemented"); + } + + public void setMultipleAsBytes(Map keysAndValues) { + // TODO Auto-generated method stub + throw new RuntimeException("unimplemented"); + } + + public void setMultipleAsBytesIfKeysNonExistent( + Map keysAndValues) { + // TODO Auto-generated method stub + throw new RuntimeException("unimplemented"); + } + + public void setMultipleIfKeysNonExistent(Map keysAndValues) { + // TODO Auto-generated method stub + throw new RuntimeException("unimplemented"); + } + + public int append(String key, String value) { + throw new RuntimeException("unimplemented"); + } + + public void convertAndSet(String key, Object value) { + setAsBytes(key, this.redisConverter.serialize(value)); + } + + public void convertAndSet(String key, Object value, long expiryInMillis) { + // TODO Auto-generated method stub + throw new RuntimeException("unimplemented"); + } + + public void convertAndSetIfKeyNonExistent(String key, Object value) { + // TODO Auto-generated method stub + throw new RuntimeException("unimplemented"); + } + + public void convertAndSetMultiple(Map keysAndValues) { + // TODO Auto-generated method stub + throw new RuntimeException("unimplemented"); + } + + public void convertAndSetMultipleIfKeysNonExistent( + Map keysAndValues) { + // TODO Auto-generated method stub + throw new RuntimeException("unimplemented"); + } + + public int decrement(String key) { + // TODO Auto-generated method stub + throw new RuntimeException("unimplemented"); + } + + public int decrementBy(String key, int value) { + // TODO Auto-generated method stub + throw new RuntimeException("unimplemented"); + } + + public List getValues(List keys) { + // TODO Auto-generated method stub + throw new RuntimeException("unimplemented"); + } + + public int increment(String key) { + // TODO Auto-generated method stub + throw new RuntimeException("unimplemented"); + } + + public int incrementBy(String key, int value) { + // TODO Auto-generated method stub + throw new RuntimeException("unimplemented"); + } + + public String subString(String key, int fromIndex, int toIndex) { + // TODO Auto-generated method stub + throw new RuntimeException("unimplemented"); + } + public List getAndConvertValues(List keys, + Class requiredType) { + // TODO Auto-generated method stub + return null; + } + public String getSubString(String key, int fromIndex, int toIndex) { + // TODO Auto-generated method stub + return null; + } + public boolean containsKey(String key) { + // TODO Auto-generated method stub + return false; + } + public boolean deleteKeys(final String... keys) { + return execute(new RedisCallback() { + public Boolean doInRedis(RedisClient redisClient) throws Exception { + Integer intVal = redisClient.del(keys); + return (intVal == 0) ? false : true; + } + }); + } + + public SetOperations getSetOperations() { + // TODO Auto-generated method stub + return null; + } + + + + + + +} \ No newline at end of file diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/ServerOperations.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/ServerOperations.java new file mode 100644 index 000000000..10e270757 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/ServerOperations.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core; + +import java.util.Map; + +/** + * Server operations for Redis + * + * @author Mark Pollack + * + */ +public interface ServerOperations { + + + // Connection handling + + + + + /** + * Calls the Redis 'info' command that returns different information and statistics about the server. + * The reply is parsed into a Map for easy programmatic access. + * Corresponds to the Redis InfoCommand INFO + * @see InfoCommand + * @return + */ + Map getServerInfo(); + + // TODO Commands Monitor, SlaveOf, Config +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/SetOperations.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/SetOperations.java new file mode 100644 index 000000000..d3026e649 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/SetOperations.java @@ -0,0 +1,35 @@ +package org.springframework.datastore.redis.core; + +import java.util.Set; + +public interface SetOperations { + + boolean add(String key, String member); + + Set getAll(String key); + + boolean remove(String key, String member); + + boolean removeRandom(String key); + + boolean moveBetweenSets(String srckey, String dstkey, String member); + + int size(String key); + + boolean contains(String key, String member); + + Set getIntersectionOfSets(String... keys); + + void storeIntersectionOfSets(String dstkey, String... keys); + + Set getUnionOfSets(String... keys); + + void storeUnionOfSets(String dstkey, String... keys); + + Set getDifferenceBetweenSets(String... keys); + + void storeDifferenceBetweenSets(String dstkey, String... keys); + + String getRandom(String key); + +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jedis/CachingJedisClientFactory.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jedis/CachingJedisClientFactory.java new file mode 100644 index 000000000..1a6d87456 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jedis/CachingJedisClientFactory.java @@ -0,0 +1,111 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core.jedis; + +import java.util.concurrent.TimeoutException; + +import org.springframework.datastore.redis.CannotGetRedisConnectionException; +import org.springframework.datastore.redis.core.AbstractRedisClientFactory; +import org.springframework.datastore.redis.core.RedisClient; +import org.springframework.datastore.redis.support.RedisPersistenceExceptionTranslator; + +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; + +/** + * A RedisClientFactory implementation that uses Jedis's native connection/client + * caching features. + * + * @author Mark Pollack + * + */ +public class CachingJedisClientFactory extends AbstractRedisClientFactory { + + private JedisPool pool; + + private int timeout; + + private int clientCacheSize; + + private long maxWaitTime; + + /** + * + * @param clientCacheSize + */ + public CachingJedisClientFactory(int clientCacheSize) { + this.clientCacheSize = clientCacheSize; + } + + public CachingJedisClientFactory(JedisPool pool) { + this.pool = pool; + } + + public int getClientCacheSize() { + return this.clientCacheSize; + } + + public JedisPool getJedisPool() { + return this.pool; + } + + public int getTimeout() { + return timeout; + } + + protected void setTimeout(int timeout) { + this.timeout = timeout; + } + + public long getMaxWaitTime() { + return this.maxWaitTime; + } + + /** + * Sets the maximum amount of time (in milliseconds) the getResource() method + * should block before throwing an TimeoutException. + * @param maxWaitTime The maximum time you would like to wait for the resource. + */ + public void setMaxWaitTime(long maxWaitTime) { + this.maxWaitTime = maxWaitTime; + } + + @Override + public RedisClient doGetClient() { + Jedis jedis; + if (getClientCacheSize() != 0) { + pool = new JedisPool(getHostName(), getPort(), getTimeout()); + pool.setResourcesNumber(getClientCacheSize()); + } + try { + if (getMaxWaitTime() != 0) + jedis = pool.getResource(getMaxWaitTime()); + else { + jedis = pool.getResource(); + } + } catch (TimeoutException e) { + throw new CannotGetRedisConnectionException( + "Timed out. Could not get Redis Connection", e); + } + return new JedisClient(jedis, getExceptionTranslator() ); + } + + @Override + public RedisPersistenceExceptionTranslator getExceptionTranslator() { + return new JedisPersistenceExceptionTranslator(); + } + +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jedis/JedisClient.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jedis/JedisClient.java new file mode 100644 index 000000000..ec57649da --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jedis/JedisClient.java @@ -0,0 +1,535 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core.jedis; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.springframework.dao.DataAccessException; +import org.springframework.datastore.redis.core.AbstractRedisClient; +import org.springframework.datastore.redis.support.RedisPersistenceExceptionTranslator; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +import redis.clients.jedis.Jedis; + +/** + * Jedis based implementation of Spring's RedisClient interface. Presents a low + * level API where method names map onto Redis commands. + * + * @author Mark Pollack + * + */ +public class JedisClient extends AbstractRedisClient { + + private Jedis _jedis; + private RedisPersistenceExceptionTranslator exceptionTranslator; + + public JedisClient(Jedis jedis, + RedisPersistenceExceptionTranslator exceptionTranslator) { + this._jedis = jedis; + this.exceptionTranslator = exceptionTranslator; + } + + public T execute(JedisClientCallback action) { + Assert.notNull(action, "Callback object must not be null"); + + // TODO jredisClient resource mgmt. + try { + if (logger.isDebugEnabled()) { + logger.debug("Executing callback on Jedis : " + _jedis); + } + return action.doInJedis(_jedis); + } catch (Exception e) { + throw convertJedisAccessException(e); + } + + } + + protected DataAccessException convertJedisAccessException(Exception ex) { + return exceptionTranslator.translateException(ex); + } + + public void disconnect() throws IOException { + execute(new JedisClientCallback() { + public Object doInJedis(Jedis jedis) throws Exception { + jedis.disconnect(); + return null; + } + }); + } + + // Database control commands + + public String save() { + return execute(new JedisClientCallback() { + public String doInJedis(Jedis jedis) throws Exception { + return jedis.save(); + } + }); + } + + public String bgsave() { + return execute(new JedisClientCallback() { + public String doInJedis(Jedis jedis) throws Exception { + return jedis.bgsave(); + } + }); + } + + public String bgrewriteaof() { + return execute(new JedisClientCallback() { + public String doInJedis(Jedis jedis) throws Exception { + return jedis.bgrewriteaof(); + } + }); + } + + public Integer lastsave() { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.lastsave(); + } + }); + } + + public String shutdown() { + return execute(new JedisClientCallback() { + public String doInJedis(Jedis jedis) throws Exception { + return jedis.shutdown(); + } + }); + } + + public Map info() { + return execute(new JedisClientCallback>() { + public Map doInJedis(Jedis jedis) throws Exception { + String[] response = StringUtils.delimitedListToStringArray( + jedis.info(), "\r\n"); + Map responseMap = new HashMap(); + for (String responseLine : response) { + if (!responseLine.isEmpty()) { + String[] keyValue = StringUtils + .split(responseLine, ":"); + if (keyValue == null) { + logger.warn("Could not parse info reponse line [" + + responseLine + "]"); + continue; + } + responseMap.put(keyValue[0], keyValue[1]); + } + } + return responseMap; + } + }); + } + + public String slaveof(final String host, final int port) { + return execute(new JedisClientCallback() { + public String doInJedis(Jedis jedis) throws Exception { + return jedis.slaveof(host, port); + } + }); + } + + public String slaveofNoOne() { + return execute(new JedisClientCallback() { + public String doInJedis(Jedis jedis) throws Exception { + return jedis.slaveofNoOne(); + } + }); + } + + public String select(final int index) { + return execute(new JedisClientCallback() { + public String doInJedis(Jedis jedis) throws Exception { + return jedis.select(index); + } + }); + } + + public String flushDb() { + return execute(new JedisClientCallback() { + public String doInJedis(Jedis jedis) throws Exception { + return jedis.flushDB(); + } + }); + } + + public String flushAll() { + return execute(new JedisClientCallback() { + public String doInJedis(Jedis jedis) throws Exception { + return jedis.flushAll(); + } + }); + } + + public Integer move(final String key, final int dbIndex) { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.move(key, dbIndex); + } + }); + } + + public String auth(final String password) { + return execute(new JedisClientCallback() { + public String doInJedis(Jedis jedis) throws Exception { + return jedis.auth(password); + } + }); + } + + public Integer dbSize() { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.dbSize(); + } + }); + } + + // Commands operating on string value types "StringOperations" or + // "Operations" + + public void set(final String key, final String value) { + execute(new JedisClientCallback() { + public String doInJedis(Jedis jedis) throws Exception { + return jedis.set(key, value); + } + }); + } + + public void set(String key, byte[] value) { + set(key, byteToString(value)); + } + + public String get(final String key) { + return execute(new JedisClientCallback() { + public String doInJedis(Jedis jedis) throws Exception { + return jedis.get(key); + } + }); + } + + public byte[] getAsBytes(String key) { + return stringToByte(get(key)); + } + + public String getSet(final String key, final String value) { + return execute(new JedisClientCallback() { + public String doInJedis(Jedis jedis) throws Exception { + return jedis.getSet(key, value); + } + }); + } + + public List mget(final String... keys) { + return execute(new JedisClientCallback>() { + public List doInJedis(Jedis jedis) throws Exception { + return jedis.mget(keys); + } + }); + } + + public Integer setnx(final String key, final String value) { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.setnx(key, value); + } + }); + } + + public String setex(final String key, final int seconds, final String value) { + return execute(new JedisClientCallback() { + public String doInJedis(Jedis jedis) throws Exception { + return jedis.setex(key, seconds, value); + } + }); + } + + public String mset(final String... keysvalues) { + return execute(new JedisClientCallback() { + public String doInJedis(Jedis jedis) throws Exception { + return jedis.mset(keysvalues); + } + }); + } + + public Integer msetnx(final String... keysvalues) { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.msetnx(keysvalues); + } + }); + } + + public Integer incrBy(final String key, final int increment) { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.incrBy(key, increment); + } + }); + } + + public Integer incr(final String key) { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.incr(key); + } + }); + } + + public Integer decr(final String key) { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.decr(key); + } + }); + } + + public Integer decrBy(final String key, final int increment) { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.decrBy(key, increment); + } + }); + } + + public Integer append(final String key, final String value) { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.append(key, value); + } + }); + } + + public String substr(final String key, final int start, final int end) { + return execute(new JedisClientCallback() { + public String doInJedis(Jedis jedis) throws Exception { + return jedis.substr(key, start, end); + } + }); + } + + // Commands operating on all value types "KeySpaceOperations" + + public Integer exists(final String key) { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.exists(key); + } + }); + } + + public Integer del(final String... keys) { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.del(keys); + } + }); + } + + public String type(final String key) { + return execute(new JedisClientCallback() { + public String doInJedis(Jedis jedis) throws Exception { + return jedis.type(key); + } + }); + } + + public List keys(final String pattern) { + return execute(new JedisClientCallback>() { + public List doInJedis(Jedis jedis) throws Exception { + return jedis.keys(pattern); + } + }); + } + + public String randomKey() { + return execute(new JedisClientCallback() { + public String doInJedis(Jedis jedis) throws Exception { + return jedis.randomKey(); + } + }); + } + + public String rename(final String oldkey, final String newkey) { + return execute(new JedisClientCallback() { + public String doInJedis(Jedis jedis) throws Exception { + return jedis.rename(oldkey, newkey); + } + }); + } + + public Integer renamenx(final String oldkey, final String newkey) { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.renamenx(oldkey, newkey); + } + }); + } + + public Integer expire(final String key, final int seconds) { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.expire(key, seconds); + } + }); + } + + public Integer expireAt(final String key, final long unixTime) { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.expireAt(key, unixTime); + } + }); + } + + public Integer ttl(final String key) { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.ttl(key); + } + }); + } + + public Integer persist(final String key) { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.persist(key); + } + }); + } + + // Commands operating on Sets + + public Integer sadd(final String key, final String member) { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.sadd(key, member); + } + }); + } + + public Set smembers(final String key) { + return execute(new JedisClientCallback>() { + public Set doInJedis(Jedis jedis) throws Exception { + return jedis.smembers(key); + } + }); + } + + public Integer srem(final String key, final String member) { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.srem(key, member); + } + }); + } + + public String spop(final String key) { + return execute(new JedisClientCallback() { + public String doInJedis(Jedis jedis) throws Exception { + return jedis.spop(key); + } + }); + } + + public Integer smove(final String srckey, final String dstkey, + final String member) { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.smove(srckey, dstkey, member); + } + }); + } + + public Integer scard(final String key) { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.scard(key); + } + }); + } + + public Integer sismember(final String key, final String member) { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.sismember(key, member); + } + }); + } + + public Set sinter(final String... keys) { + return execute(new JedisClientCallback>() { + public Set doInJedis(Jedis jedis) throws Exception { + return jedis.sinter(keys); + } + }); + } + + public Integer sinterstore(final String dstkey, final String... keys) { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.sinterstore(dstkey, keys); + } + }); + } + + public Set sunion(final String... keys) { + return execute(new JedisClientCallback>() { + public Set doInJedis(Jedis jedis) throws Exception { + return jedis.sunion(keys); + } + }); + } + + public Integer sunionstore(final String dstkey, final String... keys) { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.sunionstore(dstkey, keys); + } + }); + } + + public Set sdiff(final String... keys) { + return execute(new JedisClientCallback>() { + public Set doInJedis(Jedis jedis) throws Exception { + return jedis.sdiff(keys); + } + }); + } + + public Integer sdiffstore(final String dstkey, final String... keys) { + return execute(new JedisClientCallback() { + public Integer doInJedis(Jedis jedis) throws Exception { + return jedis.sdiffstore(dstkey, keys); + } + }); + } + + public String srandmember(final String key) { + return execute(new JedisClientCallback() { + public String doInJedis(Jedis jedis) throws Exception { + return jedis.srandmember(key); + } + }); + } + +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jedis/JedisClientCallback.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jedis/JedisClientCallback.java new file mode 100644 index 000000000..d4fd005c6 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jedis/JedisClientCallback.java @@ -0,0 +1,33 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core.jedis; + +import redis.clients.jedis.Jedis; + +/** + * Basic callback for use in JedisClient + * @author Mark Pollack + * + * @param TODO + */ +public interface JedisClientCallback { + + /** + * Execute any number of operations against the supplied Jedis + * {@link Jedis}, possibly returning a result. + */ + T doInJedis(Jedis jedis) throws Exception; +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jedis/JedisClientFactory.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jedis/JedisClientFactory.java new file mode 100644 index 000000000..fba7c8304 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jedis/JedisClientFactory.java @@ -0,0 +1,122 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core.jedis; + +import java.io.IOException; +import java.net.UnknownHostException; + +import org.springframework.datastore.redis.CannotGetRedisConnectionException; +import org.springframework.datastore.redis.core.AbstractRedisClientFactory; +import org.springframework.datastore.redis.core.RedisClient; +import org.springframework.datastore.redis.core.RedisClientFactory; +import org.springframework.datastore.redis.core.jredis.JRedisPersistenceExceptionTranslator; +import org.springframework.datastore.redis.support.RedisPersistenceExceptionTranslator; + +import redis.clients.jedis.Jedis; +import redis.clients.util.ShardInfo; + +/** + * A {@link RedisClientFactory} implementation that returns a new instance of a + * Jedis backed RedisClient from call {@link #createClient()} calls. + * + * @author Mark Pollack + * + */ +public class JedisClientFactory extends AbstractRedisClientFactory { + + private ShardInfo shardInfo; + + private int timeout; + + private RedisPersistenceExceptionTranslator exceptionTranslator = new JRedisPersistenceExceptionTranslator(); + + public JedisClientFactory() { + setHostName(getDefaultHostName()); + } + + public JedisClientFactory(String hostname) { + setHostName(hostname); + } + + public JedisClientFactory(String hostname, int port) + { + setHostName(hostname); + setPort(port); + } + + public JedisClientFactory(String hostname, int port, int timeout) + { + setHostName(hostname); + setPort(port); + setTimeout(timeout); + } + + public JedisClientFactory(ShardInfo shardInfo) { + this.shardInfo = shardInfo; + } + + protected ShardInfo getShardInfo() { + return this.shardInfo; + } + + public int getTimeout() { + return timeout; + } + + protected void setTimeout(int timeout) { + this.timeout = timeout; + } + + @Override + public RedisClient doGetClient() { + Jedis jedis; + if (getShardInfo() != null) { + jedis = new Jedis(getShardInfo()); + } + if (getPort() != 0 && getTimeout() != 0) { + jedis = new Jedis(getHostName(), getPort(), getTimeout()); + } else if (getPort() != 0) { + jedis = new Jedis(getHostName(), getPort()); + } else { + jedis = new Jedis(getHostName()); + } + try { + jedis.connect(); + if (getPassword() != null) { + jedis.auth(getPassword()); + } + } catch (UnknownHostException e) { + throw new CannotGetRedisConnectionException( + "Could not get Redis Connection", e); + } catch (IOException e) { + throw new CannotGetRedisConnectionException( + "Could not get Redis Connection", e); + } + return new JedisClient(jedis, getExceptionTranslator()); + } + + @Override + public RedisPersistenceExceptionTranslator getExceptionTranslator() { + return exceptionTranslator; + } + + public void setExceptionTranslator( + RedisPersistenceExceptionTranslator exceptionTranslator) { + this.exceptionTranslator = exceptionTranslator; + } + +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jedis/JedisPersistenceExceptionTranslator.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jedis/JedisPersistenceExceptionTranslator.java new file mode 100644 index 000000000..edc4312f8 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jedis/JedisPersistenceExceptionTranslator.java @@ -0,0 +1,35 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core.jedis; + +import org.springframework.dao.DataAccessException; +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.datastore.redis.support.RedisPersistenceExceptionTranslator; + +/** + * Translates error messages from Jedis to Spring's Data Access exception class hierarchy + * + * @author Mark Pollack + * + */ +public class JedisPersistenceExceptionTranslator implements + RedisPersistenceExceptionTranslator { + + public DataAccessException translateException(Exception ex) { + return new InvalidDataAccessApiUsageException(ex.getMessage(), ex); + } + +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jredis/JRedisClientCallback.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jredis/JRedisClientCallback.java new file mode 100644 index 000000000..11dbeca50 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jredis/JRedisClientCallback.java @@ -0,0 +1,33 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core.jredis; + +import org.jredis.ri.alphazero.JRedisClient; + +/** + * Basic callback for use in JRedisClient + * @author Mark Pollack + * + * @param TODO + */ +public interface JRedisClientCallback { + + /** + * Execute any number of operations against the supplied RedisClient + * {@link RedicClient}, possibly returning a result. + */ + T doInJRedis(JRedisClient jredisClient) throws Exception; +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jredis/JRedisClientFactory.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jredis/JRedisClientFactory.java new file mode 100644 index 000000000..f951e13a9 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jredis/JRedisClientFactory.java @@ -0,0 +1,111 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core.jredis; + +import java.io.UnsupportedEncodingException; +import java.net.InetAddress; +import java.net.UnknownHostException; + +import org.jredis.ClientRuntimeException; +import org.jredis.connector.ConnectionSpec; +import org.jredis.ri.alphazero.JRedisClient; +import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.datastore.redis.core.AbstractRedisClientFactory; +import org.springframework.datastore.redis.core.RedisClient; +import org.springframework.datastore.redis.support.RedisPersistenceExceptionTranslator; + +public class JRedisClientFactory extends AbstractRedisClientFactory { + + public static final String DEFAULT_CHARSET = "UTF-8"; + + private volatile String defaultCharset = DEFAULT_CHARSET; + + + private RedisPersistenceExceptionTranslator exceptionTranslator; + + private ConnectionSpec connectionSpec; + + public JRedisClientFactory() { + setHostName(getDefaultHostName()); + exceptionTranslator = new JRedisPersistenceExceptionTranslator(); + } + + public JRedisClientFactory(ConnectionSpec connectionSpec) { + this.connectionSpec = connectionSpec; + } + + @Override + public RedisClient doGetClient() { + JRedisClient jredis; + if (connectionSpec == null) { + + connectionSpec = DefaultConnectionSpec.newSpec(); + + InetAddress address; + try { + address = InetAddress.getByName(getHostName()); + } catch (UnknownHostException e) { + throw new ClientRuntimeException("unknown host: " + + getHostName(), e); + } + connectionSpec.setAddress(address); + + if (getPort() != 0) { + connectionSpec.setPort(getPort()); + } + + if (getPassword() != null) { + connectionSpec.setCredentials(stringToByte(getPassword())); + } + } + + jredis = new JRedisClient(connectionSpec); + return new JRedisSpringClient(jredis, getExceptionTranslator()); + } + + protected byte[] stringToByte(String string) throws InvalidDataAccessApiUsageException { + try { + return string.getBytes(this.defaultCharset); + } catch (UnsupportedEncodingException e) { + throw new InvalidDataAccessApiUsageException(defaultCharset + + " encoding not supported.", e); + } + } + + /** + * Specify the default charset to use when converting to or from text-based + * Message body content. If not specified, the charset will be "UTF-8". + */ + public void setDefaultCharset(String defaultCharset) { + this.defaultCharset = (defaultCharset != null) ? defaultCharset : DEFAULT_CHARSET; + } + + public String getDefaultCharset() { + return defaultCharset; + } + + @Override + public RedisPersistenceExceptionTranslator getExceptionTranslator() { + return exceptionTranslator; + } + + public void setExceptionTranslator( + RedisPersistenceExceptionTranslator exceptionTranslator) { + this.exceptionTranslator = exceptionTranslator; + } + +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jredis/JRedisPersistenceExceptionTranslator.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jredis/JRedisPersistenceExceptionTranslator.java new file mode 100644 index 000000000..19cb45d9f --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jredis/JRedisPersistenceExceptionTranslator.java @@ -0,0 +1,29 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core.jredis; + +import org.springframework.dao.DataAccessException; +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.datastore.redis.support.RedisPersistenceExceptionTranslator; + +public class JRedisPersistenceExceptionTranslator implements + RedisPersistenceExceptionTranslator { + + public DataAccessException translateException(Exception ex) { + return new InvalidDataAccessApiUsageException(ex.getMessage(), ex); + } + +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jredis/JRedisSpringClient.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jredis/JRedisSpringClient.java new file mode 100644 index 000000000..712aa1381 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jredis/JRedisSpringClient.java @@ -0,0 +1,494 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core.jredis; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jredis.ri.alphazero.JRedisClient; +import org.jredis.ri.alphazero.support.DefaultCodec; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.DataRetrievalFailureException; +import org.springframework.datastore.redis.core.AbstractRedisClient; +import org.springframework.datastore.redis.support.RedisPersistenceExceptionTranslator; +import org.springframework.util.Assert; + +/** + * JRedis implementation of RedisClient. Name has 'Spring' in it to avoid naming + * conflict with classes in JRedis itself. + * + * @author Mark Pollack + * + */ +public class JRedisSpringClient extends AbstractRedisClient { + + /** Logger available to subclasses */ + protected final Log logger = LogFactory.getLog(getClass()); + + private JRedisClient _jredisClient; + private RedisPersistenceExceptionTranslator exceptionTranslator; + + public JRedisSpringClient(JRedisClient jredisClient, + RedisPersistenceExceptionTranslator exceptionTransator) { + this._jredisClient = jredisClient; + this.exceptionTranslator = exceptionTransator; + this.setDefaultCharset(DefaultCodec.SUPPORTED_CHARSET_NAME); + } + + + protected Integer convertToInteger(long longTime) { + if (longTime < Integer.MIN_VALUE + || longTime > Integer.MAX_VALUE) { + throw new DataRetrievalFailureException( + longTime + + " cannot be cast to int without changing its value."); + } + return (int) longTime; + } + + public T execute(JRedisClientCallback action) { + Assert.notNull(action, "Callback object must not be null"); + + // TODO jredisClient resource mgmt. + try { + if (logger.isDebugEnabled()) { + logger.debug("Executing callback on JRedisClient : " + + _jredisClient); + } + return action.doInJRedis(_jredisClient); + } catch (Exception e) { + throw convertJRedisAccessException(e); + } + + } + + protected DataAccessException convertJRedisAccessException(Exception ex) { + return exceptionTranslator.translateException(ex); + } + + public void disconnect() { + // TODO look at disconnect exception translation + execute(new JRedisClientCallback() { + public Object doInJRedis(JRedisClient jredisClient) + throws Exception { + jredisClient.quit(); + return null; + } + }); + } + + public String get(final String key) { + return execute(new JRedisClientCallback() { + public String doInJRedis(JRedisClient jredisClient) + throws Exception { + return byteToString(jredisClient.get(key)); + } + }); + } + + public byte[] getAsBytes(final String key) { + return execute(new JRedisClientCallback() { + public byte[] doInJRedis(JRedisClient jredisClient) + throws Exception { + return jredisClient.get(key); + } + }); + } + + public void set(final String key, final String value) { + execute(new JRedisClientCallback() { + public String doInJRedis(JRedisClient jredisClient) + throws Exception { + jredisClient.set(key, value); + return null; + } + }); + } + + public void set(final String key, final byte[] value) { + execute(new JRedisClientCallback() { + public String doInJRedis(JRedisClient jredisClient) + throws Exception { + jredisClient.set(key, value); + return null; + } + }); + } + + public String save() { + return execute(new JRedisClientCallback() { + public String doInJRedis(JRedisClient jredisClient) + throws Exception { + jredisClient.save(); + return "OK"; + } + }); + } + + public String bgsave() { + return execute(new JRedisClientCallback() { + public String doInJRedis(JRedisClient jredisClient) + throws Exception { + jredisClient.bgsave(); + return "Background saving started"; + } + }); + } + + public String bgrewriteaof() { + return execute(new JRedisClientCallback() { + public String doInJRedis(JRedisClient jredisClient) + throws Exception { + jredisClient.bgrewriteaof(); + return "Background append only file rewriting started"; + } + }); + } + + public Integer lastsave() { + return execute(new JRedisClientCallback() { + public Integer doInJRedis(JRedisClient jredisClient) + throws Exception { + long longTime = jredisClient.lastsave(); + // odd that JRedis return long when the Redis command spec says + // int. + return convertToInteger(longTime); + } + }); + } + + public String shutdown() { + return execute(new JRedisClientCallback() { + public String doInJRedis(JRedisClient jredisClient) + throws Exception { + throw new UnsupportedOperationException("JRedis does not implement SHUTDOWN command"); + } + }); + } + + public Map info() { + return execute(new JRedisClientCallback>() { + public Map doInJRedis(JRedisClient jredisClient) + throws Exception { + return jredisClient.info(); + } + }); + } + + public String slaveof(final String host, final int port) { + return execute(new JRedisClientCallback() { + public String doInJRedis(JRedisClient jredisClient) + throws Exception { + jredisClient.slaveof(host,port); + return "TODO - EXTRACT CORRECT STRING RESPONSE FROM REDIS FOR COMMAND SLAVEOF"; + } + }); + } + + public String slaveofNoOne() { + return execute(new JRedisClientCallback() { + public String doInJRedis(JRedisClient jredisClient) + throws Exception { + jredisClient.slaveofnone(); + return "TODO - EXTRACT CORRECT STRING RESPONSE FROM REDIS FOR COMMAND SLAVEOF NO ONE"; + } + }); + } + + public String select(int index) { + return execute(new JRedisClientCallback() { + public String doInJRedis(JRedisClient jredisClient) + throws Exception { + throw new UnsupportedOperationException("JRedis does not implement SELECT command"); + } + }); + } + + public String flushDb() { + return execute(new JRedisClientCallback() { + public String doInJRedis(JRedisClient jredisClient) + throws Exception { + jredisClient.flushdb(); + //TODO why does flushdb() return JRedis interface? + return "OK"; + } + }); + } + + public String flushAll() { + return execute(new JRedisClientCallback() { + public String doInJRedis(JRedisClient jredisClient) + throws Exception { + jredisClient.flushall(); + //TODO why does flushdb() return JRedis interface? + return "OK"; + } + }); + } + + public Integer move(final String key, final int dbIndex) { + return execute(new JRedisClientCallback() { + public Integer doInJRedis(JRedisClient jredisClient) + throws Exception { + return jredisClient.move(key, dbIndex) ? 1 : 0; + } + }); + } + + public String auth(String password) { + return execute(new JRedisClientCallback() { + public String doInJRedis(JRedisClient jredisClient) + throws Exception { + throw new UnsupportedOperationException("JRedis does not implement AUTH command"); + } + }); + } + + public Integer dbSize() { + return execute(new JRedisClientCallback() { + public Integer doInJRedis(JRedisClient jredisClient) + throws Exception { + return convertToInteger(jredisClient.dbsize()); + } + }); + } + + + public String getSet(String key, String value) { + // TODO Auto-generated method stub + return null; + } + + + public List mget(String... keys) { + // TODO Auto-generated method stub + return null; + } + + + public Integer setnx(String key, String value) { + // TODO Auto-generated method stub + return null; + } + + + public String setex(String key, int seconds, String value) { + // TODO Auto-generated method stub + return null; + } + + + public String mset(String... keysvalues) { + // TODO Auto-generated method stub + return null; + } + + + public Integer msetnx(String... keysvalues) { + // TODO Auto-generated method stub + return null; + } + + + public Integer incrBy(String key, int increment) { + // TODO Auto-generated method stub + return null; + } + + + public Integer incr(String key) { + // TODO Auto-generated method stub + return null; + } + + + public Integer decr(String key) { + // TODO Auto-generated method stub + return null; + } + + + public Integer decrBy(String key, int decrement) { + // TODO Auto-generated method stub + return null; + } + + + public Integer append(String key, String value) { + // TODO Auto-generated method stub + return null; + } + + + public String substr(String key, int start, int end) { + // TODO Auto-generated method stub + return null; + } + + + public Integer exists(String key) { + // TODO Auto-generated method stub + return null; + } + + + public Integer del(String... keys) { + // TODO Auto-generated method stub + return null; + } + + + public String type(String key) { + // TODO Auto-generated method stub + return null; + } + + + public List keys(String pattern) { + // TODO Auto-generated method stub + return null; + } + + + public String randomKey() { + // TODO Auto-generated method stub + return null; + } + + + public String rename(String oldkey, String newkey) { + // TODO Auto-generated method stub + return null; + } + + + public Integer renamenx(String oldkey, String newkey) { + // TODO Auto-generated method stub + return null; + } + + + public Integer expire(String key, int seconds) { + // TODO Auto-generated method stub + return null; + } + + + public Integer expireAt(String key, long unixTime) { + // TODO Auto-generated method stub + return null; + } + + + public Integer ttl(String key) { + // TODO Auto-generated method stub + return null; + } + + + public Integer persist(String key) { + // TODO Auto-generated method stub + return null; + } + + + public Integer sadd(String key, String member) { + // TODO Auto-generated method stub + return null; + } + + + public Set smembers(String key) { + // TODO Auto-generated method stub + return null; + } + + + public Integer srem(String key, String member) { + // TODO Auto-generated method stub + return null; + } + + + public String spop(String key) { + // TODO Auto-generated method stub + return null; + } + + + public Integer smove(String srckey, String dstkey, String member) { + // TODO Auto-generated method stub + return null; + } + + + public Integer scard(String key) { + // TODO Auto-generated method stub + return null; + } + + + public Integer sismember(String key, String member) { + // TODO Auto-generated method stub + return null; + } + + + public Set sinter(String... keys) { + // TODO Auto-generated method stub + return null; + } + + + public Integer sinterstore(String dstkey, String... keys) { + // TODO Auto-generated method stub + return null; + } + + + public Set sunion(String... keys) { + // TODO Auto-generated method stub + return null; + } + + + public Integer sunionstore(String dstkey, String... keys) { + // TODO Auto-generated method stub + return null; + } + + + public Set sdiff(String... keys) { + // TODO Auto-generated method stub + return null; + } + + + public Integer sdiffstore(String dstkey, String... keys) { + // TODO Auto-generated method stub + return null; + } + + + public String srandmember(String key) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/support/RedisPersistenceExceptionTranslator.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/support/RedisPersistenceExceptionTranslator.java new file mode 100644 index 000000000..58809dbc4 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/support/RedisPersistenceExceptionTranslator.java @@ -0,0 +1,33 @@ +/* + * Copyright 2002-2010 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.datastore.redis.support; + +import org.springframework.dao.DataAccessException; + +/** + * Interface implemented by Spring integrations with Redis for drivers + * that throw runtime and checked exceptions. + * + * @author Mark Pollack + * + */ +public interface RedisPersistenceExceptionTranslator { + + + //NOTE some client libraries throw checked exceptions. + DataAccessException translateException(Exception ex); +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/support/RedisUtils.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/support/RedisUtils.java new file mode 100644 index 000000000..b4cc17721 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/support/RedisUtils.java @@ -0,0 +1,54 @@ +/* + * Copyright 2002-2010 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.datastore.redis.support; + +import java.io.IOException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.datastore.redis.core.RedisClient; + +/** + * Generic utility methods for working with Redis. Mainly for internal use + * within the framework. + * @author Mark Pollack + * + */ +public class RedisUtils { + + + private static final Log logger = LogFactory.getLog(RedisUtils.class); + + + /** + * Close the given Redis Client and ignore any thrown exception. + * This is useful for typical finally blocks in manual Redis code. + * @param channel the RabbitMQ Channel to close (may be null) + */ + public static void closeClient(RedisClient redisClient) { + if (redisClient != null) { + try { + redisClient.disconnect(); + } + catch (IOException ex) { + logger.debug("Could not close Redis Channel", ex); + } + catch (Throwable ex) { + logger.debug("Unexpected exception on closing Redis Client", ex); + } + } + } +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/support/converter/DefaultRedisConverter.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/support/converter/DefaultRedisConverter.java new file mode 100644 index 000000000..784edab56 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/support/converter/DefaultRedisConverter.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002-2010 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.datastore.redis.support.converter; + +import org.springframework.commons.serializer.DefaultDeserializer; +import org.springframework.commons.serializer.DefaultSerializer; +import org.springframework.commons.serializer.DeserializingConverter; +import org.springframework.commons.serializer.SerializingConverter; + +/** + * Implementation using Java Serialization + * + * @author Mark Pollack + * + */ +public class DefaultRedisConverter implements RedisConverter { + + private DeserializingConverter fromBytes = new DeserializingConverter(new DefaultDeserializer()); + private SerializingConverter toBytes = new SerializingConverter(new DefaultSerializer()); + + public Object deserialize(byte[] bytes) { + return fromBytes.convert(bytes); + } + + public byte[] serialize(Object object) { + return toBytes.convert(object); + } + +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/support/converter/RedisConverter.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/support/converter/RedisConverter.java new file mode 100644 index 000000000..7cb1fc230 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/support/converter/RedisConverter.java @@ -0,0 +1,28 @@ +/* + * Copyright 2002-2010 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.datastore.redis.support.converter; + +/** + * Basic interface serialization and deserialization from object to byte[]. Nothing is specifc to Redis + * @author Mark Pollack + * + */ +public interface RedisConverter { + + byte[] serialize(Object object); + + Object deserialize(byte[] bytes); +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/util/AbstractRedisCollection.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/util/AbstractRedisCollection.java new file mode 100644 index 000000000..6077d7f9b --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/util/AbstractRedisCollection.java @@ -0,0 +1,83 @@ +package org.springframework.datastore.redis.util; + +import java.util.Collection; +import java.util.Iterator; + +import org.springframework.datastore.redis.core.RedisTemplate; + +public abstract class AbstractRedisCollection implements RedisCollection { + + protected RedisTemplate redisTemplate; + protected String redisKey; + + public AbstractRedisCollection(RedisTemplate redisTemplate, String redisKey) { + this.redisTemplate = redisTemplate; + this.redisKey = redisKey; + } + + + /** + * They key used by the collection + * + * @return The redis key + */ + public String getRedisKey() { + return redisKey; + } + + public void clear() { + redisTemplate.deleteKeys(redisKey); + } + + public boolean isEmpty() { + return size() == 0; + } + + public Object[] toArray() { + return new Object[0]; + } + + public boolean containsAll(Collection c) { + for (Object o : c) { + if(!contains(o)) return false; + } + return true; + } + + public boolean addAll(Collection c) { + boolean changed = false; + for (Object e : c) { + boolean elChange = add(e); + if(elChange && !changed) changed = true; + } + return changed; + } + + public boolean retainAll(Collection c) { + Iterator i = iterator(); + boolean changed = false; + while (i.hasNext()) { + Object o = i.next(); + if(!c.contains(o)) { + i.remove(); + changed = true; + } + } + return changed; + } + + public boolean removeAll(Collection c) { + boolean changed = false; + for (Object e : c) { + boolean elChange = remove(e); + if(elChange && !changed) changed = true; + } + return changed; + + } + + public Object[] toArray(Object[] array) { + return new Object[0]; + } + +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/util/RedisCollection.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/util/RedisCollection.java new file mode 100644 index 000000000..b7d29da8b --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/util/RedisCollection.java @@ -0,0 +1,21 @@ +package org.springframework.datastore.redis.util; + +import java.util.Collection; +import java.util.Set; + +/** + * + * @author Graeme Rocher + * + */ +public interface RedisCollection extends Collection { + + /** + * They key used by the collection + * + * @return The redis key + */ + String getRedisKey(); + + Set members(); +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/util/RedisSet.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/util/RedisSet.java new file mode 100644 index 000000000..dbf30f2ca --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/util/RedisSet.java @@ -0,0 +1,79 @@ +package org.springframework.datastore.redis.util; + +import java.util.Iterator; +import java.util.Set; + +import org.springframework.datastore.redis.core.RedisTemplate; + +/** + * + * @author Graeme Rocher + * + */ +public class RedisSet extends AbstractRedisCollection implements Set { + + public RedisSet(RedisTemplate redisTemplate, String redisKey) { + super(redisTemplate, redisKey); + } + + public int size() { + return redisTemplate.getSetOperations().size(redisKey); + } + + public boolean contains(Object o) { + //TODO investigate cast + return redisTemplate.getSetOperations().contains(redisKey, (String)o); + } + + public Iterator iterator() { + return redisTemplate.getSetOperations().getAll(redisKey).iterator(); + } + + public boolean add(Object o) { + //TODO investigate cast + return redisTemplate.getSetOperations().add(redisKey, (String)o); + } + + public boolean remove(Object o) { + //TODO investigate cast + return redisTemplate.getSetOperations().remove(redisKey, (String)o); + } + + + public Set members() { + return redisTemplate.getSetOperations().getAll(redisKey); + } + + /* + public List members(final int offset, final int max) { + return redisTemplate.sort(redisKey, redisTemplate.sortParams().limit(offset, max)); + + }*/ + + public String getRandom() { + return redisTemplate.getSetOperations().getRandom(redisKey); + } + + public boolean removeRandom() { + return redisTemplate.getSetOperations().removeRandom(redisKey); + } + + + void intersection(RedisSet... redisSets) { + //storeIntersectionOfSets.. + } + + void union(RedisSet... redisSets) { + //storeUnionOfSets + } + + void difference(RedisSet... redisSets) { + + } + + //consider methods in google collections such as + // cartesianProduct, filter, powerSet, symmetricDifference, newRedisSet + //TODO move to another set + + // +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/util/Sets.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/util/Sets.java new file mode 100644 index 000000000..8c095ea14 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/util/Sets.java @@ -0,0 +1,18 @@ +package org.springframework.datastore.redis.util; + +import org.springframework.datastore.redis.core.RedisTemplate; + +public class Sets { + + protected RedisTemplate redisTemplate; + + public Sets(RedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; + } + + //TODO what key to assing? + + + + +} diff --git a/spring-datastore-redis/src/main/resources/META-INF/spring/app-context.xml b/spring-datastore-redis/src/main/resources/META-INF/spring/app-context.xml new file mode 100644 index 000000000..ca51b1a69 --- /dev/null +++ b/spring-datastore-redis/src/main/resources/META-INF/spring/app-context.xml @@ -0,0 +1,10 @@ + + + + Example configuration to get you started. + + + + diff --git a/spring-datastore-redis/src/test/java/org/springframework/datastore/redis/core/AbstractClientIntegrationTests.java b/spring-datastore-redis/src/test/java/org/springframework/datastore/redis/core/AbstractClientIntegrationTests.java new file mode 100644 index 000000000..9b3035364 --- /dev/null +++ b/spring-datastore-redis/src/test/java/org/springframework/datastore/redis/core/AbstractClientIntegrationTests.java @@ -0,0 +1,105 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.Map; + +import junit.framework.Assert; + +import org.junit.After; +import org.junit.Test; +import org.springframework.dao.InvalidDataAccessApiUsageException; + +import redis.clients.jedis.JedisException; + +public abstract class AbstractClientIntegrationTests { + + protected RedisClient client; + + @After + public void tearDown() throws IOException { + client.disconnect(); + } + @Test + public void save() { + String status = client.save(); + assertEquals("OK", status); + } + + @Test + public void bgsave() { + try { + String status = client.bgsave(); + assertEquals("Background saving started", status); + } catch (InvalidDataAccessApiUsageException e) { + assertEquals("ERR Background save already in progress", + e.getMessage()); + } + } + + @Test + public void bgrewriteaof() { + String status = client.bgrewriteaof(); + assertEquals("Background append only file rewriting started", status); + } + + @Test + public void lastsave() throws InterruptedException { + int before = client.lastsave(); + String st = ""; + while (!st.equals("OK")) { + try { + Thread.sleep(1000); + st = client.save(); + } catch (JedisException e) { + + } + } + int after = client.lastsave(); + assertTrue((after - before) > 0); + } + + + + @Test + public void info() { + Map infoResponse = client.info(); + Assert.assertNotNull(infoResponse); + Assert.assertTrue(infoResponse.containsKey("redis_version")); + //Map infoResponse = client.info(); + //Assert.assertTrue("Expected non empty map of info about the server.", infoResponse.size() > 0); + //Assert.assertTrue("Expected key 'redis_version' in map of info about the server.", + // infoResponse.containsKey("redis_version")); + } + + @Test + public void setAndGet() { + client.set("foo", "blah blah"); + String value = client.get("foo"); + Assert.assertEquals("blah blah", value); + } + + @Test + public void conversions() { + Person p = new Person("Joe", "Trader", 33); + + } +} diff --git a/spring-datastore-redis/src/test/java/org/springframework/datastore/redis/core/Person.java b/spring-datastore-redis/src/test/java/org/springframework/datastore/redis/core/Person.java new file mode 100644 index 000000000..5d919b892 --- /dev/null +++ b/spring-datastore-redis/src/test/java/org/springframework/datastore/redis/core/Person.java @@ -0,0 +1,96 @@ +package org.springframework.datastore.redis.core; + +/* + * Copyright 2002-2010 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. + */ + +import java.io.Serializable; + +public class Person implements Serializable { + + private String firstName; + + private String lastName; + + private int age; + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public Person(String firstName, String lastName, int age) { + super(); + this.firstName = firstName; + this.lastName = lastName; + this.age = age; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + age; + result = prime * result + + ((firstName == null) ? 0 : firstName.hashCode()); + result = prime * result + + ((lastName == null) ? 0 : lastName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Person other = (Person) obj; + if (age != other.age) + return false; + if (firstName == null) { + if (other.firstName != null) + return false; + } else if (!firstName.equals(other.firstName)) + return false; + if (lastName == null) { + if (other.lastName != null) + return false; + } else if (!lastName.equals(other.lastName)) + return false; + return true; + } + +} diff --git a/spring-datastore-redis/src/test/java/org/springframework/datastore/redis/core/RedisTemplateIntegrationTests.java b/spring-datastore-redis/src/test/java/org/springframework/datastore/redis/core/RedisTemplateIntegrationTests.java new file mode 100644 index 000000000..3844bed3d --- /dev/null +++ b/spring-datastore-redis/src/test/java/org/springframework/datastore/redis/core/RedisTemplateIntegrationTests.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.datastore.redis.core.jredis.JRedisClientFactory; + +public class RedisTemplateIntegrationTests { + + RedisTemplate template; + @Before + public void setUp() { + template = new RedisTemplate(new JRedisClientFactory()); + } + + @Test + public void conversions() { + Person p = new Person("Joe", "Trader", 33); + template.convertAndSet("trader:1", p); + Person samePerson = template.getAndConvert("trader:1", Person.class); + Assert.assertEquals(p, samePerson); + } +} diff --git a/spring-datastore-redis/src/test/java/org/springframework/datastore/redis/core/jedis/JedisRedisClientIntegrationTests.java b/spring-datastore-redis/src/test/java/org/springframework/datastore/redis/core/jedis/JedisRedisClientIntegrationTests.java new file mode 100644 index 000000000..fb52ab8fb --- /dev/null +++ b/spring-datastore-redis/src/test/java/org/springframework/datastore/redis/core/jedis/JedisRedisClientIntegrationTests.java @@ -0,0 +1,36 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core.jedis; + +import org.junit.Before; +import org.springframework.datastore.redis.core.AbstractClientIntegrationTests; +import org.springframework.datastore.redis.core.RedisClientFactory; + +public class JedisRedisClientIntegrationTests extends + AbstractClientIntegrationTests { + + @Before + public void setUp() { + RedisClientFactory clientFactory = new JedisClientFactory(); + clientFactory.setPassword("foobared"); + client = clientFactory.createClient(); + client.flushAll(); + } + + + +} diff --git a/spring-datastore-redis/src/test/java/org/springframework/datastore/redis/core/jredis/JRedisClientIntegrationTests.java b/spring-datastore-redis/src/test/java/org/springframework/datastore/redis/core/jredis/JRedisClientIntegrationTests.java new file mode 100644 index 000000000..ad6f67338 --- /dev/null +++ b/spring-datastore-redis/src/test/java/org/springframework/datastore/redis/core/jredis/JRedisClientIntegrationTests.java @@ -0,0 +1,33 @@ +/* + * Copyright 2002-2010 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.datastore.redis.core.jredis; + +import org.junit.Before; +import org.springframework.datastore.redis.core.AbstractClientIntegrationTests; +import org.springframework.datastore.redis.core.RedisClientFactory; + +public class JRedisClientIntegrationTests extends AbstractClientIntegrationTests { + + + @Before + public void setUp() { + RedisClientFactory clientFactory = new JRedisClientFactory(); + clientFactory.setPassword("foobared"); + client = clientFactory.createClient(); + } + +} diff --git a/spring-datastore-redis/src/test/resources/log4j.properties b/spring-datastore-redis/src/test/resources/log4j.properties new file mode 100644 index 000000000..6d5422d74 --- /dev/null +++ b/spring-datastore-redis/src/test/resources/log4j.properties @@ -0,0 +1,13 @@ +log4j.rootCategory=INFO, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n + +log4j.category.org.apache.activemq=ERROR +log4j.category.org.springframework.batch=DEBUG +log4j.category.org.springframework.transaction=INFO + +log4j.category.org.hibernate.SQL=DEBUG +# for debugging datasource initialization +# log4j.category.test.jdbc=DEBUG diff --git a/spring-datastore-redis/src/test/resources/org/springframework/datastore/ExampleConfigurationTests-context.xml b/spring-datastore-redis/src/test/resources/org/springframework/datastore/ExampleConfigurationTests-context.xml new file mode 100644 index 000000000..4717a9b6b --- /dev/null +++ b/spring-datastore-redis/src/test/resources/org/springframework/datastore/ExampleConfigurationTests-context.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/spring-datastore-redis/template.mf b/spring-datastore-redis/template.mf index 1b83aedfd..571e40fea 100644 --- a/spring-datastore-redis/template.mf +++ b/spring-datastore-redis/template.mf @@ -11,10 +11,15 @@ Import-Template: org.springframework.util.*;version="[3.0.0, 4.0.0)", org.springframework.data.core.*;version="[1.0.0, 2.0.0)", org.springframework.datastore.core.*;version="[1.0.0, 2.0.0)", + org.springframework.datastore.*;version="[1.0.0, 2.0.0)", org.springframework.datastore.persistence.*;version="[1.0.0, 2.0.0)", org.springframework.datastore.document.*;version="[1.0.0, 2.0.0)", org.aopalliance.*;version="[1.0.0, 2.0.0)";resolution:=optional, org.apache.commons.logging.*;version="[1.1.1, 2.0.0)", - org.w3c.dom.*;version="0" - + org.w3c.dom.*;version="0", + org.jredis.*;version="[1.0.0, 2.0.0)", + org.jredis.ri.alphazero.*;version="[1.0.0, 2.0.0)", + org.springframework.commons.serializer.*;version="[1.0.0, 2.0.0)", + redis.clients.jedis.*;version="[1.0.0, 2.0.0)", + redis.clients.util.*;version="[1.0.0, 2.0.0)",