Merge branch 'TINKERPOP-2947' into 3.6-dev
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index fd2f856..7437037 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -22,9 +22,11 @@
[[release-3-6-5]]
=== TinkerPop 3.6.5 (Release Date: NOT OFFICIALLY RELEASED YET)
+
This release also includes changes from <<release-3-5-7, 3.5.7>>.
-
+* Added `text/plain` MIME type to the HTTP endpoint to return a Gremlin Console-like representation of the data.
+* Added GraphBinary serialization option to the HTTP endpoint.
[[release-3-6-4]]
=== TinkerPop 3.6.4 (Release Date: May 12, 2023)
diff --git a/docs/src/reference/gremlin-applications.asciidoc b/docs/src/reference/gremlin-applications.asciidoc
index 8db0b6b..2ee5c21 100644
--- a/docs/src/reference/gremlin-applications.asciidoc
+++ b/docs/src/reference/gremlin-applications.asciidoc
@@ -901,6 +901,21 @@
types are preserved or to pass complex objects such as lists or maps, use `POST` which will at least support the
allowed JSON data types.
+Passing the `Accept` header with a valid MIME type will trigger the server to return the result in a particular format.
+Note that in addition to the formats available given the server's `serializers` configuration, there is also a basic
+`text/plain` format which produces a text representation of results similar to the Gremlin Console:
+
+[source,text]
+----
+$ curl -H "Accept:text/plain" -X POST -d "{\"gremlin\":\"g.V()\"}" "http://localhost:8182"
+==>v[1]
+==>v[2]
+==>v[3]
+==>v[4]
+==>v[5]
+==>v[6]
+----
+
Finally, as Gremlin Server can host multiple `ScriptEngine` instances (e.g. `gremlin-groovy`, `nashorn`), it is
possible to define the language to utilize to process the request:
diff --git a/docs/src/upgrade/release-3.6.x.asciidoc b/docs/src/upgrade/release-3.6.x.asciidoc
index e416aa2..d54ad7a 100644
--- a/docs/src/upgrade/release-3.6.x.asciidoc
+++ b/docs/src/upgrade/release-3.6.x.asciidoc
@@ -29,7 +29,24 @@
=== Upgrading for Users
+==== HTTP Plain Text
+A `text/plain` MIME type has been added to the HTTP endpoint to return Gremlin Console formatted results in plain text.
+This format can be helpful for a variety of reasons. Reading JSON formatted results can be difficult sometimes and
+`text/plain` is a more simple, readable representation for when that is helpful.
+
+[source,text]
+----
+$ curl -H "Accept:text/plain" -X POST -d "{\"gremlin\":\"g.V()\"}" "http://localhost:8182"
+==>v[1]
+==>v[2]
+==>v[3]
+==>v[4]
+==>v[5]
+==>v[6]
+----
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-2947[TINKERPOP-2947]
== TinkerPop 3.6.4
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketGremlinRequestEncoder.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketGremlinRequestEncoder.java
index 92dedab..8bb8173 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketGremlinRequestEncoder.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketGremlinRequestEncoder.java
@@ -54,7 +54,7 @@
objects.add(new BinaryWebSocketFrame(encodedMessage));
} else {
final MessageTextSerializer<?> textSerializer = (MessageTextSerializer<?>) serializer;
- objects.add(new TextWebSocketFrame(textSerializer.serializeRequestAsString(requestMessage)));
+ objects.add(new TextWebSocketFrame(textSerializer.serializeRequestAsString(requestMessage, channelHandlerContext.alloc())));
}
} catch (Exception ex) {
throw new ResponseException(ResponseStatusCode.REQUEST_ERROR_SERIALIZATION, String.format(
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphBinaryMessageSerializerV1.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphBinaryMessageSerializerV1.java
index 2eea896..1c7a69a 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphBinaryMessageSerializerV1.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphBinaryMessageSerializerV1.java
@@ -20,6 +20,7 @@
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
+import io.netty.buffer.Unpooled;
import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryIo;
@@ -36,6 +37,7 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
+import java.util.Base64;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -43,7 +45,7 @@
import static java.nio.charset.StandardCharsets.UTF_8;
-public class GraphBinaryMessageSerializerV1 extends AbstractMessageSerializer<GraphBinaryMapper> {
+public class GraphBinaryMessageSerializerV1 extends AbstractMessageSerializer<GraphBinaryMapper> implements MessageTextSerializer<GraphBinaryMapper> {
public static final String TOKEN_CUSTOM = "custom";
public static final String TOKEN_BUILDER = "builder";
@@ -52,6 +54,9 @@
private static final String MIME_TYPE = SerTokens.MIME_GRAPHBINARY_V1D0;
private static final String MIME_TYPE_STRINGD = SerTokens.MIME_GRAPHBINARY_V1D0 + "-stringd";
+ private static final Base64.Encoder base64Encoder = Base64.getEncoder();
+ private static final Base64.Decoder base64Decoder = Base64.getDecoder();
+
private byte[] header = MIME_TYPE.getBytes(UTF_8);
private boolean serializeToString = false;
private GraphBinaryReader reader;
@@ -185,6 +190,41 @@
return new String[]{serializeToString ? MIME_TYPE_STRINGD : MIME_TYPE};
}
+ @Override
+ public String serializeResponseAsString(final ResponseMessage responseMessage, final ByteBufAllocator allocator) throws SerializationException {
+ final ByteBuf bb = serializeResponseAsBinary(responseMessage, allocator);
+ return base64Encoder.encodeToString(convertToBytes(bb));
+ }
+
+ @Override
+ public String serializeRequestAsString(final RequestMessage requestMessage, final ByteBufAllocator allocator) throws SerializationException {
+ final ByteBuf bb = serializeRequestAsBinary(requestMessage, allocator);
+ return base64Encoder.encodeToString(convertToBytes(bb));
+ }
+
+ @Override
+ public RequestMessage deserializeRequest(final String msg) throws SerializationException {
+ return deserializeRequest(convertToByteBuf(msg));
+ }
+
+ @Override
+ public ResponseMessage deserializeResponse(final String msg) throws SerializationException {
+ return deserializeResponse(convertToByteBuf(msg));
+ }
+
+ private byte[] convertToBytes(final ByteBuf bb) {
+ byte[] bytes = new byte[bb.readableBytes()];
+ bb.getBytes(bb.readerIndex(), bytes);
+ return bytes;
+ }
+
+ private ByteBuf convertToByteBuf(final String msg) {
+ final byte[] b = base64Decoder.decode(msg);
+ final ByteBuf bb = Unpooled.buffer(b.length);
+ bb.writeBytes(b);
+ return bb;
+ }
+
private void addCustomClasses(final Map<String, Object> config, final TypeSerializerRegistry.Builder builder) {
final List<String> classNameList = getListStringFromConfig(TOKEN_CUSTOM, config);
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV1d0.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV1d0.java
index d3a15f5..60a2486 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV1d0.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV1d0.java
@@ -18,6 +18,7 @@
*/
package org.apache.tinkerpop.gremlin.driver.ser;
+import io.netty.buffer.ByteBufAllocator;
import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
@@ -95,7 +96,7 @@
}
@Override
- public String serializeResponseAsString(final ResponseMessage responseMessage) throws SerializationException {
+ public String serializeResponseAsString(final ResponseMessage responseMessage, final ByteBufAllocator allocator) throws SerializationException {
try {
return mapper.writeValueAsString(responseMessage);
} catch (Exception ex) {
@@ -115,7 +116,7 @@
}
@Override
- public String serializeRequestAsString(final RequestMessage requestMessage) throws SerializationException {
+ public String serializeRequestAsString(final RequestMessage requestMessage, final ByteBufAllocator allocator) throws SerializationException {
try {
return mapper.writeValueAsString(requestMessage);
} catch (Exception ex) {
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV2d0.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV2d0.java
index 6717a00..3442205 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV2d0.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV2d0.java
@@ -18,6 +18,7 @@
*/
package org.apache.tinkerpop.gremlin.driver.ser;
+import io.netty.buffer.ByteBufAllocator;
import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
@@ -110,7 +111,7 @@
}
@Override
- public String serializeResponseAsString(final ResponseMessage responseMessage) throws SerializationException {
+ public String serializeResponseAsString(final ResponseMessage responseMessage, final ByteBufAllocator allocator) throws SerializationException {
try {
return mapper.writeValueAsString(responseMessage);
} catch (Exception ex) {
@@ -130,7 +131,7 @@
}
@Override
- public String serializeRequestAsString(final RequestMessage requestMessage) throws SerializationException {
+ public String serializeRequestAsString(final RequestMessage requestMessage, final ByteBufAllocator allocator) throws SerializationException {
try {
return mapper.writeValueAsString(requestMessage);
} catch (Exception ex) {
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV3d0.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV3d0.java
index da3eea7..5aebf77 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV3d0.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV3d0.java
@@ -18,6 +18,7 @@
*/
package org.apache.tinkerpop.gremlin.driver.ser;
+import io.netty.buffer.ByteBufAllocator;
import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper;
@@ -106,7 +107,7 @@
}
@Override
- public String serializeResponseAsString(final ResponseMessage responseMessage) throws SerializationException {
+ public String serializeResponseAsString(final ResponseMessage responseMessage, final ByteBufAllocator allocator) throws SerializationException {
try {
return mapper.writeValueAsString(responseMessage);
} catch (Exception ex) {
@@ -126,7 +127,7 @@
}
@Override
- public String serializeRequestAsString(final RequestMessage requestMessage) throws SerializationException {
+ public String serializeRequestAsString(final RequestMessage requestMessage, final ByteBufAllocator allocator) throws SerializationException {
try {
return mapper.writeValueAsString(requestMessage);
} catch (Exception ex) {
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/MessageTextSerializer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/MessageTextSerializer.java
index 289d56d..6ef0885 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/MessageTextSerializer.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/MessageTextSerializer.java
@@ -18,6 +18,7 @@
*/
package org.apache.tinkerpop.gremlin.driver.ser;
+import io.netty.buffer.ByteBufAllocator;
import org.apache.tinkerpop.gremlin.driver.MessageSerializer;
import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
@@ -31,9 +32,9 @@
* @author Stephen Mallette (http://stephen.genoprime.com)
*/
public interface MessageTextSerializer<M> extends MessageSerializer<M> {
- public String serializeResponseAsString(final ResponseMessage responseMessage) throws SerializationException;
+ public String serializeResponseAsString(final ResponseMessage responseMessage, final ByteBufAllocator allocator) throws SerializationException;
- public String serializeRequestAsString(final RequestMessage requestMessage) throws SerializationException;
+ public String serializeRequestAsString(final RequestMessage requestMessage, final ByteBufAllocator allocator) throws SerializationException;
public RequestMessage deserializeRequest(final String msg) throws SerializationException;
diff --git a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/TestWSGremlinInitializer.java b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/TestWSGremlinInitializer.java
index b9e9b52..eda187c 100644
--- a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/TestWSGremlinInitializer.java
+++ b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/TestWSGremlinInitializer.java
@@ -18,6 +18,8 @@
*/
package org.apache.tinkerpop.gremlin.driver;
+import io.netty.buffer.ByteBufAllocator;
+import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import org.slf4j.Logger;
@@ -82,6 +84,7 @@
* Gremlin serializer used for serializing/deserializing the request/response. This should be same as client.
*/
private static final GraphSONMessageSerializerV2d0 SERIALIZER = new GraphSONMessageSerializerV2d0();
+ private final static ByteBufAllocator allocator = UnpooledByteBufAllocator.DEFAULT;
@Override
public void postInit(ChannelPipeline pipeline) {
@@ -128,7 +131,7 @@
final ResponseMessage responseMessage = ResponseMessage.build(msg)
.code(ResponseStatusCode.SERVER_ERROR)
.statusAttributeException(new RuntimeException()).create();
- ctx.channel().writeAndFlush(new TextWebSocketFrame(SERIALIZER.serializeResponseAsString(responseMessage)));
+ ctx.channel().writeAndFlush(new TextWebSocketFrame(SERIALIZER.serializeResponseAsString(responseMessage, allocator)));
} else if (msg.getRequestId().equals(CLOSE_CONNECTION_REQUEST_ID)) {
Thread.sleep(1000);
ctx.channel().writeAndFlush(new CloseWebSocketFrame());
@@ -152,7 +155,7 @@
final GraphTraversalSource g = graph.traversal();
final Vertex t = g.V().limit(1).next();
- return SERIALIZER.serializeResponseAsString(ResponseMessage.build(requestID).result(t).create());
+ return SERIALIZER.serializeResponseAsString(ResponseMessage.build(requestID).result(t).create(), allocator);
}
/**
@@ -160,7 +163,7 @@
* @throws SerializationException
*/
private String returnSimpleStringResponse(final UUID requestID, String message) throws SerializationException {
- return SERIALIZER.serializeResponseAsString(ResponseMessage.build(requestID).result(message).create());
+ return SERIALIZER.serializeResponseAsString(ResponseMessage.build(requestID).result(message).create(), allocator);
}
/**
diff --git a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV1d0Test.java b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV1d0Test.java
index 4c4b7a2..001ce7e 100644
--- a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV1d0Test.java
+++ b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV1d0Test.java
@@ -18,6 +18,7 @@
*/
package org.apache.tinkerpop.gremlin.driver.ser;
+import io.netty.buffer.ByteBufAllocator;
import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
@@ -74,6 +75,7 @@
private static final RequestMessage msg = RequestMessage.build("op")
.overrideRequestId(UUID.fromString("2D62161B-9544-4F39-AF44-62EC49F9A595")).create();
private static final ObjectMapper mapper = new ObjectMapper();
+ private static final ByteBufAllocator allocator = ByteBufAllocator.DEFAULT;
@Test
public void shouldConfigureIoRegistry() throws Exception {
@@ -86,7 +88,7 @@
final ResponseMessage toSerialize = ResponseMessage.build(UUID.fromString("2D62161B-9544-4F39-AF44-62EC49F9A595"))
.result(Color.RED).create();
- final String results = serializer.serializeResponseAsString(toSerialize);
+ final String results = serializer.serializeResponseAsString(toSerialize, allocator);
final JsonNode json = mapper.readTree(results);
assertNotNull(json);
assertThat(json.get(SerTokens.TOKEN_RESULT).get(SerTokens.TOKEN_DATA).booleanValue(), is(true));
@@ -95,7 +97,7 @@
@Test
public void shouldSerializeToJsonNullResultReturnsNull() throws Exception {
final ResponseMessage message = ResponseMessage.build(msg).create();
- final String results = SERIALIZER.serializeResponseAsString(message);
+ final String results = SERIALIZER.serializeResponseAsString(message, allocator);
final JsonNode json = mapper.readTree(results);
assertNotNull(json);
assertEquals(msg.getRequestId().toString(), json.path(SerTokens.TOKEN_REQUEST).asText());
@@ -108,7 +110,7 @@
funList.add(new FunObject("x"));
funList.add(new FunObject("y"));
- final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList).create());
+ final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList).create(), allocator);
final JsonNode json = mapper.readTree(results);
assertNotNull(json);
@@ -127,7 +129,7 @@
funList.add(new FunObject("x"));
funList.add(new FunObject("y"));
- final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList.iterator()).create());
+ final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList.iterator()).create(), allocator);
final JsonNode json = mapper.readTree(results);
assertNotNull(json);
@@ -148,7 +150,7 @@
funList.add(null);
funList.add(new FunObject("y"));
- final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList.iterator()).create());
+ final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList.iterator()).create(), allocator);
final JsonNode json = mapper.readTree(results);
assertNotNull(json);
@@ -172,7 +174,7 @@
map.put("y", "some");
map.put("z", innerMap);
- final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(map).create());
+ final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(map).create(), allocator);
final JsonNode json = mapper.readTree(results);
assertNotNull(json);
@@ -199,7 +201,7 @@
map.put(v1, 100);
map.put(d, "test");
- final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(IteratorUtils.asList(map)).create());
+ final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(IteratorUtils.asList(map)).create(), allocator);
final JsonNode json = mapper.readTree(results);
assertNotNull(json);
@@ -226,7 +228,7 @@
e.property("abc", 123);
final Iterable<Edge> iterable = IteratorUtils.list(g.edges());
- final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create());
+ final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create(), allocator);
final JsonNode json = mapper.readTree(results);
@@ -260,7 +262,7 @@
e.property("abc", 123);
final Iterable<Property<Object>> iterable = IteratorUtils.list(e.properties("abc"));
- final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create());
+ final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create(), allocator);
final JsonNode json = mapper.readTree(results);
@@ -293,7 +295,7 @@
v.property(VertexProperty.Cardinality.single, "friends", friends);
final Iterable iterable = IteratorUtils.list(g.vertices());
- final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create());
+ final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create(), allocator);
final JsonNode json = mapper.readTree(results);
assertNotNull(json);
@@ -334,7 +336,7 @@
final Map<Vertex, Integer> map = new HashMap<>();
map.put(g.V().has("name", "marko").next(), 1000);
- final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(map).create());
+ final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(map).create(), allocator);
final JsonNode json = mapper.readTree(results);
assertNotNull(json);
@@ -393,7 +395,7 @@
.statusMessage("worked")
.create();
- final String results = SERIALIZER.serializeResponseAsString(response);
+ final String results = SERIALIZER.serializeResponseAsString(response, allocator);
final ResponseMessage deserialized = SERIALIZER.deserializeResponse(results);
assertEquals(id, deserialized.getRequestId());
@@ -423,7 +425,7 @@
.statusMessage(null)
.create();
- final String results = SERIALIZER.serializeResponseAsString(response);
+ final String results = SERIALIZER.serializeResponseAsString(response, allocator);
final ResponseMessage deserialized = SERIALIZER.deserializeResponse(results);
Assert.assertNotNull(SERIALIZER.getClass().getSimpleName() + " should be able to deserialize ResponseMessage "
+ "with null message field", deserialized);
@@ -436,7 +438,7 @@
final Tree t = g.V(1).out().properties("name").tree().next();
- final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(t).create());
+ final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(t).create(), allocator);
final JsonNode json = mapper.readTree(results);
diff --git a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV2d0Test.java b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV2d0Test.java
index a497400..3d01af4 100644
--- a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV2d0Test.java
+++ b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV2d0Test.java
@@ -104,7 +104,7 @@
final ResponseMessage toSerialize = ResponseMessage.build(UUID.fromString("2D62161B-9544-4F39-AF44-62EC49F9A595"))
.result(Color.RED).create();
- final String results = serializer.serializeResponseAsString(toSerialize);
+ final String results = serializer.serializeResponseAsString(toSerialize, allocator);
final JsonNode json = mapper.readTree(results);
assertNotNull(json);
assertThat(json.get(SerTokens.TOKEN_RESULT).get(SerTokens.TOKEN_DATA).booleanValue(), is(true));
@@ -113,7 +113,7 @@
@Test
public void shouldSerializeToJsonNullResultReturnsNull() throws Exception {
final ResponseMessage message = ResponseMessage.build(msg).create();
- final String results = SERIALIZER.serializeResponseAsString(message);
+ final String results = SERIALIZER.serializeResponseAsString(message, allocator);
final JsonNode json = mapper.readTree(results);
assertNotNull(json);
assertEquals(msg.getRequestId().toString(), json.path(SerTokens.TOKEN_REQUEST).asText());
@@ -126,7 +126,7 @@
funList.add(new FunObject("x"));
funList.add(new FunObject("y"));
- final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList).create());
+ final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList).create(), allocator);
final JsonNode json = mapper.readTree(results);
assertNotNull(json);
@@ -145,7 +145,7 @@
funList.add(new FunObject("x"));
funList.add(new FunObject("y"));
- final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList).create());
+ final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList).create(), allocator);
final JsonNode json = mapper.readTree(results);
assertNotNull(json);
@@ -166,7 +166,7 @@
funList.add(null);
funList.add(new FunObject("y"));
- final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList.iterator()).create());
+ final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList.iterator()).create(), allocator);
final JsonNode json = mapper.readTree(results);
assertNotNull(json);
@@ -190,7 +190,7 @@
map.put("y", "some");
map.put("z", innerMap);
- final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(map).create());
+ final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(map).create(), allocator);
final JsonNode json = mapper.readTree(results);
assertNotNull(json);
@@ -217,7 +217,7 @@
map.put(v1, 100);
map.put(d, "test");
- final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(IteratorUtils.asList(map)).create());
+ final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(IteratorUtils.asList(map)).create(), allocator);
final JsonNode json = mapper.readTree(results);
assertNotNull(json);
@@ -244,7 +244,7 @@
e.property("abc", 123);
final Iterable<Edge> iterable = IteratorUtils.list(g.edges());
- final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create());
+ final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create(), allocator);
final JsonNode json = mapper.readTree(results);
@@ -277,7 +277,7 @@
e.property("abc", 123);
final Iterable<Property<Object>> iterable = IteratorUtils.list(e.properties("abc"));
- final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create());
+ final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create(), allocator);
final JsonNode json = mapper.readTree(results);
@@ -310,7 +310,7 @@
v.property(VertexProperty.Cardinality.single, "friends", friends);
final Iterable iterable = IteratorUtils.list(g.vertices());
- final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create());
+ final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create(), allocator);
final JsonNode json = mapper.readTree(results);
assertNotNull(json);
@@ -351,7 +351,7 @@
final Map<Vertex, Integer> map = new HashMap<>();
map.put(g.V().has("name", "marko").next(), 1000);
- final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(map).create());
+ final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(map).create(), allocator);
final JsonNode json = mapper.readTree(results);
assertNotNull(json);
@@ -410,7 +410,7 @@
.statusMessage("worked")
.create();
- final String results = SERIALIZER.serializeResponseAsString(response);
+ final String results = SERIALIZER.serializeResponseAsString(response, allocator);
final ResponseMessage deserialized = SERIALIZER.deserializeResponse(results);
assertEquals(id, deserialized.getRequestId());
@@ -429,7 +429,7 @@
final GraphTraversalSource g = graph.traversal();
final Tree t = g.V(1).out().properties("name").tree().next();
- final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(t).create());
+ final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(t).create(), allocator);
final JsonNode json = mapper.readTree(results);
diff --git a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryMessageSerializerV1Test.java b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryMessageSerializerV1Test.java
index d77de48..6cd37b8 100644
--- a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryMessageSerializerV1Test.java
+++ b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryMessageSerializerV1Test.java
@@ -175,6 +175,43 @@
assertEquals(java.awt.Color.RED.toString(), deserialized.getResult().getData());
}
+ @Test
+ public void shouldToStringSerializeAsText() throws SerializationException {
+ final GraphBinaryMessageSerializerV1 serializer = new GraphBinaryMessageSerializerV1();
+ final Map<String,Object> conf = new HashMap<String,Object>() {{
+ put(GraphBinaryMessageSerializerV1.TOKEN_SERIALIZE_RESULT_TO_STRING, true);
+ }};
+ serializer.configure(conf, Collections.emptyMap());
+
+ final ResponseMessage messageWithUnexpectedType = ResponseMessage.build(UUID.randomUUID()).
+ result(java.awt.Color.RED).create();
+ final String base64 = serializer.serializeResponseAsString(messageWithUnexpectedType, allocator);
+ final ResponseMessage deserialized = serializer.deserializeResponse(base64);
+
+ assertEquals(java.awt.Color.RED.toString(), deserialized.getResult().getData());
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeRequestAsText() throws SerializationException {
+ final GraphBinaryMessageSerializerV1 serializer = new GraphBinaryMessageSerializerV1();
+ final Map<String,Object> conf = new HashMap<String,Object>() {{
+ put(GraphBinaryMessageSerializerV1.TOKEN_SERIALIZE_RESULT_TO_STRING, true);
+ }};
+ serializer.configure(conf, Collections.emptyMap());
+
+ final RequestMessage request = RequestMessage.build("op1")
+ .processor("proc1")
+ .overrideRequestId(UUID.randomUUID())
+ .addArg("arg1", "value1")
+ .create();
+
+ final ByteBuf buffer = serializer.serializeRequestAsBinary(request, allocator);
+ final int mimeLen = buffer.readByte();
+ buffer.readBytes(new byte[mimeLen]);
+ final RequestMessage deserialized = serializer.deserializeRequest(buffer);
+ assertThat(request, reflectionEquals(deserialized));
+ }
+
private static void assertResponseEquals(ResponseMessage expected, ResponseMessage actual) {
assertEquals(expected.getRequestId(), actual.getRequestId());
// Status
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/AbstractSession.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/AbstractSession.java
index d891e50..4b137f3 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/AbstractSession.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/AbstractSession.java
@@ -751,7 +751,7 @@
.code(code)
.statusAttributes(statusAttributes)
.responseMetaData(responseMetaData)
- .result(aggregate).create()));
+ .result(aggregate).create(), nettyContext.alloc()));
}
} catch (Exception ex) {
logger.warn("The result [{}] in the request {} could not be serialized and returned.", aggregate, msg.getRequestId(), ex);
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 6020b36..54fe16b 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
@@ -84,9 +84,9 @@
// if the request came in on a session then the serialization must occur that same thread except
// in the case of errors for reasons described above.
if (null == session || !o.getStatus().getCode().isSuccess())
- serialized = new Frame(textSerializer.serializeResponseAsString(o));
+ serialized = new Frame(textSerializer.serializeResponseAsString(o, ctx.alloc()));
else
- serialized = new Frame(session.getExecutor().submit(() -> textSerializer.serializeResponseAsString(o)).get());
+ serialized = new Frame(session.getExecutor().submit(() -> textSerializer.serializeResponseAsString(o, ctx.alloc())).get());
objects.add(serialized);
}
@@ -102,7 +102,7 @@
objects.add(serializer.serializeResponseAsBinary(error, ctx.alloc()));
} else {
final MessageTextSerializer<?> textSerializer = (MessageTextSerializer<?>) serializer;
- objects.add(textSerializer.serializeResponseAsString(error));
+ objects.add(textSerializer.serializeResponseAsString(error, ctx.alloc()));
}
}
}
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 634d88f..6995b21 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
@@ -19,7 +19,10 @@
package org.apache.tinkerpop.gremlin.server.handler;
import com.codahale.metrics.Timer;
+import io.netty.buffer.ByteBufAllocator;
+import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
import org.apache.tinkerpop.gremlin.driver.ser.SerializationException;
+import org.apache.tinkerpop.gremlin.server.util.TextPlainMessageSerializer;
import org.javatuples.Pair;
import org.javatuples.Quartet;
import org.slf4j.Logger;
@@ -60,10 +63,12 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -99,6 +104,11 @@
*/
private final Map<String, MessageSerializer<?>> serializers;
+ /**
+ * Serializer for {@code text/plain} which is a serializer exclusive to HTTP.
+ */
+ private static final TextPlainMessageSerializer textPlainSerializer = new TextPlainMessageSerializer();
+
private final GremlinExecutor gremlinExecutor;
private final GraphManager graphManager;
private final Settings settings;
@@ -223,7 +233,7 @@
attemptCommit(requestArguments.getValue3(), graphManager, settings.strictTransactionManagement);
try {
- return Unpooled.wrappedBuffer(serializer.getValue1().serializeResponseAsString(responseMessage).getBytes(UTF8));
+ return Unpooled.wrappedBuffer(serializer.getValue1().serializeResponseAsString(responseMessage, ctx.alloc()).getBytes(UTF8));
} catch (Exception ex) {
logger.warn(String.format("Error during serialization for %s", responseMessage), ex);
@@ -331,6 +341,9 @@
final String accept = p.getValue0().equals("*/*") ? "application/json" : p.getValue0();
if (serializers.containsKey(accept))
return Pair.with(accept, (MessageTextSerializer<?>) serializers.get(accept));
+ else if (accept.equals("text/plain")) {
+ return Pair.with(accept, textPlainSerializer);
+ }
}
return null;
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 383d074..a0c3a37 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
@@ -297,7 +297,7 @@
.code(code)
.statusAttributes(statusAttributes)
.responseMetaData(responseMetaData)
- .result(aggregate).create()));
+ .result(aggregate).create(), nettyContext.alloc()));
}
} catch (Exception ex) {
logger.warn("The result [{}] in the request {} could not be serialized and returned.", aggregate, msg.getRequestId(), ex);
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/TextPlainMessageSerializer.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/TextPlainMessageSerializer.java
new file mode 100644
index 0000000..81b3662
--- /dev/null
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/TextPlainMessageSerializer.java
@@ -0,0 +1,97 @@
+/*
+ * 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;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufAllocator;
+import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
+import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
+import org.apache.tinkerpop.gremlin.driver.ser.MessageTextSerializer;
+import org.apache.tinkerpop.gremlin.driver.ser.SerializationException;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Function;
+
+/**
+ * A highly use-case specific serializer that only has context for HTTP where results simply need to be converted
+ * to string in a line by line fashion for text based returns.
+ */
+public class TextPlainMessageSerializer implements MessageTextSerializer<Function<Object, String>> {
+
+ @Override
+ public Function<Object, String> getMapper() {
+ return Objects::toString;
+ }
+
+ @Override
+ public ByteBuf serializeResponseAsBinary(final ResponseMessage responseMessage, final ByteBufAllocator allocator) throws SerializationException {
+ throw new UnsupportedOperationException("text/plain does not produce binary");
+ }
+
+ @Override
+ public ByteBuf serializeRequestAsBinary(final RequestMessage requestMessage, final ByteBufAllocator allocator) throws SerializationException {
+ throw new UnsupportedOperationException("text/plain does not produce binary");
+ }
+
+ @Override
+ public RequestMessage deserializeRequest(final ByteBuf msg) throws SerializationException {
+ throw new UnsupportedOperationException("text/plain does not have deserialization functions");
+ }
+
+ @Override
+ public ResponseMessage deserializeResponse(final ByteBuf msg) throws SerializationException {
+ throw new UnsupportedOperationException("text/plain does not have deserialization functions");
+ }
+
+ @Override
+ public String[] mimeTypesSupported() {
+ return new String[] { "text/plain" };
+ }
+
+ @Override
+ public String serializeResponseAsString(final ResponseMessage responseMessage, final ByteBufAllocator allocator) throws SerializationException {
+ final StringBuilder sb = new StringBuilder();
+
+ // this should only serialize success conditions so all should have data in List form
+ final List<Object> data = (List<Object>) responseMessage.getResult().getData();
+ for (int ix = 0; ix < data.size(); ix ++) {
+ sb.append("==>");
+ sb.append(data.get(ix));
+ if (ix < data.size() - 1)
+ sb.append(System.lineSeparator());
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public String serializeRequestAsString(final RequestMessage requestMessage, final ByteBufAllocator allocator) throws SerializationException {
+ throw new UnsupportedOperationException("text/plain does not have any need to serialize requests");
+ }
+
+ @Override
+ public RequestMessage deserializeRequest(final String msg) throws SerializationException {
+ throw new UnsupportedOperationException("text/plain does not have deserialization functions");
+ }
+
+ @Override
+ public ResponseMessage deserializeResponse(final String msg) throws SerializationException {
+ throw new UnsupportedOperationException("text/plain does not have deserialization functions");
+ }
+}
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 7a33bb0..4f44158 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
@@ -20,6 +20,8 @@
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.http.HttpHeaders;
+import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
+import org.apache.tinkerpop.gremlin.driver.ser.GraphBinaryMessageSerializerV1;
import org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV1d0;
import org.apache.tinkerpop.gremlin.driver.Tokens;
import org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV2d0;
@@ -36,15 +38,17 @@
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.tinkerpop.gremlin.server.handler.SaslAndHttpBasicAuthenticationHandler;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.io.binary.TypeSerializerRegistry;
import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONTokens;
import org.apache.tinkerpop.shaded.jackson.databind.JsonNode;
import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper;
import org.junit.Test;
-import java.io.File;
import java.time.Instant;
import java.util.Base64;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
@@ -54,7 +58,9 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.hamcrest.core.StringContains.containsString;
+import static org.hamcrest.core.StringRegularExpression.matchesRegex;
import static org.hamcrest.core.StringStartsWith.startsWith;
import static org.junit.Assert.assertEquals;
@@ -366,6 +372,68 @@
}
@Test
+ public void should200OnGETWithGremlinQueryStringArgumentWithIteratorResultGraphBinary() throws Exception {
+ final CloseableHttpClient httpclient = HttpClients.createDefault();
+ final HttpGet httpget = new HttpGet(TestClientFactory.createURLString("?gremlin=gclassic.V()"));
+ final String mime = SerTokens.MIME_GRAPHBINARY_V1D0;
+ httpget.addHeader("Accept", mime);
+
+ try (final CloseableHttpResponse response = httpclient.execute(httpget)) {
+ assertEquals(200, response.getStatusLine().getStatusCode());
+ assertEquals(mime, response.getEntity().getContentType().getValue());
+ final String base64 = EntityUtils.toString(response.getEntity());
+ final GraphBinaryMessageSerializerV1 serializer = new GraphBinaryMessageSerializerV1(TypeSerializerRegistry.INSTANCE);
+ final ResponseMessage msg = serializer.deserializeResponse(base64);
+ final List<Object> data = (List<Object>) msg.getResult().getData();
+ assertEquals(6, data.size());
+ for (Object o : data) {
+ assertThat(o, instanceOf(Vertex.class));
+ }
+ }
+ }
+
+ @Test
+ public void should200OnGETWithGremlinQueryStringArgumentWithIteratorResultGraphBinaryToString() throws Exception {
+ final CloseableHttpClient httpclient = HttpClients.createDefault();
+ final HttpGet httpget = new HttpGet(TestClientFactory.createURLString("?gremlin=gclassic.V()"));
+ final String mime = SerTokens.MIME_GRAPHBINARY_V1D0 + "-stringd";
+ httpget.addHeader("Accept", mime);
+
+ try (final CloseableHttpResponse response = httpclient.execute(httpget)) {
+ assertEquals(200, response.getStatusLine().getStatusCode());
+ assertEquals(mime, response.getEntity().getContentType().getValue());
+ final String base64 = EntityUtils.toString(response.getEntity());
+ final GraphBinaryMessageSerializerV1 serializer = new GraphBinaryMessageSerializerV1(TypeSerializerRegistry.INSTANCE);
+ final ResponseMessage msg = serializer.deserializeResponse(base64);
+ final List<Object> data = (List<Object>) msg.getResult().getData();
+ assertEquals(6, data.size());
+ for (Object o : data) {
+ assertThat(o, instanceOf(String.class));
+ assertThat((String) o, matchesRegex("v\\[\\d\\]"));
+ }
+ }
+ }
+
+ @Test
+ public void should200OnGETWithGremlinQueryStringArgumentWithIteratorResultTextPlain() throws Exception {
+ final CloseableHttpClient httpclient = HttpClients.createDefault();
+ final HttpGet httpget = new HttpGet(TestClientFactory.createURLString("?gremlin=gclassic.V()"));
+ final String mime = "text/plain";
+ httpget.addHeader("Accept", mime);
+
+ try (final CloseableHttpResponse response = httpclient.execute(httpget)) {
+ assertEquals(200, response.getStatusLine().getStatusCode());
+ assertEquals(mime, response.getEntity().getContentType().getValue());
+ final String text = EntityUtils.toString(response.getEntity());
+ final String[] split = text.split(System.lineSeparator());
+ assertEquals(6, split.length);
+ for (String line : split) {
+ assertThat(line, matchesRegex("==>v\\[\\d\\]"));
+ }
+ }
+ }
+
+ @Test
public void should200OnGETWithGremlinQueryStringArgument() throws Exception {
final CloseableHttpClient httpclient = HttpClients.createDefault();
final HttpGet httpget = new HttpGet(TestClientFactory.createURLString("?gremlin=2-1"));
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/util/TextPlainMessageSerializerTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/util/TextPlainMessageSerializerTest.java
new file mode 100644
index 0000000..803d149
--- /dev/null
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/util/TextPlainMessageSerializerTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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;
+
+import io.netty.buffer.ByteBufAllocator;
+import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import static org.junit.Assert.assertEquals;
+
+public class TextPlainMessageSerializerTest {
+
+ @Test
+ public void shouldProducePlainText() throws Exception {
+ final Map<String, Object> m = new HashMap<>();
+ final ResponseMessage msg = ResponseMessage.build(UUID.randomUUID()).
+ result(Arrays.asList(1, new DetachedVertex(100, "person", m), java.awt.Color.RED)).create();
+
+ final TextPlainMessageSerializer messageSerializer = new TextPlainMessageSerializer();
+ final String output = messageSerializer.serializeResponseAsString(msg, ByteBufAllocator.DEFAULT);
+ final String exp = "==>1" + System.lineSeparator() +
+ "==>v[100]" + System.lineSeparator() +
+ "==>java.awt.Color[r=255,g=0,b=0]";
+ assertEquals(exp, output);
+ }
+}