| /* |
| * 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.driver.ser; |
| |
| import groovy.json.JsonBuilder; |
| import io.netty.buffer.ByteBuf; |
| import io.netty.buffer.ByteBufAllocator; |
| import io.netty.util.ReferenceCountUtil; |
| import org.apache.tinkerpop.gremlin.driver.message.RequestMessage; |
| import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage; |
| import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode; |
| import org.apache.tinkerpop.gremlin.structure.Graph; |
| import org.apache.tinkerpop.gremlin.structure.io.graphson.AbstractObjectDeserializer; |
| import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper; |
| import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONUtil; |
| import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONVersion; |
| import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONXModuleV2d0; |
| import org.apache.tinkerpop.shaded.jackson.core.JsonGenerationException; |
| import org.apache.tinkerpop.shaded.jackson.core.JsonGenerator; |
| import org.apache.tinkerpop.shaded.jackson.core.type.TypeReference; |
| import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper; |
| import org.apache.tinkerpop.shaded.jackson.databind.SerializerProvider; |
| import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeSerializer; |
| import org.apache.tinkerpop.shaded.jackson.databind.module.SimpleModule; |
| import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdSerializer; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.io.IOException; |
| import java.util.Map; |
| import java.util.UUID; |
| |
| /** |
| * @author Stephen Mallette (http://stephen.genoprime.com) |
| */ |
| public abstract class AbstractGraphSONMessageSerializerV2d0 extends AbstractMessageSerializer { |
| private static final Logger logger = LoggerFactory.getLogger(AbstractGraphSONMessageSerializerV2d0.class); |
| |
| protected ObjectMapper mapper; |
| |
| protected final TypeReference<Map<String, Object>> mapTypeReference = new TypeReference<Map<String, Object>>() { |
| }; |
| |
| public AbstractGraphSONMessageSerializerV2d0() { |
| final GraphSONMapper.Builder builder = configureBuilder(initBuilder(null)); |
| mapper = builder.create().createMapper(); |
| } |
| |
| public AbstractGraphSONMessageSerializerV2d0(final GraphSONMapper mapper) { |
| this.mapper = mapper.createMapper(); |
| } |
| |
| abstract byte[] obtainHeader(); |
| |
| abstract GraphSONMapper.Builder configureBuilder(final GraphSONMapper.Builder builder); |
| |
| @Override |
| public void configure(final Map<String, Object> config, final Map<String, Graph> graphs) { |
| final GraphSONMapper.Builder initialBuilder = initBuilder(null); |
| addIoRegistries(config, initialBuilder); |
| mapper = configureBuilder(initialBuilder).create().createMapper(); |
| } |
| |
| @Override |
| public ByteBuf serializeResponseAsBinary(final ResponseMessage responseMessage, final ByteBufAllocator allocator) throws SerializationException { |
| ByteBuf encodedMessage = null; |
| try { |
| final byte[] payload = mapper.writeValueAsBytes(responseMessage); |
| encodedMessage = allocator.buffer(payload.length); |
| encodedMessage.writeBytes(payload); |
| |
| return encodedMessage; |
| } catch (Exception ex) { |
| if (encodedMessage != null) ReferenceCountUtil.release(encodedMessage); |
| |
| logger.warn(String.format("Response [%s] could not be serialized by %s.", responseMessage, AbstractGraphSONMessageSerializerV2d0.class.getName()), ex); |
| throw new SerializationException(ex); |
| } |
| } |
| |
| @Override |
| public ByteBuf serializeRequestAsBinary(final RequestMessage requestMessage, final ByteBufAllocator allocator) throws SerializationException { |
| ByteBuf encodedMessage = null; |
| try { |
| final byte[] header = obtainHeader(); |
| final byte[] payload = mapper.writeValueAsBytes(requestMessage); |
| |
| encodedMessage = allocator.buffer(header.length + payload.length); |
| encodedMessage.writeBytes(header); |
| encodedMessage.writeBytes(payload); |
| |
| return encodedMessage; |
| } catch (Exception ex) { |
| if (encodedMessage != null) ReferenceCountUtil.release(encodedMessage); |
| |
| logger.warn(String.format("Request [%s] could not be serialized by %s.", requestMessage, AbstractGraphSONMessageSerializerV2d0.class.getName()), ex); |
| throw new SerializationException(ex); |
| } |
| } |
| |
| @Override |
| public RequestMessage deserializeRequest(final ByteBuf msg) throws SerializationException { |
| try { |
| final byte[] payload = new byte[msg.readableBytes()]; |
| msg.readBytes(payload); |
| return mapper.readValue(payload, RequestMessage.class); |
| } catch (Exception ex) { |
| logger.warn(String.format("Request [%s] could not be deserialized by %s.", msg, AbstractGraphSONMessageSerializerV2d0.class.getName()), ex); |
| throw new SerializationException(ex); |
| } |
| } |
| |
| @Override |
| public ResponseMessage deserializeResponse(final ByteBuf msg) throws SerializationException { |
| try { |
| final byte[] payload = new byte[msg.readableBytes()]; |
| msg.readBytes(payload); |
| return mapper.readValue(payload, ResponseMessage.class); |
| } catch (Exception ex) { |
| logger.warn(String.format("Response [%s] could not be deserialized by %s.", msg, AbstractGraphSONMessageSerializerV2d0.class.getName()), ex); |
| throw new SerializationException(ex); |
| } |
| } |
| |
| private GraphSONMapper.Builder initBuilder(final GraphSONMapper.Builder builder) { |
| final GraphSONMapper.Builder b = null == builder ? GraphSONMapper.build() : builder; |
| return b.addCustomModule(new AbstractGraphSONMessageSerializerV2d0.GremlinServerModule()) |
| .addCustomModule(GraphSONXModuleV2d0.build().create(false)) |
| .version(GraphSONVersion.V2_0); |
| } |
| |
| public final static class GremlinServerModule extends SimpleModule { |
| public GremlinServerModule() { |
| super("graphson-gremlin-server"); |
| |
| // SERIALIZERS |
| addSerializer(JsonBuilder.class, new JsonBuilderJacksonSerializer()); |
| addSerializer(ResponseMessage.class, new ResponseMessageSerializer()); |
| addSerializer(RequestMessage.class, new RequestMessageSerializer()); |
| |
| //DESERIALIZERS |
| addDeserializer(ResponseMessage.class, new ResponseMessageDeserializer()); |
| addDeserializer(RequestMessage.class, new RequestMessageDeserializer()); |
| } |
| } |
| |
| public final static class JsonBuilderJacksonSerializer extends StdSerializer<JsonBuilder> { |
| public JsonBuilderJacksonSerializer() { |
| super(JsonBuilder.class); |
| } |
| |
| @Override |
| public void serialize(final JsonBuilder json, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) |
| throws IOException, JsonGenerationException { |
| // the JSON from the builder will already be started/ended as array or object...just need to surround it |
| // with appropriate chars to fit into the serialization pattern. |
| jsonGenerator.writeRaw(":"); |
| jsonGenerator.writeRaw(json.toString()); |
| jsonGenerator.writeRaw(","); |
| } |
| } |
| |
| public final static class RequestMessageSerializer extends StdSerializer<RequestMessage> { |
| public RequestMessageSerializer() { |
| super(RequestMessage.class); |
| } |
| |
| @Override |
| public void serialize(final RequestMessage requestMessage, final JsonGenerator jsonGenerator, |
| final SerializerProvider serializerProvider) throws IOException { |
| ser(requestMessage, jsonGenerator, serializerProvider, null); |
| } |
| |
| @Override |
| public void serializeWithType(final RequestMessage requestMessage, final JsonGenerator jsonGenerator, |
| final SerializerProvider serializerProvider, |
| final TypeSerializer typeSerializer) throws IOException { |
| ser(requestMessage, jsonGenerator, serializerProvider, typeSerializer); |
| } |
| |
| public void ser(final RequestMessage requestMessage, final JsonGenerator jsonGenerator, |
| final SerializerProvider serializerProvider, |
| final TypeSerializer typeSerializer) throws IOException { |
| GraphSONUtil.writeStartObject(requestMessage, jsonGenerator, typeSerializer); |
| |
| jsonGenerator.writeStringField(SerTokens.TOKEN_REQUEST, requestMessage.getRequestId().toString()); |
| jsonGenerator.writeStringField(SerTokens.TOKEN_OP, requestMessage.getOp()); |
| jsonGenerator.writeStringField(SerTokens.TOKEN_PROCESSOR, requestMessage.getProcessor()); |
| jsonGenerator.writeObjectField(SerTokens.TOKEN_ARGS, requestMessage.getArgs()); |
| |
| GraphSONUtil.writeEndObject(requestMessage, jsonGenerator, typeSerializer); |
| } |
| } |
| |
| public final static class RequestMessageDeserializer extends AbstractObjectDeserializer<RequestMessage> { |
| protected RequestMessageDeserializer() { |
| super(RequestMessage.class); |
| } |
| |
| @Override |
| public RequestMessage createObject(final Map<String, Object> data) { |
| final Map<String, Object> args = (Map<String, Object>) data.get(SerTokens.TOKEN_ARGS); |
| RequestMessage.Builder builder = RequestMessage.build(data.get(SerTokens.TOKEN_OP).toString()) |
| .overrideRequestId(UUID.fromString(data.get(SerTokens.TOKEN_REQUEST).toString())); |
| |
| if (data.containsKey(SerTokens.TOKEN_PROCESSOR)) |
| builder = builder.processor(data.get(SerTokens.TOKEN_PROCESSOR).toString()); |
| |
| if (args != null) { |
| for (Map.Entry<String, Object> kv : args.entrySet()) { |
| builder = builder.addArg(kv.getKey(), kv.getValue()); |
| } |
| } |
| |
| return builder.create(); |
| } |
| } |
| |
| public final static class ResponseMessageSerializer extends StdSerializer<ResponseMessage> { |
| public ResponseMessageSerializer() { |
| super(ResponseMessage.class); |
| } |
| |
| @Override |
| public void serialize(final ResponseMessage responseMessage, final JsonGenerator jsonGenerator, |
| final SerializerProvider serializerProvider) throws IOException { |
| ser(responseMessage, jsonGenerator, serializerProvider, null); |
| } |
| |
| @Override |
| public void serializeWithType(final ResponseMessage responseMessage, final JsonGenerator jsonGenerator, |
| final SerializerProvider serializerProvider, |
| final TypeSerializer typeSerializer) throws IOException { |
| ser(responseMessage, jsonGenerator, serializerProvider, typeSerializer); |
| } |
| |
| public void ser(final ResponseMessage responseMessage, final JsonGenerator jsonGenerator, |
| final SerializerProvider serializerProvider, |
| final TypeSerializer typeSerializer) throws IOException { |
| GraphSONUtil.writeStartObject(responseMessage, jsonGenerator, typeSerializer); |
| |
| jsonGenerator.writeStringField(SerTokens.TOKEN_REQUEST, responseMessage.getRequestId() != null ? responseMessage.getRequestId().toString() : null); |
| jsonGenerator.writeFieldName(SerTokens.TOKEN_STATUS); |
| |
| GraphSONUtil.writeStartObject(responseMessage, jsonGenerator, typeSerializer); |
| jsonGenerator.writeStringField(SerTokens.TOKEN_MESSAGE, responseMessage.getStatus().getMessage()); |
| jsonGenerator.writeNumberField(SerTokens.TOKEN_CODE, responseMessage.getStatus().getCode().getValue()); |
| jsonGenerator.writeObjectField(SerTokens.TOKEN_ATTRIBUTES, responseMessage.getStatus().getAttributes()); |
| GraphSONUtil.writeEndObject(responseMessage, jsonGenerator, typeSerializer); |
| |
| jsonGenerator.writeFieldName(SerTokens.TOKEN_RESULT); |
| |
| GraphSONUtil.writeStartObject(responseMessage, jsonGenerator, typeSerializer); |
| |
| if (null == responseMessage.getResult().getData()){ |
| jsonGenerator.writeNullField(SerTokens.TOKEN_DATA); |
| } else { |
| jsonGenerator.writeFieldName(SerTokens.TOKEN_DATA); |
| final Object result = responseMessage.getResult().getData(); |
| serializerProvider.findTypedValueSerializer(result.getClass(), true, null).serialize(result, jsonGenerator, serializerProvider); |
| } |
| |
| jsonGenerator.writeObjectField(SerTokens.TOKEN_META, responseMessage.getResult().getMeta()); |
| GraphSONUtil.writeEndObject(responseMessage, jsonGenerator, typeSerializer); |
| |
| GraphSONUtil.writeEndObject(responseMessage, jsonGenerator, typeSerializer); |
| } |
| } |
| |
| public final static class ResponseMessageDeserializer extends AbstractObjectDeserializer<ResponseMessage> { |
| protected ResponseMessageDeserializer() { |
| super(ResponseMessage.class); |
| } |
| |
| @Override |
| public ResponseMessage createObject(final Map<String, Object> data) { |
| final Map<String, Object> status = (Map<String, Object>) data.get(SerTokens.TOKEN_STATUS); |
| final Map<String, Object> result = (Map<String, Object>) data.get(SerTokens.TOKEN_RESULT); |
| return ResponseMessage.build(UUID.fromString(data.get(SerTokens.TOKEN_REQUEST).toString())) |
| .code(ResponseStatusCode.getFromValue((Integer) status.get(SerTokens.TOKEN_CODE))) |
| .statusMessage(status.get(SerTokens.TOKEN_MESSAGE).toString()) |
| .statusAttributes((Map<String, Object>) status.get(SerTokens.TOKEN_ATTRIBUTES)) |
| .result(result.get(SerTokens.TOKEN_DATA)) |
| .responseMetaData((Map<String, Object>) result.get(SerTokens.TOKEN_META)) |
| .create(); |
| } |
| } |
| } |