/**
* Copyright (c) 2015 The original author or authors
*
* 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 org.reveno.atp.core.engine.components;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.reveno.atp.api.exceptions.BufferOutOfBoundsException;
import org.reveno.atp.core.api.TransactionCommitInfo;
import org.reveno.atp.core.api.TransactionCommitInfo.Builder;
import org.reveno.atp.core.api.channel.Buffer;
import org.reveno.atp.core.api.serialization.TransactionInfoSerializer;
import org.reveno.atp.core.serialization.DefaultJavaSerializer;
import org.reveno.atp.core.serialization.ProtostuffSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
public class SerializersChain implements TransactionInfoSerializer {
@Override
public int getSerializerType() {
return 0;
}
@Override
public boolean isRegistered(Class<?> type) {
return preferedSerializer.isRegistered(type);
}
@Override
public void registerTransactionType(Class<?> txDataType) {
transactionSerializers.forEach(s -> { try { s.registerTransactionType(txDataType);
} catch (Throwable t) { log.error("registerType", t); }});
}
@Override
public void serialize(TransactionCommitInfo info, Buffer buffer) {
tryTo(buffer, serializer.get().with(buffer, info));
}
@Override
public TransactionCommitInfo deserialize(Builder builder, Buffer buffer) {
return tryToD(buffer, s -> s.deserialize(builder, buffer));
}
@Override
public void serializeCommands(List<Object> commands, Buffer buffer) {
tryTo(buffer, serializer.get().with(buffer, commands));
}
@Override
public List<Object> deserializeCommands(Buffer buffer) {
return tryToD(buffer, s -> s.deserializeCommands(buffer));
}
@Override
public void serializeObject(Buffer buffer, Object tc) {
tryTo(buffer, s -> s.serializeObject(buffer, tc));
}
@Override
public Object deserializeObject(Buffer buffer) {
return tryToD(buffer, s -> s.deserializeObject(buffer));
}
@SuppressWarnings("serial")
public SerializersChain(ClassLoader classLoader) {
this(new ArrayList<TransactionInfoSerializer>() {{
add(new ProtostuffSerializer(classLoader));
add(new DefaultJavaSerializer());
}});
}
public SerializersChain(List<TransactionInfoSerializer> transactionsSerializers) {
this.transactionSerializers = transactionsSerializers;
this.transactionSerializers.forEach(s -> transactionSerializersMap.put(s.getSerializerType(), s));
this.preferedSerializer = transactionsSerializers.get(0);
}
protected <T> T tryToD(Buffer buffer, Function<TransactionInfoSerializer, T> f) {
if (!buffer.isAvailable()) {
throw new BufferOutOfBoundsException();
}
int serializerType = buffer.readInt();
if (serializerType == 0) {
throw new BufferOutOfBoundsException();
}
TransactionInfoSerializer s = transactionSerializersMap.get(serializerType);
if (s == null) {
throw new IllegalArgumentException(String.format("Can't find serializer for %d.", serializerType));
}
return f.apply(s);
}
protected void tryTo(Buffer buffer, Consumer<TransactionInfoSerializer> c) {
if (!accept(buffer, c, preferedSerializer)) {
for (int i = 1; i < transactionSerializers.size(); i++) {
if (accept(buffer, c, transactionSerializers.get(i)))
return;
}
throw new RuntimeException("All serializers failed to serialize.");
}
}
protected boolean accept(Buffer buffer, Consumer<TransactionInfoSerializer> c,
TransactionInfoSerializer s) {
try {
buffer.markWriter();
buffer.writeInt(s.getSerializerType());
c.accept(s);
return true;
} catch (Throwable tw) {
log.error("serialize", tw);
buffer.resetWriter();
return false;
}
}
protected List<TransactionInfoSerializer> transactionSerializers;
protected TransactionInfoSerializer preferedSerializer;
protected Int2ObjectMap<TransactionInfoSerializer> transactionSerializersMap = new Int2ObjectOpenHashMap<>();
protected static final ThreadLocal<Serializer> serializer = new ThreadLocal<Serializer>() {
@Override
protected Serializer initialValue() {
return new Serializer();
}
};
protected static final Logger log = LoggerFactory.getLogger(SerializersChain.class);
/**
* The payment for less garbage in real-time.
*/
protected static class Serializer implements Consumer<TransactionInfoSerializer> {
private Buffer buffer;
private TransactionCommitInfo info;
private List<Object> commands;
public Serializer with(Buffer buffer, TransactionCommitInfo info) {
this.buffer = buffer;
this.commands = null;
this.info = info;
return this;
}
public Serializer with(Buffer buffer, List<Object> commands) {
this.buffer = buffer;
this.info = null;
this.commands = commands;
return this;
}
@Override
public void accept(TransactionInfoSerializer transactionInfoSerializer) {
if (commands == null)
transactionInfoSerializer.serialize(info, buffer);
else
transactionInfoSerializer.serializeCommands(commands, buffer);
}
}
}