You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2017/04/06 21:57:09 UTC
tinkerpop git commit: TINKERPOP-1044 Additional error data to Gremlin
Server responses
Repository: tinkerpop
Updated Branches:
refs/heads/TINKERPOP-1044 [created] b29bab0ac
TINKERPOP-1044 Additional error data to Gremlin Server responses
Applies to all protocols: http, nio, and websocket. Added stack trace and exception hierarchy. Deprecated the "Exception-Class" in the http protocol.
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/b29bab0a
Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/b29bab0a
Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/b29bab0a
Branch: refs/heads/TINKERPOP-1044
Commit: b29bab0acada54aa525184935d96b41f9cf0854b
Parents: 95d3efc
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Thu Apr 6 17:55:39 2017 -0400
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Thu Apr 6 17:55:39 2017 -0400
----------------------------------------------------------------------
.../tinkerpop/gremlin/driver/Handler.java | 11 ++++--
.../apache/tinkerpop/gremlin/driver/Tokens.java | 3 ++
.../driver/exception/ResponseException.java | 20 +++++++++++
.../gremlin/driver/message/ResponseMessage.java | 19 ++++++++++
.../gremlin/driver/ResultQueueTest.java | 2 --
.../handler/GremlinResponseFrameEncoder.java | 6 ++--
.../handler/HttpGremlinEndpointHandler.java | 9 +++--
.../gremlin/server/handler/IteratorHandler.java | 5 ++-
.../handler/NioGremlinResponseEncoder.java | 5 +--
.../server/handler/OpExecutorHandler.java | 1 +
.../server/handler/OpSelectorHandler.java | 4 ++-
.../handler/WsGremlinResponseEncoder.java | 5 +--
.../server/op/AbstractEvalOpProcessor.java | 31 +++++++++++-----
.../gremlin/server/op/AbstractOpProcessor.java | 5 +--
.../op/traversal/TraversalOpProcessor.java | 35 +++++++++++++-----
.../gremlin/server/util/ExceptionHelper.java | 38 ++++++++++++++++++++
.../server/GremlinDriverIntegrateTest.java | 5 +++
.../server/GremlinServerHttpIntegrateTest.java | 8 +++++
18 files changed, 179 insertions(+), 33 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b29bab0a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Handler.java
----------------------------------------------------------------------
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Handler.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Handler.java
index 650bc2f..1bd0a3b 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Handler.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Handler.java
@@ -235,8 +235,15 @@ final class Handler {
}
} else {
// this is a "success" but represents no results otherwise it is an error
- if (statusCode != ResponseStatusCode.NO_CONTENT)
- queue.markError(new ResponseException(response.getStatus().getCode(), response.getStatus().getMessage()));
+ if (statusCode != ResponseStatusCode.NO_CONTENT) {
+ final Map<String,Object> attributes = response.getStatus().getAttributes();
+ final String stackTrace = attributes.containsKey(Tokens.STATUS_ATTRIBUTE_STACK_TRACE) ?
+ (String) attributes.get(Tokens.STATUS_ATTRIBUTE_STACK_TRACE) : null;
+ final List<String> exceptions = attributes.containsKey(Tokens.STATUS_ATTRIBUTE_EXCEPTIONS) ?
+ (List<String>) attributes.get(Tokens.STATUS_ATTRIBUTE_EXCEPTIONS) : null;
+ queue.markError(new ResponseException(response.getStatus().getCode(), response.getStatus().getMessage(),
+ exceptions, stackTrace));
+ }
}
// as this is a non-PARTIAL_CONTENT code - the stream is done
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b29bab0a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Tokens.java
----------------------------------------------------------------------
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Tokens.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Tokens.java
index fb577d7..9aee728 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Tokens.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Tokens.java
@@ -155,4 +155,7 @@ public final class Tokens {
public static final String VAL_AGGREGATE_TO_SET = "set";
public static final String VAL_TRAVERSAL_SOURCE_ALIAS = "g";
+
+ public static final String STATUS_ATTRIBUTE_EXCEPTIONS = "exceptions";
+ public static final String STATUS_ATTRIBUTE_STACK_TRACE = "stackTrace";
}
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b29bab0a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/exception/ResponseException.java
----------------------------------------------------------------------
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/exception/ResponseException.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/exception/ResponseException.java
index 3ae4e75..800c2fc 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/exception/ResponseException.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/exception/ResponseException.java
@@ -20,18 +20,38 @@ package org.apache.tinkerpop.gremlin.driver.exception;
import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
+import java.util.List;
+import java.util.Optional;
+
/**
* @author Stephen Mallette (http://stephen.genoprime.com)
*/
public class ResponseException extends Exception {
private ResponseStatusCode responseStatusCode;
+ private String remoteStackTrace = null;
+ private List<String> remoteExceptionHierarchy = null;
public ResponseException(final ResponseStatusCode responseStatusCode, final String serverMessage) {
super(serverMessage);
this.responseStatusCode = responseStatusCode;
}
+ public ResponseException(final ResponseStatusCode responseStatusCode, final String serverMessage,
+ final List<String> remoteExceptionHierarchy, final String remoteStackTrace) {
+ this(responseStatusCode, serverMessage);
+ this.remoteExceptionHierarchy = remoteExceptionHierarchy;
+ this.remoteStackTrace = remoteStackTrace;
+ }
+
public ResponseStatusCode getResponseStatusCode() {
return responseStatusCode;
}
+
+ public Optional<String> getRemoteStackTrace() {
+ return Optional.ofNullable(remoteStackTrace);
+ }
+
+ public Optional<List<String>> getRemoteExceptionHierarchy() {
+ return Optional.ofNullable(remoteExceptionHierarchy);
+ }
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b29bab0a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/message/ResponseMessage.java
----------------------------------------------------------------------
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/message/ResponseMessage.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/message/ResponseMessage.java
index c5a00f6..f4126b1 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/message/ResponseMessage.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/message/ResponseMessage.java
@@ -18,7 +18,12 @@
*/
package org.apache.tinkerpop.gremlin.driver.message;
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.apache.tinkerpop.gremlin.driver.Tokens;
+import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
+
import java.util.Collections;
+import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@@ -102,6 +107,20 @@ public final class ResponseMessage {
return this;
}
+ public Builder statusAttributeException(final Throwable ex) {
+ statusAttribute(Tokens.STATUS_ATTRIBUTE_EXCEPTIONS, IteratorUtils.asList(
+ IteratorUtils.map(ExceptionUtils.getThrowableList(ex), t -> t.getClass().getName())));
+ statusAttribute(Tokens.STATUS_ATTRIBUTE_STACK_TRACE, ExceptionUtils.getFullStackTrace(ex));
+ return this;
+ }
+
+ public Builder statusAttribute(final String key, final Object value) {
+ if (this.attributes == Collections.EMPTY_MAP)
+ attributes = new HashMap<>();
+ attributes.put(key, value);
+ return this;
+ }
+
public Builder result(final Object result) {
this.result = result;
return this;
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b29bab0a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ResultQueueTest.java
----------------------------------------------------------------------
diff --git a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ResultQueueTest.java b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ResultQueueTest.java
index 67bbc48..a7e6066 100644
--- a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ResultQueueTest.java
+++ b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ResultQueueTest.java
@@ -37,8 +37,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.greaterThan;
-import static org.hamcrest.core.IsCollectionContaining.hasItem;
-import static org.hamcrest.Matchers.contains;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b29bab0a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/GremlinResponseFrameEncoder.java
----------------------------------------------------------------------
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/GremlinResponseFrameEncoder.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/GremlinResponseFrameEncoder.java
index d0f9d76..3aa8c36 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/GremlinResponseFrameEncoder.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/GremlinResponseFrameEncoder.java
@@ -20,11 +20,13 @@ package org.apache.tinkerpop.gremlin.server.handler;
import com.codahale.metrics.Meter;
import org.apache.tinkerpop.gremlin.driver.MessageSerializer;
+import org.apache.tinkerpop.gremlin.driver.Tokens;
import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
import org.apache.tinkerpop.gremlin.driver.ser.MessageTextSerializer;
import org.apache.tinkerpop.gremlin.server.GremlinServer;
import org.apache.tinkerpop.gremlin.server.op.session.Session;
+import org.apache.tinkerpop.gremlin.server.util.ExceptionHelper;
import org.apache.tinkerpop.gremlin.server.util.MetricManager;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
@@ -91,10 +93,10 @@ public class GremlinResponseFrameEncoder extends MessageToMessageEncoder<Respons
} catch (Exception ex) {
errorMeter.mark();
logger.warn("The result [{}] in the request {} could not be serialized and returned.", o.getResult(), o.getRequestId(), ex);
- final String errorMessage = String.format("Error during serialization: %s",
- ex.getCause() != null ? ex.getCause().getMessage() : ex.getMessage());
+ final String errorMessage = String.format("Error during serialization: %s", ExceptionHelper.getMessageFromExceptionOrCause(ex));
final ResponseMessage error = ResponseMessage.build(o.getRequestId())
.statusMessage(errorMessage)
+ .statusAttributeException(ex)
.code(ResponseStatusCode.SERVER_ERROR_SERIALIZATION).create();
if (useBinary) {
objects.add(serializer.serializeResponseAsBinary(error, ctx.alloc()));
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b29bab0a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/HttpGremlinEndpointHandler.java
----------------------------------------------------------------------
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/HttpGremlinEndpointHandler.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/HttpGremlinEndpointHandler.java
index 899d488..a1030cc 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/HttpGremlinEndpointHandler.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/HttpGremlinEndpointHandler.java
@@ -20,6 +20,7 @@ package org.apache.tinkerpop.gremlin.server.handler;
import com.codahale.metrics.Meter;
import com.codahale.metrics.Timer;
+import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.tinkerpop.gremlin.driver.MessageSerializer;
import org.apache.tinkerpop.gremlin.driver.Tokens;
import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
@@ -49,7 +50,6 @@ import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
-import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.tinkerpop.shaded.jackson.databind.JsonNode;
import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper;
import org.apache.tinkerpop.shaded.jackson.databind.node.ArrayNode;
@@ -457,8 +457,13 @@ public class HttpGremlinEndpointHandler extends ChannelInboundHandlerAdapter {
errorMeter.mark();
final ObjectNode node = mapper.createObjectNode();
node.put("message", message);
- if (t.isPresent()){
+ if (t.isPresent()) {
+ // "Exception-Class" needs to go away - didn't realize it was named that way during review for some reason.
+ // replaced with the same method for exception reporting as is used with websocket/nio protocol
node.put("Exception-Class", t.get().getClass().getName());
+ final ArrayNode exceptionList = node.putArray(Tokens.STATUS_ATTRIBUTE_EXCEPTIONS);
+ ExceptionUtils.getThrowableList(t.get()).forEach(throwable -> exceptionList.add(throwable.getClass().getName()));
+ node.put(Tokens.STATUS_ATTRIBUTE_STACK_TRACE, ExceptionUtils.getFullStackTrace(t.get()));
}
final FullHttpResponse response = new DefaultFullHttpResponse(
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b29bab0a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/IteratorHandler.java
----------------------------------------------------------------------
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/IteratorHandler.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/IteratorHandler.java
index 527aa37..78e2dc9 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/IteratorHandler.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/IteratorHandler.java
@@ -110,7 +110,10 @@ public class IteratorHandler extends ChannelOutboundHandlerAdapter {
if (!f.isSuccess()) {
final String errorMessage = String.format("Response iteration and serialization exceeded the configured threshold for request [%s] - %s", msg, f.cause().getMessage());
logger.warn(errorMessage);
- ctx.writeAndFlush(ResponseMessage.build(requestMessage).code(ResponseStatusCode.SERVER_ERROR_TIMEOUT).statusMessage(errorMessage).create());
+ ctx.writeAndFlush(ResponseMessage.build(requestMessage)
+ .code(ResponseStatusCode.SERVER_ERROR_TIMEOUT)
+ .statusAttributeException(f.cause())
+ .statusMessage(errorMessage).create());
}
});
} finally {
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b29bab0a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/NioGremlinResponseEncoder.java
----------------------------------------------------------------------
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/NioGremlinResponseEncoder.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/NioGremlinResponseEncoder.java
index 6a98b8b..79368c1 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/NioGremlinResponseEncoder.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/NioGremlinResponseEncoder.java
@@ -25,6 +25,7 @@ import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
import org.apache.tinkerpop.gremlin.driver.ser.MessageTextSerializer;
import org.apache.tinkerpop.gremlin.server.GremlinServer;
+import org.apache.tinkerpop.gremlin.server.util.ExceptionHelper;
import org.apache.tinkerpop.gremlin.server.util.MetricManager;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
@@ -72,10 +73,10 @@ public class NioGremlinResponseEncoder extends MessageToByteEncoder<ResponseMess
} catch (Exception ex) {
errorMeter.mark();
logger.warn("The result [{}] in the request {} could not be serialized and returned.", responseMessage.getResult(), responseMessage.getRequestId(), ex);
- final String errorMessage = String.format("Error during serialization: %s",
- ex.getCause() != null ? ex.getCause().getMessage() : ex.getMessage());
+ final String errorMessage = String.format("Error during serialization: %s", ExceptionHelper.getMessageFromExceptionOrCause(ex));
final ResponseMessage error = ResponseMessage.build(responseMessage.getRequestId())
.statusMessage(errorMessage)
+ .statusAttributeException(ex)
.code(ResponseStatusCode.SERVER_ERROR_SERIALIZATION).create();
if (useBinary) {
final ByteBuf bytes = serializer.serializeResponseAsBinary(error, ctx.alloc());
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b29bab0a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/OpExecutorHandler.java
----------------------------------------------------------------------
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/OpExecutorHandler.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/OpExecutorHandler.java
index eefe600..477cb11 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/OpExecutorHandler.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/OpExecutorHandler.java
@@ -77,6 +77,7 @@ public class OpExecutorHandler extends SimpleChannelInboundHandler<Pair<RequestM
logger.warn(ex.getMessage(), ex);
ctx.writeAndFlush(ResponseMessage.build(msg)
.code(ResponseStatusCode.SERVER_ERROR)
+ .statusAttributeException(ex)
.statusMessage(ex.getMessage()).create());
} finally {
ReferenceCountUtil.release(objects);
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b29bab0a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/OpSelectorHandler.java
----------------------------------------------------------------------
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/OpSelectorHandler.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/OpSelectorHandler.java
index 432cecd..30055bf 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/OpSelectorHandler.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/OpSelectorHandler.java
@@ -90,7 +90,9 @@ public class OpSelectorHandler extends MessageToMessageDecoder<RequestMessage> {
else {
// invalid op processor selected so write back an error by way of OpProcessorException.
final String errorMessage = String.format("Invalid OpProcessor requested [%s]", msg.getProcessor());
- throw new OpProcessorException(errorMessage, ResponseMessage.build(msg).code(ResponseStatusCode.REQUEST_ERROR_INVALID_REQUEST_ARGUMENTS).statusMessage(errorMessage).create());
+ throw new OpProcessorException(errorMessage, ResponseMessage.build(msg)
+ .code(ResponseStatusCode.REQUEST_ERROR_INVALID_REQUEST_ARGUMENTS)
+ .statusMessage(errorMessage).create());
}
} catch (OpProcessorException ope) {
logger.warn(ope.getMessage(), ope);
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b29bab0a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/WsGremlinResponseEncoder.java
----------------------------------------------------------------------
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/WsGremlinResponseEncoder.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/WsGremlinResponseEncoder.java
index 80565f1..ecd5159 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/WsGremlinResponseEncoder.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/WsGremlinResponseEncoder.java
@@ -25,6 +25,7 @@ import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
import org.apache.tinkerpop.gremlin.driver.ser.MessageTextSerializer;
import org.apache.tinkerpop.gremlin.server.GremlinServer;
import org.apache.tinkerpop.gremlin.server.op.session.Session;
+import org.apache.tinkerpop.gremlin.server.util.ExceptionHelper;
import org.apache.tinkerpop.gremlin.server.util.MetricManager;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
@@ -89,10 +90,10 @@ public class WsGremlinResponseEncoder extends MessageToMessageEncoder<ResponseMe
} catch (Exception ex) {
errorMeter.mark();
logger.warn("The result [{}] in the request {} could not be serialized and returned.", o.getResult(), o.getRequestId(), ex);
- final String errorMessage = String.format("Error during serialization: %s",
- ex.getCause() != null ? ex.getCause().getMessage() : ex.getMessage());
+ final String errorMessage = String.format("Error during serialization: %s", ExceptionHelper.getMessageFromExceptionOrCause(ex));
final ResponseMessage error = ResponseMessage.build(o.getRequestId())
.statusMessage(errorMessage)
+ .statusAttributeException(ex)
.code(ResponseStatusCode.SERVER_ERROR_SERIALIZATION).create();
if (useBinary) {
objects.add(new BinaryWebSocketFrame(serializer.serializeResponseAsBinary(error, ctx.alloc())));
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b29bab0a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractEvalOpProcessor.java
----------------------------------------------------------------------
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractEvalOpProcessor.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractEvalOpProcessor.java
index 1ba6e36..ffbda08 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractEvalOpProcessor.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractEvalOpProcessor.java
@@ -254,20 +254,25 @@ public abstract class AbstractEvalOpProcessor extends AbstractOpProcessor {
// and by default disabled. we can remove this handling once we drop that setting all together
final String errorMessage = String.format("Response iteration exceeded the configured threshold for request [%s] - %s", msg, ex.getMessage());
logger.warn(errorMessage);
- ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_TIMEOUT).statusMessage(errorMessage).create());
+ ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_TIMEOUT)
+ .statusMessage(errorMessage)
+ .statusAttributeException(ex).create());
if (managedTransactionsForRequest) attemptRollback(msg, context.getGraphManager(), settings.strictTransactionManagement);
} catch (InterruptedException ex) {
// interruption occurs if there is a forced timeout during result iteration. this timeout
// is driven by the script evaluation timeout so for consistency the message should be the same
final String errorMessage = String.format("Script evaluation exceeded the configured threshold for request [%s] - %s", msg, ex.getMessage());
logger.warn(errorMessage, ex);
- ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_TIMEOUT).statusMessage(ex.getMessage()).create());
+ ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_TIMEOUT)
+ .statusMessage(ex.getMessage())
+ .statusAttributeException(ex).create());
if (managedTransactionsForRequest) attemptRollback(msg, context.getGraphManager(), settings.strictTransactionManagement);
} catch (Exception ex) {
logger.warn(String.format("Exception processing a script on request [%s].", msg), ex);
final String err = ex.getMessage();
ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR)
- .statusMessage(null == err || err.isEmpty() ? ex.getClass().getSimpleName() : err).create());
+ .statusMessage(null == err || err.isEmpty() ? ex.getClass().getSimpleName() : err)
+ .statusAttributeException(ex).create());
if (managedTransactionsForRequest) attemptRollback(msg, context.getGraphManager(), settings.strictTransactionManagement);
}
}).create();
@@ -284,16 +289,22 @@ public abstract class AbstractEvalOpProcessor extends AbstractOpProcessor {
// occurs when the TimedInterruptCustomizerProvider is in play
final String errorMessage = String.format("A timeout occurred within the script during evaluation of [%s] - consider increasing the limit given to TimedInterruptCustomizerProvider", msg);
logger.warn(errorMessage);
- ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_TIMEOUT).statusMessage("Timeout during script evaluation triggered by TimedInterruptCustomizerProvider").create());
+ ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_TIMEOUT)
+ .statusMessage("Timeout during script evaluation triggered by TimedInterruptCustomizerProvider")
+ .statusAttributeException(t).create());
} else if (t instanceof org.apache.tinkerpop.gremlin.groovy.jsr223.TimedInterruptTimeoutException) {
// occurs when the TimedInterruptCustomizerProvider is in play
final String errorMessage = String.format("A timeout occurred within the script during evaluation of [%s] - consider increasing the limit given to TimedInterruptCustomizerProvider", msg);
logger.warn(errorMessage);
- ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_TIMEOUT).statusMessage("Timeout during script evaluation triggered by TimedInterruptCustomizerProvider").create());
+ ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_TIMEOUT)
+ .statusMessage("Timeout during script evaluation triggered by TimedInterruptCustomizerProvider")
+ .statusAttributeException(t).create());
} else if (t instanceof TimeoutException) {
final String errorMessage = String.format("Script evaluation exceeded the configured threshold for request [%s] - %s", msg, t.getMessage());
logger.warn(errorMessage, t);
- ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_TIMEOUT).statusMessage(t.getMessage()).create());
+ ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_TIMEOUT)
+ .statusMessage(t.getMessage())
+ .statusAttributeException(t).create());
} else {
// try to trap the specific jvm error of "Method code too large!" to re-write it as something nicer,
// but only re-write if it's the only error because otherwise we might lose some other important
@@ -305,10 +316,14 @@ public abstract class AbstractEvalOpProcessor extends AbstractOpProcessor {
((MultipleCompilationErrorsException) t).getErrorCollector().getErrorCount() == 1) {
final String errorMessage = String.format("The Gremlin statement that was submitted exceed the maximum compilation size allowed by the JVM, please split it into multiple smaller statements - %s", msg);
logger.warn(errorMessage);
- ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_SCRIPT_EVALUATION).statusMessage(errorMessage).create());
+ ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_SCRIPT_EVALUATION)
+ .statusMessage(errorMessage)
+ .statusAttributeException(t).create());
} else {
logger.warn(String.format("Exception processing a script on request [%s].", msg), t);
- ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_SCRIPT_EVALUATION).statusMessage(t.getMessage()).create());
+ ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_SCRIPT_EVALUATION)
+ .statusMessage(t.getMessage())
+ .statusAttributeException(t).create());
}
}
}
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b29bab0a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractOpProcessor.java
----------------------------------------------------------------------
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractOpProcessor.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractOpProcessor.java
index c3ac475..8899bb5 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractOpProcessor.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractOpProcessor.java
@@ -32,6 +32,7 @@ import org.apache.tinkerpop.gremlin.server.OpProcessor;
import org.apache.tinkerpop.gremlin.server.Settings;
import org.apache.tinkerpop.gremlin.server.handler.Frame;
import org.apache.tinkerpop.gremlin.server.handler.StateKey;
+import org.apache.tinkerpop.gremlin.server.util.ExceptionHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -277,10 +278,10 @@ public abstract class AbstractOpProcessor implements OpProcessor {
}
} catch (Exception ex) {
logger.warn("The result [{}] in the request {} could not be serialized and returned.", aggregate, msg.getRequestId(), ex);
- final String errorMessage = String.format("Error during serialization: %s",
- ex.getCause() != null ? ex.getCause().getMessage() : ex.getMessage());
+ final String errorMessage = String.format("Error during serialization: %s", ExceptionHelper.getMessageFromExceptionOrCause(ex));
final ResponseMessage error = ResponseMessage.build(msg.getRequestId())
.statusMessage(errorMessage)
+ .statusAttributeException(ex)
.code(ResponseStatusCode.SERVER_ERROR_SERIALIZATION).create();
ctx.writeAndFlush(error);
throw ex;
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b29bab0a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/traversal/TraversalOpProcessor.java
----------------------------------------------------------------------
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/traversal/TraversalOpProcessor.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/traversal/TraversalOpProcessor.java
index c5a05e4..9d908ca 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/traversal/TraversalOpProcessor.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/traversal/TraversalOpProcessor.java
@@ -299,12 +299,16 @@ public class TraversalOpProcessor extends AbstractOpProcessor {
} catch (TimeoutException ex) {
final String errorMessage = String.format("Response iteration exceeded the configured threshold for request [%s] - %s", msg.getRequestId(), ex.getMessage());
logger.warn(errorMessage);
- ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_TIMEOUT).statusMessage(errorMessage).create());
+ ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_TIMEOUT)
+ .statusMessage(errorMessage)
+ .statusAttributeException(ex).create());
onError(graph, context);
return;
} catch (Exception ex) {
logger.warn(String.format("Exception processing a side-effect on iteration for request [%s].", msg.getRequestId()), ex);
- ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR).statusMessage(ex.getMessage()).create());
+ ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR)
+ .statusMessage(ex.getMessage())
+ .statusAttributeException(ex).create());
onError(graph, context);
return;
}
@@ -312,7 +316,9 @@ public class TraversalOpProcessor extends AbstractOpProcessor {
onSideEffectSuccess(graph, context);
} catch (Exception ex) {
logger.warn(String.format("Exception processing a side-effect on request [%s].", msg.getRequestId()), ex);
- ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR).statusMessage(ex.getMessage()).create());
+ ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR)
+ .statusMessage(ex.getMessage())
+ .statusAttributeException(ex).create());
onError(graph, context);
} finally {
timerContext.stop();
@@ -322,7 +328,9 @@ public class TraversalOpProcessor extends AbstractOpProcessor {
} catch (Exception ex) {
timerContext.stop();
throw new OpProcessorException("Could not iterate the side-effect instance",
- ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR).statusMessage(ex.getMessage()).create());
+ ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR)
+ .statusMessage(ex.getMessage())
+ .statusAttributeException(ex).create());
}
}
@@ -357,7 +365,8 @@ public class TraversalOpProcessor extends AbstractOpProcessor {
logger.error("Could not deserialize the Traversal instance", context);
throw new OpProcessorException("Could not deserialize the Traversal instance",
ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_SERIALIZATION)
- .statusMessage(ex.getMessage()).create());
+ .statusMessage(ex.getMessage())
+ .statusAttributeException(ex).create());
}
final Timer.Context timerContext = traversalOpTimer.time();
@@ -376,18 +385,24 @@ public class TraversalOpProcessor extends AbstractOpProcessor {
} catch (TimeoutException ex) {
final String errorMessage = String.format("Response iteration exceeded the configured threshold for request [%s] - %s", msg.getRequestId(), ex.getMessage());
logger.warn(errorMessage);
- ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_TIMEOUT).statusMessage(errorMessage).create());
+ ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_TIMEOUT)
+ .statusMessage(errorMessage)
+ .statusAttributeException(ex).create());
onError(graph, context);
return;
} catch (Exception ex) {
logger.warn(String.format("Exception processing a Traversal on iteration for request [%s].", msg.getRequestId()), ex);
- ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR).statusMessage(ex.getMessage()).create());
+ ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR)
+ .statusMessage(ex.getMessage())
+ .statusAttributeException(ex).create());
onError(graph, context);
return;
}
} catch (Exception ex) {
logger.warn(String.format("Exception processing a Traversal on request [%s].", msg.getRequestId()), ex);
- ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR).statusMessage(ex.getMessage()).create());
+ ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR)
+ .statusMessage(ex.getMessage())
+ .statusAttributeException(ex).create());
onError(graph, context);
} finally {
timerContext.stop();
@@ -397,7 +412,9 @@ public class TraversalOpProcessor extends AbstractOpProcessor {
} catch (Exception ex) {
timerContext.stop();
throw new OpProcessorException("Could not iterate the Traversal instance",
- ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR).statusMessage(ex.getMessage()).create());
+ ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR)
+ .statusMessage(ex.getMessage())
+ .statusAttributeException(ex).create());
}
}
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b29bab0a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/ExceptionHelper.java
----------------------------------------------------------------------
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/ExceptionHelper.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/ExceptionHelper.java
new file mode 100644
index 0000000..49eb8a6
--- /dev/null
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/ExceptionHelper.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.tinkerpop.gremlin.server.util;
+
+/**
+ * Utility class for working with exceptions.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class ExceptionHelper {
+
+ private ExceptionHelper() {}
+
+ public static String getMessageOrName(final Throwable t) {
+ return (null == t.getMessage() || t.getMessage().isEmpty()) ?
+ t.getClass().getName() : t.getMessage();
+ }
+
+ public static String getMessageFromExceptionOrCause(final Throwable t) {
+ return null == t.getCause() ? getMessageOrName(t) : getMessageOrName(t.getCause());
+ }
+}
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b29bab0a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
----------------------------------------------------------------------
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
index a8164b5..9eabf15 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
@@ -363,6 +363,11 @@ public class GremlinDriverIntegrateTest extends AbstractGremlinServerIntegration
final Throwable inner = ExceptionUtils.getRootCause(ex);
assertTrue(inner instanceof ResponseException);
assertThat(inner.getMessage(), endsWith("Division by zero"));
+
+ final ResponseException rex = (ResponseException) inner;
+ assertEquals("java.lang.ArithmeticException", rex.getRemoteExceptionHierarchy().get().get(0));
+ assertEquals(1, rex.getRemoteExceptionHierarchy().get().size());
+ assertThat(rex.getRemoteStackTrace().get(), startsWith("java.lang.ArithmeticException: Division by zero\n\tat java.math.BigDecimal.divide(BigDecimal.java:1742)\n\tat org.codehaus.groovy.runtime.typehandling.BigDecimalMath.divideImpl(BigDecimalMath.java:68)\n\tat org.codehaus.groovy.runtime.typehandling.IntegerMath.divideImpl(IntegerMath.java:49)\n\tat org.codehaus.groovy.runtime.dgmimpl.NumberNumberDiv$NumberNumber.invoke(NumberNumberDiv.java:323)\n\tat org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:56)\n\tat org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)\n\tat org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)\n\tat org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)\n"));
}
// should not die completely just because we had a bad serialization error. that kind of stuff happens
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b29bab0a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerHttpIntegrateTest.java
----------------------------------------------------------------------
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerHttpIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerHttpIntegrateTest.java
index 78109e6..b1cbd7f 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerHttpIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerHttpIntegrateTest.java
@@ -18,6 +18,7 @@
*/
package org.apache.tinkerpop.gremlin.server;
+import org.apache.tinkerpop.gremlin.driver.Tokens;
import org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV2d0;
import org.apache.tinkerpop.gremlin.server.auth.SimpleAuthenticator;
import org.apache.tinkerpop.gremlin.server.channel.HttpChannelizer;
@@ -42,6 +43,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.StringStartsWith.startsWith;
import static org.junit.Assert.assertEquals;
/**
@@ -799,6 +802,11 @@ public class GremlinServerHttpIntegrateTest extends AbstractGremlinServerIntegra
try (final CloseableHttpResponse response = httpclient.execute(httppost)) {
assertEquals(500, response.getStatusLine().getStatusCode());
+ final String json = EntityUtils.toString(response.getEntity());
+ final JsonNode node = mapper.readTree(json);
+ assertEquals("java.lang.ArithmeticException", node.get(Tokens.STATUS_ATTRIBUTE_EXCEPTIONS).get(0).asText());
+ assertEquals(1, node.get(Tokens.STATUS_ATTRIBUTE_EXCEPTIONS).size());
+ assertThat(node.get(Tokens.STATUS_ATTRIBUTE_STACK_TRACE).asText(), startsWith("java.lang.ArithmeticException: Division by zero\n\tat java.math.BigDecimal.divide(BigDecimal.java:1742)\n\tat org.codehaus.groovy.runtime.typehandling.BigDecimalMath.divideImpl(BigDecimalMath.java:68)\n\tat org.codehaus.groovy.runtime.typehandling.IntegerMath.divideImpl(IntegerMath.java:49)\n\tat org.codehaus.groovy.runtime.dgmimpl.NumberNumberDiv$NumberNumber.invoke(NumberNumberDiv.java:323)\n\tat org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:56)\n\tat org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)\n\tat org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)\n\tat org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)\n"));
}
}