/*******************************************************************************
* Copyright 2015 Klaus Pfeiffer <klaus@allpiper.com>
*
* 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 com.jfastnet.serialiser;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.jfastnet.config.SerialiserConfig;
import com.jfastnet.messages.Message;
import lombok.extern.slf4j.Slf4j;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.CRC32;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;
/** Blazing fast kryo serialiser. Strongly recommended!
* @author Klaus Pfeiffer - klaus@allpiper.com */
@Slf4j
public class KryoSerialiser implements ISerialiser{
private final SerialiserConfig config;
private Output output = new Output(128, 1048576);
private ThreadLocal<Kryo> kryos;
private Kryo kryo;
private CRC32 crc = new CRC32();
public KryoSerialiser(SerialiserConfig config, ThreadLocal<Kryo> kryos) {
this.config = config;
this.kryos = kryos;
}
public KryoSerialiser(SerialiserConfig config, Kryo kryo) {
this.config = config;
this.kryo = kryo;
}
@Override
public byte[] serialise(Message message) {
try {
output = new Output(128, 1048576);
// output.clear(); // FIXME see https://github.com/EsotericSoftware/kryo/issues/312
getKryo().writeClassAndObject(output, message);
output.flush();
return output.toBytes();
} catch (Exception e) {
log.error("Couldn't create output byte array.", e);
if (output != null) {
log.error("Output position: {}, Buffer length: {}", output.position(), output.getBuffer().length);
}
} finally {
output.close();
}
return null;
}
@Override
public Message deserialise(byte[] byteArray, int offset, int length) {
try (Input input = new Input(byteArray, offset, length)) {
Message message = (Message) getKryo().readClassAndObject(input);
if (message != null) {
message.payload = byteArray;
return message;
}
} catch (Exception e) {
log.error("Couldn't stream from byte array.", e);
}
return null;
}
@Override
public void serialiseWithStream(Message message, OutputStream _outputStream) {
if (config.useBasicCompression) {
// It usually requires more bytes if used with deflater. Depends on the size.
_outputStream = new DeflaterOutputStream(_outputStream);
}
try (OutputStream outputStream = _outputStream) {
Output output = new Output(outputStream);
getKryo().writeClassAndObject(output, message);
output.close();
} catch (IOException e) {
log.error("Couldn't stream to byte buffer.", e);
}
}
@Override
public Message deserialiseWithStream(InputStream _is) {
if (this.config.useBasicCompression) {
_is = new InflaterInputStream(_is);
}
try (InputStream is = _is) {
Input input = new Input(is);
Message message = (Message) getKryo().readClassAndObject(input);
if (message != null) {
//message.payload = content;
return message;
}
} catch (EOFException e) {
log.error("EOFException", e);
} catch (IOException e) {
log.error("Couldn't stream from byte buffer.", e);
}
return null;
}
@Override
public CRC32 getChecksum(Message message, byte[] salt) {
Object output = message.payload;
crc.reset();
if (output instanceof byte[]) {
byte[] bytes = (byte[]) output;
crc.update(bytes);
crc.update(salt);
} else {
if (output == null) {
log.error("Payload was null.");
} else {
log.error("Type '{}' not supported", output.getClass());
}
return null;
}
return crc;
}
private Kryo getKryo() {
return kryos != null ? kryos.get() : this.kryo;
}
}