package ch.ethz.syslab.telesto.server.controller;
import java.util.Arrays;
import java.util.List;
import ch.ethz.syslab.telesto.common.model.Client;
import ch.ethz.syslab.telesto.common.model.ClientMode;
import ch.ethz.syslab.telesto.common.model.Message;
import ch.ethz.syslab.telesto.common.model.Queue;
import ch.ethz.syslab.telesto.common.model.ReadMode;
import ch.ethz.syslab.telesto.common.protocol.CreateQueuePacket;
import ch.ethz.syslab.telesto.common.protocol.CreateQueueResponsePacket;
import ch.ethz.syslab.telesto.common.protocol.DeleteClientPacket;
import ch.ethz.syslab.telesto.common.protocol.DeleteQueuePacket;
import ch.ethz.syslab.telesto.common.protocol.ErrorPacket;
import ch.ethz.syslab.telesto.common.protocol.GetActiveQueuesPacket;
import ch.ethz.syslab.telesto.common.protocol.GetActiveQueuesResponsePacket;
import ch.ethz.syslab.telesto.common.protocol.GetMessagesPacket;
import ch.ethz.syslab.telesto.common.protocol.GetMessagesResponsePacket;
import ch.ethz.syslab.telesto.common.protocol.GetQueueIdPacket;
import ch.ethz.syslab.telesto.common.protocol.GetQueueIdResponsePacket;
import ch.ethz.syslab.telesto.common.protocol.GetQueueNamePacket;
import ch.ethz.syslab.telesto.common.protocol.GetQueueNameResponsePacket;
import ch.ethz.syslab.telesto.common.protocol.GetQueuesPacket;
import ch.ethz.syslab.telesto.common.protocol.GetQueuesResponsePacket;
import ch.ethz.syslab.telesto.common.protocol.Packet;
import ch.ethz.syslab.telesto.common.protocol.PingPacket;
import ch.ethz.syslab.telesto.common.protocol.PongPacket;
import ch.ethz.syslab.telesto.common.protocol.PutMessagePacket;
import ch.ethz.syslab.telesto.common.protocol.ReadMessagePacket;
import ch.ethz.syslab.telesto.common.protocol.ReadMessageResponsePacket;
import ch.ethz.syslab.telesto.common.protocol.ReadResponsePacket;
import ch.ethz.syslab.telesto.common.protocol.SuccessPacket;
import ch.ethz.syslab.telesto.common.protocol.handler.IServerProtocolHandler;
import ch.ethz.syslab.telesto.common.protocol.handler.PacketProcessingException;
import ch.ethz.syslab.telesto.common.protocol.handler.ProtocolHandler;
import ch.ethz.syslab.telesto.common.util.ErrorType;
import ch.ethz.syslab.telesto.common.util.NumberUtil;
import ch.ethz.syslab.telesto.server.db.Database;
import ch.ethz.syslab.telesto.server.db.procedure.ClientProcedure;
import ch.ethz.syslab.telesto.server.db.procedure.MessageProcedure;
import ch.ethz.syslab.telesto.server.db.procedure.QueueProcedure;
public class ServerProtocolHandler extends ProtocolHandler implements IServerProtocolHandler {
private Database db;
private Client client;
public ServerProtocolHandler(Database database, Client client) {
db = database;
this.client = client;
}
@Override
public Packet handle(PingPacket packet) throws PacketProcessingException {
return new PongPacket();
}
@Override
public Packet handle(DeleteClientPacket packet) throws PacketProcessingException {
int clientId = db.callSimpleProcedure(ClientProcedure.DELETE_CLIENT, client.id);
if (clientId != 0) {
return new SuccessPacket();
}
return new ErrorPacket(ErrorType.CLIENT_NOT_EXISTING, "Could not delete non-existent Client");
}
@Override
public Packet handle(CreateQueuePacket packet) throws PacketProcessingException {
try {
List<Queue> queues = db.callQueueProcedure(QueueProcedure.CREATE_QUEUE, packet.name);
if (queues.size() == 1) {
return new CreateQueueResponsePacket(queues.get(0).id);
}
} catch (PacketProcessingException e) {
if (e.type == ErrorType.UNIQUE_CONSTRAINT) {
// produce a more specific error message
throw new PacketProcessingException(ErrorType.QUEUE_NAME_NOT_UNIQUE, "There is already a queue with the given name");
}
throw e;
}
return new ErrorPacket(ErrorType.INTERNAL_ERROR, "Failed to create new Queue");
}
@Override
public Packet handle(DeleteQueuePacket packet) throws PacketProcessingException {
int queueId = db.callSimpleProcedure(QueueProcedure.DELETE_QUEUE, packet.queueId);
if (queueId != 0) {
return new SuccessPacket();
}
return new ErrorPacket(ErrorType.QUEUE_NOT_EXISTING, "Failed to delete non-existing Queue");
}
@Override
public Packet handle(GetQueueIdPacket packet) throws PacketProcessingException {
List<Queue> queues = db.callQueueProcedure(QueueProcedure.GET_QUEUE_ID, packet.name);
if (queues.size() == 1) {
return new GetQueueIdResponsePacket(queues.get(0).id);
}
return new ErrorPacket(ErrorType.QUEUE_NOT_EXISTING, "Failed to get id of non-existing Queue");
}
@Override
public Packet handle(GetQueueNamePacket packet) throws PacketProcessingException {
List<Queue> queues = db.callQueueProcedure(QueueProcedure.GET_QUEUE_NAME, packet.queueId);
if (queues.size() == 1) {
return new GetQueueNameResponsePacket(queues.get(0).name);
}
return new ErrorPacket(ErrorType.QUEUE_NOT_EXISTING, "Failed to get name of non-existing Queue");
}
@Override
public Packet handle(GetQueuesPacket packet) throws PacketProcessingException {
List<Queue> queues = db.callQueueProcedure(QueueProcedure.LIST_QUEUES);
if (!queues.isEmpty()) {
return new GetQueuesResponsePacket(queues.toArray(new Queue[queues.size()]));
}
return new ErrorPacket(ErrorType.NO_QUEUES_EXISTING, "No Queues are currently existing");
}
@Override
public Packet handle(GetActiveQueuesPacket packet) throws PacketProcessingException {
List<Queue> queues = db.callQueueProcedure(QueueProcedure.GET_ACTIVE_QUEUES, client.id);
if (!queues.isEmpty()) {
return new GetActiveQueuesResponsePacket(queues.toArray(new Queue[queues.size()]));
}
return new ErrorPacket(ErrorType.NO_ACTIVE_QUEUES_EXISTING, "No Queues with messages for Client are currently existing");
}
@Override
public Packet handle(GetMessagesPacket packet) throws PacketProcessingException {
List<Message> messages = db.callMessageProcedure(MessageProcedure.GET_MESSAGES_FROM_QUEUE);
if (messages.size() > 0) {
return new GetMessagesResponsePacket(messages.toArray(new Message[messages.size()]));
}
return new ErrorPacket(ErrorType.NO_MESSAGES_IN_QUEUE, "No Messages were found in the specified Queue");
}
@Override
public Packet handle(PutMessagePacket packet) throws PacketProcessingException {
requireClientMode(ClientMode.FULL); // not accessible for READ_ONLY clients
Message msg = packet.message;
Integer receiverId = NumberUtil.set0ToNull(msg.receiverId);
Integer context = NumberUtil.set0ToNull(msg.context);
if (packet.additionalQueueIds == null || packet.additionalQueueIds.length == 0) {
// insert into single queue
int queueId = db.callSimpleProcedure(MessageProcedure.PUT_MESSAGE, msg.queueId, client.id, receiverId, context, msg.priority,
msg.message);
if (queueId == msg.queueId) {
return new SuccessPacket();
}
return new ErrorPacket(ErrorType.QUEUE_NOT_EXISTING, "Message was not inserted because Queue does not exist");
} else {
// insert into multiple queues
int[] queueIds = new int[packet.additionalQueueIds.length + 1];
queueIds[0] = msg.queueId;
System.arraycopy(packet.additionalQueueIds, 0, queueIds, 1, packet.additionalQueueIds.length);
List<Integer> insertedQueueIds = db.callIntegerListProcedure(MessageProcedure.PUT_MESSAGES, queueIds, client.id, receiverId, context,
msg.priority, msg.message);
if (insertedQueueIds.size() == queueIds.length) {
return new SuccessPacket();
} else {
return new ErrorPacket(ErrorType.QUEUE_NOT_EXISTING, "Message was not inserted because some Queues do not exist. Tried: "
+ Arrays.toString(queueIds) + "; Inserted: " + insertedQueueIds.toString());
}
}
}
@Override
public Packet handle(ReadMessagePacket packet) throws PacketProcessingException {
List<Message> messages;
Integer queueId = NumberUtil.set0ToNull(packet.queueId);
Integer senderId = NumberUtil.set0ToNull(packet.senderId);
Integer receiverId = client.id;
switch (ReadMode.fromByteValue(packet.mode)) {
case TIME:
messages = db.callMessageProcedure(MessageProcedure.READ_MESSAGE_BY_TIMESTAMP, queueId, senderId, receiverId);
break;
case PRIORITY:
default:
messages = db.callMessageProcedure(MessageProcedure.READ_MESSAGE_BY_PRIORITY, queueId, senderId, receiverId);
break;
}
if (!messages.isEmpty()) {
return new ReadMessageResponsePacket(messages.get(0));
}
return new ErrorPacket(ErrorType.NO_MESSAGES_RETRIEVED, "No corresponding message to the query found");
}
@Override
public Packet handle(ReadResponsePacket packet) throws PacketProcessingException {
List<Message> messages;
int queueId = packet.queueId;
int receiverId = client.id;
int context = packet.context;
if (queueId == 0 || context == 0) {
throw new PacketProcessingException(ErrorType.REQUIRED_PARAMETER_MISSING, "QueueId and Context are required parameters to read responses");
}
messages = db.callMessageProcedure(MessageProcedure.READ_RESPONSE_MESSAGE, queueId, receiverId, context);
if (!messages.isEmpty()) {
return new ReadMessageResponsePacket(messages.get(0));
}
return new ErrorPacket(ErrorType.NO_MESSAGES_RETRIEVED, "No corresponding message to the query found");
}
protected void requireClientMode(ClientMode... modes) throws PacketProcessingException {
if (!Arrays.asList(modes).contains(client.mode)) {
throw new PacketProcessingException(ErrorType.CLIENT_MODE_PERMISSION_VIOLATION, "To use this method the client mode must be one of "
+ Arrays.toString(modes));
}
}
protected void requiredIntFields(int... values) throws PacketProcessingException {
for (int v : values) {
if (v == 0) {
throw new PacketProcessingException(ErrorType.REQUIRED_PARAMETER_MISSING, "A required parameter is missing");
}
}
}
}