/*
* Copyright (C) 2015 SoftIndex LLC.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.datakernel.datagraph.server;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Predicate;
import com.google.common.base.Throwables;
import com.google.common.collect.Ordering;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator;
import com.google.gson.JsonSyntaxException;
import io.datakernel.codegen.DefiningClassLoader;
import io.datakernel.datagraph.graph.StreamId;
import io.datakernel.datagraph.node.*;
import io.datakernel.datagraph.server.command.DatagraphCommand;
import io.datakernel.datagraph.server.command.DatagraphCommandDownload;
import io.datakernel.datagraph.server.command.DatagraphCommandExecute;
import io.datakernel.serializer.BufferSerializer;
import io.datakernel.serializer.GsonSubclassesAdapter;
import io.datakernel.serializer.SerializerBuilder;
import io.datakernel.stream.processor.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Type;
import java.net.InetSocketAddress;
import java.util.*;
/**
* Responsible for setting up serialization operations for datagraph.
* Provides Gson object with specific adapters.
* Maintains the cache of BufferSerializer's.
*/
public final class DatagraphSerialization {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public final Gson gson;
private final Map<Class<?>, BufferSerializer<?>> serializers = new HashMap<>();
public DatagraphSerialization() {
this(Collections.<NodeSubclass>emptyList());
}
/**
* Constructs a new datagraph serialization object, initializes Gson object with used classes.
*/
public DatagraphSerialization(List<NodeSubclass> nodeSubclasses) {
Class<?> naturalOrderingClass;
Class<?> identityFunctionClass;
try {
naturalOrderingClass = Class.forName("com.google.common.collect.NaturalOrdering");
identityFunctionClass = Class.forName("com.google.common.base.Functions$IdentityFunction");
} catch (ClassNotFoundException e) {
throw Throwables.propagate(e);
}
GsonSubclassesAdapter<Object> gsonSubclassesAdapter = GsonSubclassesAdapter.create()
.withSubclassField("nodeType")
.withSubclass("Download", NodeDownload.class)
.withSubclass("Upload", NodeUpload.class)
.withSubclass("Map", NodeMap.class)
.withSubclass("Filter", NodeFilter.class)
.withSubclass("Sort", NodeSort.class)
.withSubclass("Shard", NodeShard.class)
.withSubclass("Merge", NodeMerge.class)
.withSubclass("Reduce", NodeReduce.class)
.withSubclass("ReduceSimple", NodeReduceSimple.class)
.withSubclass("Join", NodeJoin.class)
.withSubclass("Union", NodeUnion.class)
.withSubclass("ProducerOfIterable", NodeProducerOfIterable.class)
.withSubclass("ConsumerToList", NodeConsumerToList.class);
for (NodeSubclass nodeSubclass : nodeSubclasses) {
gsonSubclassesAdapter =
gsonSubclassesAdapter.withSubclass(nodeSubclass.getSubclassName(), nodeSubclass.getSubclass());
}
this.gson = new GsonBuilder()
.registerTypeAdapter(Class.class, new GsonClassTypeAdapter())
.registerTypeAdapter(StreamId.class, new GsonStreamIdAdapter())
.registerTypeAdapter(InetSocketAddress.class, new GsonInetSocketAddressAdapter())
.registerTypeAdapter(DatagraphCommand.class, GsonSubclassesAdapter.create()
.withSubclassField("commandType")
.withSubclass("Download", DatagraphCommandDownload.class)
.withSubclass("Execute", DatagraphCommandExecute.class))
.registerTypeAdapter(Node.class, gsonSubclassesAdapter)
.registerTypeAdapter(Predicate.class, GsonSubclassesAdapter.create()
.withSubclassField("predicateType"))
.registerTypeAdapter(Function.class, GsonSubclassesAdapter.create()
.withSubclassField("functionType")
.withClassTag(identityFunctionClass.getName(), identityFunctionClass, new InstanceCreator<Object>() {
@Override
public Object createInstance(Type type) {
return Functions.identity();
}
}))
.registerTypeAdapter(Comparator.class, GsonSubclassesAdapter.create()
.withSubclassField("comparatorType")
.withClassTag(naturalOrderingClass.getName(), naturalOrderingClass, new InstanceCreator<Object>() {
@Override
public Object createInstance(Type type) {
return Ordering.natural();
}
}))
.registerTypeAdapter(StreamMap.Mapper.class, GsonSubclassesAdapter.create()
.withSubclassField("mapperType"))
.registerTypeAdapter(StreamReducers.Reducer.class, GsonSubclassesAdapter.create()
.withSubclassField("reducerType")
.withClassTag("MergeDeduplicateReducer", StreamReducers.MergeDeduplicateReducer.class)
.withClassTag("MergeSortReducer", StreamReducers.MergeSortReducer.class)
.withSubclass("InputToAccumulator", StreamReducers.ReducerToResult.InputToAccumulator.class)
.withSubclass("InputToOutput", StreamReducers.ReducerToResult.InputToOutput.class)
.withSubclass("AccumulatorToAccumulator", StreamReducers.ReducerToResult.AccumulatorToAccumulator.class)
.withSubclass("AccumulatorToOutput", StreamReducers.ReducerToResult.AccumulatorToOutput.class))
.registerTypeAdapter(StreamReducers.ReducerToResult.class, GsonSubclassesAdapter.create()
.withSubclassField("reducerToResultType"))
.registerTypeAdapter(Sharder.class, GsonSubclassesAdapter.create()
.withSubclassField("sharderType")
.withSubclass("HashSharder", Sharders.HashSharder.class))
.registerTypeAdapter(StreamJoin.Joiner.class, GsonSubclassesAdapter.create()
.withSubclassField("joinerType"))
.setPrettyPrinting()
.enableComplexMapKeySerialization()
.create();
}
@SuppressWarnings("unchecked")
public synchronized <T> BufferSerializer<T> getSerializer(Class<T> type) {
BufferSerializer<T> serializer = (BufferSerializer<T>) serializers.get(type);
if (serializer == null) {
logger.info("Creating serializer for {}", type);
serializer = SerializerBuilder.create(
DefiningClassLoader.create(ClassLoader.getSystemClassLoader())).build(type);
serializers.put(type, serializer);
}
return serializer;
}
<T> boolean checkGson(T item, Class<T> type) {
String str = null;
try {
str = gson.toJson(item, type);
gson.fromJson(str, type);
return true;
} catch (JsonSyntaxException e) {
logger.error("Gson error:\n{}\n\n{}", e, str);
throw e;
}
}
}