Java tutorial
/* * 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 com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.StdSerializer; import org.apache.tinkerpop.gremlin.driver.MessageSerializer; 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.GraphSONMapper; import groovy.json.JsonBuilder; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.util.ReferenceCountUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.UUID; /** * @author Stephen Mallette (http://stephen.genoprime.com) */ public abstract class AbstractJsonMessageSerializerV1d0 implements MessageSerializer { private static final Logger logger = LoggerFactory.getLogger(AbstractJsonMessageSerializerV1d0.class); protected ObjectMapper mapper; protected static final String TOKEN_USE_MAPPER_FROM_GRAPH = "useMapperFromGraph"; protected final TypeReference<Map<String, Object>> mapTypeReference = new TypeReference<Map<String, Object>>() { }; public AbstractJsonMessageSerializerV1d0() { final GraphSONMapper.Builder builder = configureBuilder(GraphSONMapper.build()); mapper = builder.create().createMapper(); } public AbstractJsonMessageSerializerV1d0(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; final Object graphToUseForMapper = config.get(TOKEN_USE_MAPPER_FROM_GRAPH); if (graphToUseForMapper != null) { if (null == graphs) throw new IllegalStateException(String.format( "No graphs have been provided to the serializer and therefore %s is not a valid configuration", TOKEN_USE_MAPPER_FROM_GRAPH)); final Graph g = graphs.get(graphToUseForMapper.toString()); if (null == g) throw new IllegalStateException( String.format("There is no graph named [%s] configured to be used in the %s setting", graphToUseForMapper, TOKEN_USE_MAPPER_FROM_GRAPH)); // a graph was found so use the mapper it constructs. this allows graphson to be auto-configured with any // custom classes that the implementation allows for initialBuilder = g.io().graphSONMapper(); } else { // no graph was supplied so just use the default - this will likely be the case when using a graph // with no custom classes or a situation where the user needs complete control like when using two // distinct implementations each with their own custom classes. initialBuilder = GraphSONMapper.build(); } mapper = configureBuilder(initialBuilder).create().createMapper(); } @Override public ByteBuf serializeResponseAsBinary(final ResponseMessage responseMessage, final ByteBufAllocator allocator) throws SerializationException { ByteBuf encodedMessage = null; try { final Map<String, Object> result = new HashMap<>(); result.put(SerTokens.TOKEN_DATA, responseMessage.getResult().getData()); result.put(SerTokens.TOKEN_META, responseMessage.getResult().getMeta()); final Map<String, Object> status = new HashMap<>(); status.put(SerTokens.TOKEN_MESSAGE, responseMessage.getStatus().getMessage()); status.put(SerTokens.TOKEN_CODE, responseMessage.getStatus().getCode().getValue()); status.put(SerTokens.TOKEN_ATTRIBUTES, responseMessage.getStatus().getAttributes()); final Map<String, Object> message = new HashMap<>(); message.put(SerTokens.TOKEN_STATUS, status); message.put(SerTokens.TOKEN_RESULT, result); message.put(SerTokens.TOKEN_REQUEST, responseMessage.getRequestId() != null ? responseMessage.getRequestId() : null); final byte[] payload = mapper.writeValueAsBytes(message); encodedMessage = allocator.buffer(payload.length); encodedMessage.writeBytes(payload); return encodedMessage; } catch (Exception ex) { if (encodedMessage != null) ReferenceCountUtil.release(encodedMessage); logger.warn("Response [{}] could not be serialized by {}.", responseMessage.toString(), AbstractJsonMessageSerializerV1d0.class.getName()); 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("Request [{}] could not be serialized by {}.", requestMessage.toString(), AbstractJsonMessageSerializerV1d0.class.getName()); 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("Request [{}] could not be deserialized by {}.", msg, AbstractJsonMessageSerializerV1d0.class.getName()); throw new SerializationException(ex); } } @Override public ResponseMessage deserializeResponse(final ByteBuf msg) throws SerializationException { try { final byte[] payload = new byte[msg.readableBytes()]; msg.readBytes(payload); final Map<String, Object> responseData = mapper.readValue(payload, mapTypeReference); final Map<String, Object> status = (Map<String, Object>) responseData.get(SerTokens.TOKEN_STATUS); final Map<String, Object> result = (Map<String, Object>) responseData.get(SerTokens.TOKEN_RESULT); return ResponseMessage.build(UUID.fromString(responseData.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(); } catch (Exception ex) { logger.warn("Response [{}] could not be deserialized by {}.", msg, AbstractJsonMessageSerializerV1d0.class.getName()); throw new SerializationException(ex); } } public static class GremlinServerModule extends SimpleModule { public GremlinServerModule() { super("graphson-gremlin-server"); addSerializer(JsonBuilder.class, new JsonBuilderJacksonSerializer()); } } public 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(","); } } }