package com.faforever.client.remote;
import com.faforever.client.remote.domain.SerializableMessage;
import com.faforever.client.remote.io.QDataWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.serializer.Serializer;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.invoke.MethodHandles;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;
/**
* Sends data to the server. Classes should not use the server writer directly, but e.g. {@link com.faforever.client.remote.FafService} or
* any other server accessor instead.
*/
public class ServerWriter implements Closeable {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final QDataWriter qDataWriter;
private final Map<Class<?>, Serializer<?>> objectWriters;
public ServerWriter(OutputStream outputStream) {
qDataWriter = new QDataWriter(new DataOutputStream(new BufferedOutputStream(outputStream)));
objectWriters = new HashMap<>();
}
public void registerMessageSerializer(Serializer<?> objectSerializer, Class<?> writableClass) {
objectWriters.put(writableClass, objectSerializer);
}
@SuppressWarnings("unchecked")
public void write(SerializableMessage object) {
Class<?> clazz = object.getClass();
Serializer<SerializableMessage> serializer = (Serializer<SerializableMessage>) findSerializerForClass(clazz);
if (serializer == null) {
throw new IllegalStateException("No object writer registered for type: " + clazz);
}
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
serializer.serialize(object, outputStream);
synchronized (qDataWriter) {
qDataWriter.appendWithSize(outputStream.toByteArray());
qDataWriter.flush();
}
} catch (EOFException | SocketException e) {
logger.debug("Server writer has been closed");
} catch (IOException e) {
logger.debug("Server writer has been closed", e);
}
}
/**
* Finds the appropriate serializer by walking up the type hierarchy. Interfaces are not checked.
*
* @return the appropriate serializer, or {@code null} if none was found
*/
private Serializer<?> findSerializerForClass(Class<?> clazz) {
Class<?> classToCheck = clazz;
while (!objectWriters.containsKey(classToCheck) && classToCheck != Object.class) {
classToCheck = classToCheck.getSuperclass();
}
return objectWriters.get(classToCheck);
}
@Override
public void close() throws IOException {
qDataWriter.close();
}
}