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/RedisConnectionFailureException.java similarity index 72% rename from spring-datastore-redis/src/main/java/org/springframework/datastore/redis/CannotGetRedisConnectionException.java rename to spring-datastore-redis/src/main/java/org/springframework/datastore/redis/RedisConnectionFailureException.java index 45cd5090f..3de6f8dca 100644 --- a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/CannotGetRedisConnectionException.java +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/RedisConnectionFailureException.java @@ -1,35 +1,35 @@ -/* - * Copyright 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); - } -} +/* + * Copyright 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 the Redis connection fails completely. + * + * @author Mark Pollack + */ +public class RedisConnectionFailureException extends DataAccessResourceFailureException { + + public RedisConnectionFailureException(String msg) { + super(msg); + } + + public RedisConnectionFailureException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/DataType.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/DataType.java new file mode 100644 index 000000000..ce095c327 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/DataType.java @@ -0,0 +1,56 @@ +/* + * Copyright 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.connection; + +import java.util.EnumSet; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Enumeration of the Redis data types. + * + * @author Costin Leau + */ +public enum DataType { + + NONE("none"), STRING("string"), LIST("list"), SET("set"), ZSET("zset"), HASH("hash"); + + private static final Map codeLookup = new ConcurrentHashMap(6); + + static { + for (DataType type : EnumSet.allOf(DataType.class)) + codeLookup.put(type.code, type); + + } + + private final String code; + + DataType(String name) { + this.code = name; + } + + public String code() { + return code; + } + + public static DataType fromCode(String code) { + DataType data = codeLookup.get(code); + if (data == null) + throw new IllegalArgumentException("unknown data type code"); + return data; + } +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/RedisCommands.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/RedisCommands.java index eb12526cc..38a2fc9e5 100644 --- a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/RedisCommands.java +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/RedisCommands.java @@ -29,7 +29,7 @@ public interface RedisCommands { int del(String... keys); - DataTypes type(String key); + DataType type(String key); Collection keys(String pattern); diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/RedisConnection.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/RedisConnection.java index 34a53d729..70850faa3 100644 --- a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/RedisConnection.java +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/RedisConnection.java @@ -24,7 +24,8 @@ import org.springframework.datastore.redis.UncategorizedRedisException; * * @author Costin Leau */ -public interface RedisConnection extends RedisCommands { +public interface RedisConnection extends RedisCommands, RedisHashCommands, RedisListCommands, RedisSetCommands, + RedisStringCommands, RedisZSetCommands { /** * Close (or quit) the connection. @@ -35,5 +36,7 @@ public interface RedisConnection extends RedisCommands { boolean isClosed(); - T getNativeConnection(); + T getNativeConnection(); + + String getCharset(); } diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/DataTypes.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/RedisConnectionFactory.java similarity index 67% rename from spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/DataTypes.java rename to spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/RedisConnectionFactory.java index e8f14fb6a..aff2b61c5 100644 --- a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/DataTypes.java +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/RedisConnectionFactory.java @@ -16,12 +16,15 @@ package org.springframework.datastore.redis.core.connection; +import org.springframework.dao.support.PersistenceExceptionTranslator; + /** - * Enumeration of the Redis data types. + * Thread-safe factory of Redis connections. Additionally performs exception translation + * between the underlying Redis client library and Spring DAO exceptions. * * @author Costin Leau */ -public enum DataTypes { +public interface RedisConnectionFactory extends PersistenceExceptionTranslator { - NONE, STRING, LIST, SET, ZSET, HASH; + RedisConnection getConnection(); } diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/RedisStringCommands.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/RedisStringCommands.java index f9b9464e0..6f1ecbb23 100644 --- a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/RedisStringCommands.java +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/RedisStringCommands.java @@ -17,7 +17,7 @@ package org.springframework.datastore.redis.core.connection; /** - * String specific commands supported by Redis . + * String specific commands supported by Redis. * * @author Costin Leau */ diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/jedis/JedisConnection.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/jedis/JedisConnection.java new file mode 100644 index 000000000..116f10f5b --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/jedis/JedisConnection.java @@ -0,0 +1,295 @@ +/* + * Copyright 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.connection.jedis; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.Collection; + +import org.springframework.dao.DataAccessException; +import org.springframework.datastore.keyvalue.UncategorizedKeyvalueStoreException; +import org.springframework.datastore.redis.UncategorizedRedisException; +import org.springframework.datastore.redis.core.connection.DataType; +import org.springframework.datastore.redis.core.connection.RedisConnection; +import org.springframework.util.ReflectionUtils; + +import redis.clients.jedis.Client; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisException; + +/** + * Jedis based {@link RedisConnection}. + * + * @author Costin Leau + */ +public class JedisConnection implements RedisConnection { + + private static final Field CLIENT_FIELD; + + static { + CLIENT_FIELD = ReflectionUtils.findField(Jedis.class, "client", Client.class); + ReflectionUtils.makeAccessible(CLIENT_FIELD); + } + + private final Jedis jedis; + private final Client client; + + public JedisConnection(Jedis jedis) { + this.jedis = jedis; + // extract underlying client for batch operations + client = (Client) ReflectionUtils.getField(CLIENT_FIELD, jedis); + } + + protected DataAccessException convertJedisAccessException(Exception ex) { + if (ex instanceof JedisException) { + return JedisUtils.convertJedisAccessException((JedisException) ex); + } + if (ex instanceof IOException) { + return JedisUtils.convertJedisAccessException((IOException) ex); + } + + throw new UncategorizedKeyvalueStoreException("Unknown jedis exception", ex); + } + + @Override + public void close() throws UncategorizedRedisException { + try { + jedis.disconnect(); + jedis.quit(); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public String getCharset() { + return "UTF-8"; + } + + @Override + public Jedis getNativeConnection() { + return jedis; + } + + @Override + public boolean isClosed() { + try { + return !jedis.isConnected(); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public int dbSize() { + try { + return jedis.dbSize(); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public int del(String... keys) { + try { + return jedis.del(keys); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public void discard() { + try { + client.discard(); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public void exec() { + try { + client.exec(); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public boolean exists(String key) { + try { + return (jedis.exists(key) == 1); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public boolean expire(String key, long seconds) { + try { + return (jedis.expire(key, (int) seconds) == 1); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public Collection keys(String pattern) { + try { + return (jedis.keys(pattern)); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public void multi() { + try { + jedis.multi(); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public boolean persist(String key) { + try { + return (jedis.persist(key) == 1); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public String randomKey() { + try { + return jedis.randomKey(); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public boolean rename(String oldName, String newName) { + try { + return (JedisUtils.OK_CODE.equals(jedis.rename(oldName, newName))); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public boolean renameNx(String oldName, String newName) { + try { + return (JedisUtils.OK_CODE.equals(jedis.renamenx(oldName, newName))); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public void select(int dbIndex) { + try { + jedis.select(dbIndex); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public int ttl(String key) { + try { + return jedis.ttl(key); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public DataType type(String key) { + try { + return DataType.fromCode(jedis.type(key)); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public void unwatch() { + try { + jedis.unwatch(); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public void watch(String... keys) { + try { + for (String key : keys) { + jedis.watch(key); + } + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public int hSet(String key, String field, String value) { + try { + return jedis.hset(key, field, value); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public int lPush(String key, String value) { + try { + return jedis.lpush(key, value); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public int rPush(String key, String value) { + try { + return jedis.rpush(key, value); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public String get(String key) { + try { + return jedis.get(key); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + @Override + public void set(String key, String value) { + try { + jedis.set(key, value); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } +} \ No newline at end of file diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/jedis/JedisConnectionFactory.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/jedis/JedisConnectionFactory.java new file mode 100644 index 000000000..ff8c0a4be --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/jedis/JedisConnectionFactory.java @@ -0,0 +1,235 @@ +/* + * Copyright 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.connection.jedis; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.concurrent.TimeoutException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.dao.DataAccessException; +import org.springframework.datastore.redis.core.connection.RedisConnection; +import org.springframework.datastore.redis.core.connection.RedisConnectionFactory; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisShardInfo; + +/** + * Connection factory using Jedis underneath. + * + * @author Costin Leau + */ +public class JedisConnectionFactory implements InitializingBean, DisposableBean, RedisConnectionFactory { + + private final static Log log = LogFactory.getLog(JedisConnectionFactory.class); + + private JedisShardInfo shardInfo; + private String password; + private int timeout; + + private boolean usePool = true; + + private JedisPool pool = null; + // taken from Jedis code + private int poolSize = 10; + + /** + * Constructs a new JedisConnectionFactory instance. + */ + public JedisConnectionFactory() { + this(getDefaultHostName()); + } + + /** + * Constructs a new JedisConnectionFactory instance. + * + * @param hostname + */ + public JedisConnectionFactory(String hostName) { + Assert.hasText(hostName); + shardInfo = new JedisShardInfo(hostName); + } + + /** + * Constructs a new JedisConnectionFactory instance. + * + * @param hostname + * @param port + */ + public JedisConnectionFactory(String hostName, int port) { + shardInfo = new JedisShardInfo(hostName, port); + } + + /** + * Constructs a new JedisConnectionFactory instance. + * + * @param shardInfo + */ + public JedisConnectionFactory(JedisShardInfo shardInfo) { + this.shardInfo = shardInfo; + } + + /** + * Returns a Jedis instance to be used as a Redis connection. + * The instance can be newly created or retrieved from a pool. + * + * @return Jedis instance ready for wrapping into a {@link RedisConnection}. + */ + protected Jedis fetchJedisConnector() { + try { + if (usePool) { + return pool.getResource(); + } + return new Jedis(getShardInfo()); + } catch (TimeoutException ex) { + throw JedisUtils.convertJedisAccessException(ex); + } + } + + public void afterPropertiesSet() { + if (StringUtils.hasLength(password)) { + shardInfo.setPassword(password); + } + + if (timeout > 0) { + shardInfo.setTimeout(timeout); + } + + if (usePool) { + int size = getPoolSize(); + pool = new JedisPool(shardInfo); + pool.setResourcesNumber(size); + } + } + + public void destroy() throws Exception { + // TODO: should this component do tracking of all returned connections + // normally not but then again we're the ones creating the connections + // so we end up behaving like a pool + } + + public JedisConnection getConnection() { + return new JedisConnection(fetchJedisConnector()); + } + + @Override + public DataAccessException translateExceptionIfPossible(RuntimeException ex) { + return JedisUtils.convertJedisAccessException(ex); + } + + private static String getDefaultHostName() { + String temp; + try { + InetAddress localMachine = InetAddress.getLocalHost(); + temp = localMachine.getHostName(); + if (log.isDebugEnabled()) + log.debug("Using hostname [" + temp + "] for hostname."); + } catch (UnknownHostException e) { + log.warn("Could not get host name, using 'localhost' as default value", e); + temp = "localhost"; + } + return temp; + } + + /** + * @return the password + */ + public String getPassword() { + return password; + } + + /** + * @param password the password to set + */ + public void setPassword(String password) { + this.password = password; + } + + /** + * Returns the shardInfo. + * + * @return Returns the shardInfo + */ + public JedisShardInfo getShardInfo() { + return shardInfo; + } + + /** + * @param shardInfo The shardInfo to set. + */ + public void setShardInfo(JedisShardInfo shardInfo) { + this.shardInfo = shardInfo; + } + + /** + * Returns the timeout. + * + * @return Returns the timeout + */ + public int getTimeout() { + return timeout; + } + + /** + * @param timeout The timeout to set. + */ + public void setTimeout(int timeout) { + this.timeout = timeout; + } + + /** + * Indicates the use of a connection pool. + * + * @return Returns the use of connection pooling. + */ + public boolean isPooling() { + return usePool; + } + + /** + * Turns on or off the use of connection pooling. + * + * @param usePool The usePool to set. + */ + public void setPooling(boolean usePool) { + this.usePool = usePool; + } + + /** + * Returns the poolSize. + * + * @return Returns the poolSize + */ + public int getPoolSize() { + return poolSize; + } + + /** + * @param poolSize The poolSize to set. + */ + public void setPoolSize(int poolSize) { + Assert.isTrue(poolSize > 0, "pool size needs to be bigger then zero"); + this.poolSize = poolSize; + usePool = true; + } +} \ No newline at end of file diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/jedis/JedisUtils.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/jedis/JedisUtils.java new file mode 100644 index 000000000..ae9925171 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/connection/jedis/JedisUtils.java @@ -0,0 +1,61 @@ +/* + * Copyright 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.connection.jedis; + +import java.io.IOException; +import java.net.UnknownHostException; +import java.util.concurrent.TimeoutException; + +import org.springframework.dao.DataAccessException; +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.datastore.redis.RedisConnectionFailureException; +import org.springframework.datastore.redis.UncategorizedRedisException; + +import redis.clients.jedis.JedisException; + +/** + * Helper class featuring methods for Jedis connection handling, providing support for exception translation. + * + * @author Costin Leau + */ +public abstract class JedisUtils { + + public static final String OK_CODE = "OK"; + + public static DataAccessException convertJedisAccessException(JedisException ex) { + return new InvalidDataAccessApiUsageException(ex.getMessage(), ex); + } + + public static DataAccessException convertJedisAccessException(RuntimeException ex) { + if (ex instanceof JedisException) { + return convertJedisAccessException((JedisException) ex); + } + + return new UncategorizedRedisException("Unknown exception", ex); + } + + static DataAccessException convertJedisAccessException(IOException ex) { + if (ex instanceof UnknownHostException) { + return new RedisConnectionFailureException("Unknown host " + ex.getMessage(), ex); + } + return new RedisConnectionFailureException("Could not connect to Redis server", ex); + } + + static DataAccessException convertJedisAccessException(TimeoutException ex) { + throw new RedisConnectionFailureException("Jedis pool timed out. Could not get Redis Connection", ex); + } +} 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 deleted file mode 100644 index 8ce7988d8..000000000 --- a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jedis/CachingJedisClientFactory.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 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 deleted file mode 100644 index ac243925c..000000000 --- a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jedis/JedisClient.java +++ /dev/null @@ -1,535 +0,0 @@ -/* - * Copyright 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 deleted file mode 100644 index 4221e8c10..000000000 --- a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jedis/JedisClientCallback.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 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 deleted file mode 100644 index 5d208d364..000000000 --- a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jedis/JedisClientFactory.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 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.jedis.JedisShardInfo; -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 JedisShardInfo 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(JedisShardInfo shardInfo) { - this.shardInfo = shardInfo; - } - - protected JedisShardInfo 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 deleted file mode 100644 index 42e2043d7..000000000 --- a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/jedis/JedisPersistenceExceptionTranslator.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 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.dao.support.PersistenceExceptionTranslator; -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, - PersistenceExceptionTranslator { - - public DataAccessException translateException(Exception ex) { - return new InvalidDataAccessApiUsageException(ex.getMessage(), ex); - } - - public DataAccessException translateExceptionIfPossible(RuntimeException ex) { - return new InvalidDataAccessApiUsageException(ex.getMessage(), ex); - } -}