diff --git a/src/main/java/org/springframework/data/redis/connection/RedisPipelineException.java b/src/main/java/org/springframework/data/redis/connection/RedisPipelineException.java new file mode 100644 index 000000000..6f1a514f7 --- /dev/null +++ b/src/main/java/org/springframework/data/redis/connection/RedisPipelineException.java @@ -0,0 +1,81 @@ +/* + * Copyright 2011-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.redis.connection; + +import java.util.Collections; +import java.util.List; + +import org.springframework.dao.InvalidDataAccessResourceUsageException; + +/** + * Exception thrown when executing/closing a pipeline that contains one or multiple invalid/incorrect statements. + * The exception contains the pipeline result, allowing for analysis and tracing. + *

+ * Typically, the first exception returned by the pipeline is used as the cause of this exception for easier + * debugging. + * + * @author Costin Leau + */ +public class RedisPipelineException extends InvalidDataAccessResourceUsageException { + + private final List results; + + /** + * Constructs a new RedisPipelineException instance. + * + * @param msg the message + * @param cause the cause + * @param pipelineResult the pipeline result + */ + public RedisPipelineException(String msg, Throwable cause, List pipelineResult) { + super(msg, cause); + results = Collections.unmodifiableList(pipelineResult); + } + + /** + * Constructs a new RedisPipelineException instance using a default message. + * + * @param cause the cause + * @param pipelineResult the pipeline result + */ + public RedisPipelineException(Exception cause, List pipelineResult) { + this("Pipeline contained one or more invalid commands", cause, pipelineResult); + } + + + /** + * Constructs a new RedisPipelineException instance. + * + * @param msg message + * @param pipelineResult pipeline partial results + */ + public RedisPipelineException(String msg, List pipelineResult) { + super(msg); + results = Collections.unmodifiableList(pipelineResult); + } + + /** + * Returns the result of the pipeline that caused the exception. + * Typically contains both the results of the successful statements but also + * the exceptions of the incorrect ones. + * + * @return result of the pipeline + */ + public List getPipelineResult() { + return results; + } +} \ No newline at end of file diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnection.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnection.java index 746f6178d..d43ae07de 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnection.java @@ -30,6 +30,7 @@ import org.springframework.data.redis.RedisSystemException; import org.springframework.data.redis.connection.DataType; import org.springframework.data.redis.connection.MessageListener; import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisPipelineException; import org.springframework.data.redis.connection.RedisSubscribedConnectionException; import org.springframework.data.redis.connection.SortParameters; import org.springframework.data.redis.connection.Subscription; @@ -68,8 +69,8 @@ public class JedisConnection implements RedisConnection { static { CLIENT_FIELD = ReflectionUtils.findField(BinaryJedis.class, "client", Client.class); ReflectionUtils.makeAccessible(CLIENT_FIELD); - SEND_COMMAND = ReflectionUtils.findMethod(Connection.class, "sendCommand", - new Class[] { Command.class, byte[][].class }); + SEND_COMMAND = ReflectionUtils.findMethod(Connection.class, "sendCommand", new Class[] { Command.class, + byte[][].class }); ReflectionUtils.makeAccessible(SEND_COMMAND); GET_RESPONSE = ReflectionUtils.findMethod(Queable.class, "getResponse", Builder.class); ReflectionUtils.makeAccessible(GET_RESPONSE); @@ -135,27 +136,33 @@ public class JedisConnection implements RedisConnection { public Object execute(String command, byte[]... args) { Assert.hasText(command, "a valid command needs to be specified"); - List mArgs = new ArrayList(); - if (!ObjectUtils.isEmpty(args)) { - Collections.addAll(mArgs, args); + try { + List mArgs = new ArrayList(); + if (!ObjectUtils.isEmpty(args)) { + Collections.addAll(mArgs, args); + } + + Object result = ReflectionUtils.invokeMethod(SEND_COMMAND, client, + Command.valueOf(command.trim().toUpperCase()), mArgs.toArray(new byte[mArgs.size()][])); + if (isQueueing() || isPipelined()) { + Object target = (isPipelined() ? pipeline : transaction); + ReflectionUtils.invokeMethod(GET_RESPONSE, target, new Builder() { + public Object build(Object data) { + return data; + } + + public String toString() { + return "Object"; + } + }); + } + else { + client.getOne(); + } + return result; + } catch (Exception ex) { + throw convertJedisAccessException(ex); } - - Object result = ReflectionUtils.invokeMethod(SEND_COMMAND, client, - Command.valueOf(command.trim().toUpperCase()), mArgs.toArray(new byte[mArgs.size()][])); - if (isQueueing() || isPipelined()) { - Object target = (isPipelined() ? pipeline : transaction); - ReflectionUtils.invokeMethod(GET_RESPONSE, target, new Builder() { - public Object build(Object data) { - return data; - } - - public String toString() { - return "Object"; - } - }); - } - - return result; } public void close() throws DataAccessException { @@ -232,13 +239,25 @@ public class JedisConnection implements RedisConnection { if (pipeline != null) { List execute = pipeline.syncAndReturnAll(); if (execute != null && !execute.isEmpty()) { - return execute; + Exception cause = null; + for (int i = 0; i < execute.size(); i++) { + Object object = execute.get(i); + if (object instanceof Exception) { + DataAccessException dataAccessException = convertJedisAccessException((Exception) object); + if (cause == null) { + cause = dataAccessException; + } + execute.set(i, dataAccessException); + } + } + if (cause != null) { + throw new RedisPipelineException(cause, execute); + } } } return Collections.emptyList(); } - public List sort(byte[] key, SortParameters params) { SortingParams sortParams = JedisUtils.convertSortParams(params); diff --git a/src/main/java/org/springframework/data/redis/connection/jredis/JredisConnection.java b/src/main/java/org/springframework/data/redis/connection/jredis/JredisConnection.java index f0cdc25d1..d99e7dbe1 100644 --- a/src/main/java/org/springframework/data/redis/connection/jredis/JredisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/jredis/JredisConnection.java @@ -89,14 +89,17 @@ public class JredisConnection implements RedisConnection { public Object execute(String command, byte[]... args) { Assert.hasText(command, "a valid command needs to be specified"); - List mArgs = new ArrayList(); - if (!ObjectUtils.isEmpty(args)) { - Collections.addAll(mArgs, args); + try { + List mArgs = new ArrayList(); + if (!ObjectUtils.isEmpty(args)) { + Collections.addAll(mArgs, args); + } + + return ReflectionUtils.invokeMethod(SERVICE_REQUEST, jredis, Command.valueOf(command.trim().toUpperCase()), + mArgs.toArray(new byte[mArgs.size()][])); + } catch (Exception ex) { + throw convertJredisAccessException(ex); } - - return ReflectionUtils.invokeMethod(SERVICE_REQUEST, jredis, Command.valueOf(command.trim().toUpperCase()), - mArgs.toArray(new byte[mArgs.size()][])); - } public void close() throws RedisSystemException { diff --git a/src/main/java/org/springframework/data/redis/connection/rjc/RjcConnection.java b/src/main/java/org/springframework/data/redis/connection/rjc/RjcConnection.java index 621dc9591..3d203e587 100644 --- a/src/main/java/org/springframework/data/redis/connection/rjc/RjcConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/rjc/RjcConnection.java @@ -83,9 +83,16 @@ public class RjcConnection implements RedisConnection { public Object execute(String command, byte[]... args) { Assert.hasText(command, "a valid command needs to be specified"); - connection.sendCommand(Command.valueOf(command.trim().toUpperCase()), - (ObjectUtils.isEmpty(args) ? new byte[0][] : args)); - return connection.getAll(); + try { + connection.sendCommand(Command.valueOf(command.trim().toUpperCase()), + (ObjectUtils.isEmpty(args) ? new byte[0][] : args)); + if (!isPipelined()) { + return connection.getAll(); + } + return null; + } catch (Exception ex) { + throw convertRjcAccessException(ex); + } } public void close() throws DataAccessException { diff --git a/src/main/java/org/springframework/data/redis/connection/srp/SrpConnection.java b/src/main/java/org/springframework/data/redis/connection/srp/SrpConnection.java index b7bfa2617..b31b2ab13 100644 --- a/src/main/java/org/springframework/data/redis/connection/srp/SrpConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/srp/SrpConnection.java @@ -80,14 +80,18 @@ public class SrpConnection implements RedisConnection { public Object execute(String command, byte[]... args) { Assert.hasText(command, "a valid command needs to be specified"); - String name = command.trim().toUpperCase(); - Command cmd = new Command(name.getBytes(Charsets.UTF_8), args); - if (isPipelined()) { - client.pipeline(name, cmd); - return null; - } - else { - return client.execute(name, cmd); + try { + String name = command.trim().toUpperCase(); + Command cmd = new Command(name.getBytes(Charsets.UTF_8), args); + if (isPipelined()) { + client.pipeline(name, cmd); + return null; + } + else { + return client.execute(name, cmd); + } + } catch (RedisException ex) { + throw convertSRAccessException(ex); } } diff --git a/src/test/java/org/springframework/data/redis/connection/jredis/JRedisConnectionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/jredis/JRedisConnectionIntegrationTests.java index c38a6134c..8d92a6397 100644 --- a/src/test/java/org/springframework/data/redis/connection/jredis/JRedisConnectionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jredis/JRedisConnectionIntegrationTests.java @@ -91,4 +91,8 @@ public class JRedisConnectionIntegrationTests extends AbstractConnectionIntegrat public void testBitSet() throws Exception { } + @Ignore + public void exceptionExecuteNativeWithPipeline() throws Exception { + } + } \ No newline at end of file